diff --git a/kernel/branches/Kolibri-acpi/blkdev/cd_drv.inc b/kernel/branches/Kolibri-acpi/blkdev/cd_drv.inc index 48af6f88a..d7082f117 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/cd_drv.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/cd_drv.inc @@ -9,110 +9,110 @@ $Revision$ ;********************************************************** -; D (ATAPI) +; Непосредственная работа с устройством СD (ATAPI) ;********************************************************** -; -; , Mario79, +; Автор части исходного текста Кулаков Владимир Геннадьевич +; Адаптация, доработка и разработка Mario79, -; +; Максимальное количество повторений операции чтения MaxRetr equ 10 -; -; ( ) +; Предельное время ожидания готовности к приему команды +; (в тиках) BSYWaitTime equ 1000 ;2 NoTickWaitTime equ 0xfffff CDBlockSize equ 2048 ;******************************************** -;* * -;* * +;* ЧТЕНИЕ СЕКТОРА С ПОВТОРАМИ * +;* Многократное повторение чтения при сбоях * ;******************************************** ReadCDWRetr: ;----------------------------------------------------------- ; input : eax = block to read ; ebx = destination ;----------------------------------------------------------- - pushad + pushad mov eax, [CDSectorAddress] mov ebx, [CDDataBuf_pointer] - call cd_calculate_cache + call cd_calculate_cache xor edi, edi add esi, 8 - inc edi + inc edi .hdreadcache: ; cmp dword [esi+4],0 ; empty ; je .nohdcache cmp [esi], eax ; correct sector - je .yeshdcache + je .yeshdcache .nohdcache: add esi, 8 - inc edi - dec ecx - jnz .hdreadcache - call find_empty_slot_CD_cache ; ret in edi + inc edi + dec ecx + jnz .hdreadcache + call find_empty_slot_CD_cache ; ret in edi - push edi - push eax - call cd_calculate_cache_2 + push edi + push eax + call cd_calculate_cache_2 shl edi, 11 add edi, eax mov [CDDataBuf_pointer], edi - pop eax - pop edi + pop eax + pop edi - call ReadCDWRetr_1 + call ReadCDWRetr_1 cmp [DevErrorCode], 0 - jne .exit + jne .exit mov [CDDataBuf_pointer], ebx - call cd_calculate_cache_1 + call cd_calculate_cache_1 lea esi, [edi*8+esi] mov [esi], eax ; sector number ; mov dword [esi+4],1 ; hd read - mark as same as in hd .yeshdcache: mov esi, edi shl esi, 11;9 - push eax - call cd_calculate_cache_2 + push eax + call cd_calculate_cache_2 add esi, eax - pop eax + pop eax mov edi, ebx;[CDDataBuf_pointer] mov ecx, 512;/4 - cld - rep movsd ; move data + cld + rep movsd ; move data .exit: - popad - ret + popad + ret ReadCDWRetr_1: pushad -; , -; +; Цикл, пока команда не выполнена успешно или не +; исчерпано количество попыток mov ECX, MaxRetr @@NextRetr: -; +; Подать команду ;************************************************* -;* - * -;* , * -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - ; * -;* CDSectorAddress - . * -;* CDDataBuf. * +;* ПОЛНОЕ ЧТЕНИЕ СЕКТОРА КОМПАКТ-ДИСКА * +;* Считываются данные пользователя, информация * +;* субканала и контрольная информация * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале; * +;* CDSectorAddress - адрес считываемого сектора. * +;* Данные считывается в массив CDDataBuf. * ;************************************************* ;ReadCD: - push ecx + push ecx ; pusha -; +; Задать размер сектора ; mov [CDBlockSize],2048 ;2352 -; - call clear_packet_buffer -; -; -; Read CD +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать пакетную команду для считывания +; сектора данных +; Задать код команды Read CD mov [PacketCommand], byte 0x28;0xBE -; +; Задать адрес сектора mov AX, word [CDSectorAddress+2] xchg AL, AH mov word [PacketCommand+2], AX @@ -121,106 +121,106 @@ ReadCDWRetr_1: mov word [PacketCommand+4], AX ; mov eax,[CDSectorAddress] ; mov [PacketCommand+2],eax -; +; Задать количество считываемых секторов mov [PacketCommand+8], byte 1 -; +; Задать считывание данных в полном объеме ; mov [PacketCommand+9],byte 0xF8 -; - call SendPacketDatCommand - pop ecx +; Подать команду + call SendPacketDatCommand + pop ecx ; ret ; cmp [DevErrorCode],0 test eax, eax - jz @@End_4 + jz @@End_4 or ecx, ecx ;{SPraid.simba} (for cd load) - jz @@End_4 - dec ecx + jz @@End_4 + dec ecx cmp [timer_ticks_enable], 0 - jne @f + jne @f mov eax, NoTickWaitTime .wait: - dec eax + dec eax ; test eax,eax - jz @@NextRetr - jmp .wait + jz @@NextRetr + jmp .wait @@: -; 2,5 +; Задержка на 2,5 секунды ; mov EAX,[timer_ticks] ; add EAX,50 ;250 ;@@Wait: ; call change_task ; cmp EAX,[timer_ticks] ; ja @@Wait - loop @@NextRetr + loop @@NextRetr @@End_4: mov dword [DevErrorCode], eax popad 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 +; mov byte [DevErrorCode],al +; Задать режим CHS mov byte [ATAAddressMode], al -; ATA- +; Послать ATA-команду передачи пакетной команды mov byte [ATAFeatures], al mov byte [ATASectorCount], al mov byte [ATASectorNumber], al - ; + ; Загрузить размер передаваемого блока mov [ATAHead], al ; mov AX,[CDBlockSize] mov [ATACylinder], CDBlockSize mov [ATACommand], 0A0h call SendCommandToHDD_1 test eax, eax -; cmp [DevErrorCode],0 ; - jnz @@End_8 ;, +; cmp [DevErrorCode],0 ;проверить код ошибки + jnz @@End_8 ;закончить, сохранив код ошибки -; -; +; Ожидание готовности дисковода к приему +; пакетной команды mov DX, [ATABasePortAddr] - add DX, 7 ; 17h + add DX, 7 ;порт 1х7h mov ecx, NoTickWaitTime @@WaitDevice0: cmp [timer_ticks_enable], 0 @@ -231,21 +231,21 @@ SendPacketDatCommand: jmp .test @@: call change_task - ; + ; Проверить время выполнения команды mov EAX, [timer_ticks] sub EAX, [TickCounter_1] cmp EAX, BSYWaitTime - ja @@Err1_1 ; - - ; + ja @@Err1_1 ;ошибка тайм-аута + ; Проверить готовность .test: in AL, DX - test AL, 80h ; BSY + test AL, 80h ;состояние сигнала BSY jnz @@WaitDevice0 - test AL, 1 ; ERR + test AL, 1 ;состояние сигнала ERR jnz @@Err6 - test AL, 08h ; DRQ + test AL, 08h ;состояние сигнала DRQ jz @@WaitDevice0 -; +; Послать пакетную команду cli mov DX, [ATABasePortAddr] mov AX, [PacketCommand] @@ -261,9 +261,9 @@ SendPacketDatCommand: mov AX, [PacketCommand+10] out DX, AX sti -; +; Ожидание готовности данных mov DX, [ATABasePortAddr] - add DX, 7 ; 17h + add DX, 7 ;порт 1х7h mov ecx, NoTickWaitTime @@WaitDevice1: cmp [timer_ticks_enable], 0 @@ -274,54 +274,54 @@ SendPacketDatCommand: jmp .test_1 @@: call change_task - ; + ; Проверить время выполнения команды mov EAX, [timer_ticks] sub EAX, [TickCounter_1] cmp EAX, MaxCDWaitTime - ja @@Err1_1 ; - - ; + ja @@Err1_1 ;ошибка тайм-аута + ; Проверить готовность .test_1: in AL, DX - test AL, 80h ; BSY + test AL, 80h ;состояние сигнала BSY jnz @@WaitDevice1 - test AL, 1 ; ERR + test AL, 1 ;состояние сигнала ERR jnz @@Err6_temp - test AL, 08h ; DRQ + test AL, 08h ;состояние сигнала DRQ jz @@WaitDevice1 -; +; Принять блок данных от контроллера mov EDI, [CDDataBuf_pointer];0x7000 ;CDDataBuf - ; - mov DX, [ATABasePortAddr]; 1x0h - ; + ; Загрузить адрес регистра данных контроллера + mov DX, [ATABasePortAddr];порт 1x0h + ; Загрузить в счетчик размер блока в байтах xor ecx, ecx mov CX, CDBlockSize - ; 16- - shr CX, 1; 2 - ; + ; Вычислить размер блока в 16-разрядных словах + shr CX, 1;разделить размер блока на 2 + ; Принять блок данных cli cld - rep insw + rep insw sti -; +; Успешное завершение приема данных @@End_8: xor eax, eax ret -; +; Записать код ошибки @@Err1_1: xor eax, eax - inc eax - ret + inc eax + ret ; mov [DevErrorCode],1 -; ret +; ret @@Err6_temp: mov eax, 7 - ret + ret ; mov [DevErrorCode],7 -; ret +; ret @@Err6: mov eax, 6 - ret + ret ; mov [DevErrorCode],6 ;@@End_8: ; ret @@ -329,21 +329,21 @@ SendPacketDatCommand: ;*********************************************** -;* ATAPI , * -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - ; * -;* PacketCommand - 12- . * +;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, * +;* НЕ ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧИ ДАННЫХ * +;* Входные параметры передаются через * +;* глобальные перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале; * +;* PacketCommand - 12-байтный командный пакет. * ;*********************************************** SendPacketNoDatCommand: pushad xor eax, eax ; mov byte [DevErrorCode],al -; CHS +; Задать режим CHS mov byte [ATAAddressMode], al -; ATA- +; Послать ATA-команду передачи пакетной команды mov byte [ATAFeatures], al mov byte [ATASectorCount], al mov byte [ATASectorNumber], al @@ -351,29 +351,29 @@ SendPacketNoDatCommand: mov byte [ATAHead], al mov [ATACommand], 0A0h call SendCommandToHDD_1 -; cmp [DevErrorCode],0 ; +; cmp [DevErrorCode],0 ;проверить код ошибки test eax, eax - jnz @@End_9 ;, -; -; + jnz @@End_9 ;закончить, сохранив код ошибки +; Ожидание готовности дисковода к приему +; пакетной команды mov DX, [ATABasePortAddr] - add DX, 7 ; 17h + 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] @@ -390,34 +390,34 @@ SendPacketNoDatCommand: out DX, AX ; sti cmp [ignore_CD_eject_wait], 1 - je @@clear_DEC -; + je @@clear_DEC +; Ожидание подтверждения приема команды mov DX, [ATABasePortAddr] - add DX, 7 ; 17h + 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 - jmp @@End_9 + inc eax + jmp @@End_9 @@Err6_1: mov eax, 6 @@End_9: @@ -426,103 +426,103 @@ SendPacketNoDatCommand: ret ;**************************************************** -;* * -;* * -;* : * -;* ChannelNumber - (1 2); * -;* DiskNumber - (0 1); * -;* ATAFeatures - ""; * -;* ATASectorCount - ; * -;* ATASectorNumber - ; * -;* ATACylinder - ; * -;* ATAHead - ; * -;* ATAAddressMode - (0-CHS, 1-LBA); * -;* ATACommand - . * -;* : * -;* ATABasePortAddr - HDD; * -;* DevErrorCode - . * -;* DevErrorCode * -;* eax * +;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска (0 или 1); * +;* ATAFeatures - "особенности"; * +;* ATASectorCount - количество секторов; * +;* ATASectorNumber - номер начального сектора; * +;* ATACylinder - номер начального цилиндра; * +;* ATAHead - номер начальной головки; * +;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); * +;* ATACommand - код команды. * +;* После успешного выполнения функции: * +;* в ATABasePortAddr - базовый адрес HDD; * +;* в DevErrorCode - ноль. * +;* При возникновении ошибки в DevErrorCode будет * +;* возвращен код ошибки в eax * ;**************************************************** SendCommandToHDD_1: ; pushad -; mov [DevErrorCode],0 not need -; +; 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 mov ecx, NoTickWaitTime @@WaitHDReady_2: cmp [timer_ticks_enable], 0 - jne @f - dec ecx + jne @f + dec ecx ; test ecx,ecx - jz @@Err1_4 - jmp .test + jz @@Err1_4 + 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,62 +530,62 @@ SendCommandToHDD_1: shl AH, 6 or AL, AH out DX, AL -; +; Послать команду mov AL, [ATACommand] - inc DX ; + inc DX ;регистр команд out DX, AL sti -; +; Сбросить признак ошибки ; mov [DevErrorCode],0 @@End_10: xor eax, eax - ret -; + ret +; Записать код ошибки @@Err1_4: xor eax, eax - inc eax + inc eax ; mov [DevErrorCode],1 - ret + ret @@Err2_4: mov eax, 2 ; mov [DevErrorCode],2 - ret + ret @@Err3_4: mov eax, 3 ; mov [DevErrorCode],3 - ret + ret @@Err4_4: mov eax, 4 ; mov [DevErrorCode],4 - ret + ret @@Err5_4: mov eax, 5 ; mov [DevErrorCode],5 -; - ret +; Завершение работы программы + ret ; sti ; popad ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * +;* ОЖИДАНИЕ ГОТОВНОСТИ УСТРОЙСТВА К РАБОТЕ * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * ;************************************************* WaitUnitReady: pusha -; +; Запомнить время начала операции mov EAX, [timer_ticks] mov [WURStartTime], EAX -; - call clear_packet_buffer -; TEST UNIT READY +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду TEST UNIT READY mov [PacketCommand], word 00h -; +; ЦИКЛ ОЖИДАНИЯ ГОТОВНОСТИ УСТРОЙСТВА mov ecx, NoTickWaitTime @@SendCommand: - ; + ; Подать команду проверки готовности call SendPacketNoDatCommand cmp [timer_ticks_enable], 0 jne @f @@ -597,148 +597,162 @@ WaitUnitReady: jmp @@SendCommand @@: call change_task - ; + ; Проверить код ошибки cmp [DevErrorCode], 0 je @@End_11 - ; + ; Проверить время ожидания готовности mov EAX, [timer_ticks] sub EAX, [WURStartTime] cmp EAX, MaxCDWaitTime jb @@SendCommand .Error: - ; - + ; Ошибка тайм-аута mov [DevErrorCode], 1 @@End_11: popa ret ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * +;* ЗАПРЕТИТЬ СМЕНУ ДИСКА * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * ;************************************************* prevent_medium_removal: pusha -; - call clear_packet_buffer -; +; Очистить буфер пакетной команды + call clear_packet_buffer +; Задать код команды mov [PacketCommand], byte 0x1E -; +; Задать код запрета mov [PacketCommand+4], byte 11b -; - call SendPacketNoDatCommand +; Подать команду + call SendPacketNoDatCommand mov eax, ATAPI_IDE0_lock add eax, [cdpos] - dec eax + dec eax mov [eax], byte 1 popa ret ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * +;* РАЗРЕШИТЬ СМЕНУ ДИСКА * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * ;************************************************* allow_medium_removal: pusha -; - call clear_packet_buffer -; +; Очистить буфер пакетной команды + call clear_packet_buffer +; Задать код команды mov [PacketCommand], byte 0x1E -; +; Задать код запрета mov [PacketCommand+4], byte 00b -; - call SendPacketNoDatCommand +; Подать команду + call SendPacketNoDatCommand mov eax, ATAPI_IDE0_lock add eax, [cdpos] - dec eax + dec eax mov [eax], byte 0 popa ret ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * +;* ЗАГРУЗИТЬ НОСИТЕЛЬ В ДИСКОВОД * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * ;************************************************* LoadMedium: pusha -; - call clear_packet_buffer -; START/STOP UNIT - ; +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду 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 - ; +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду 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 mov eax, [timer_ticks] sub eax, [timer_ATAPI_check] cmp eax, 100 - jb .end_1 + jb .end_1 mov al, [DRIVE_DATA+1] and al, 11b cmp al, 10b - jz .ide3 + jz .ide3 .ide2_1: mov al, [DRIVE_DATA+1] and al, 1100b cmp al, 1000b - jz .ide2 + jz .ide2 .ide1_1: mov al, [DRIVE_DATA+1] and al, 110000b cmp al, 100000b - jz .ide1 + jz .ide1 .ide0_1: mov al, [DRIVE_DATA+1] and al, 11000000b cmp al, 10000000b - jz .ide0 + jz .ide0 .end: - sti + sti mov eax, [timer_ticks] mov [timer_ATAPI_check], eax .end_1: @@ -746,102 +760,102 @@ check_ATAPI_device_event: ret .ide3: - cli + cli cmp [ATAPI_IDE3_lock], 1 - jne .ide2_1 + jne .ide2_1 cmp [IDE_Channel_2], 0 - jne .ide1_1 + jne .ide1_1 cmp [cd_status], 0 - jne .end + jne .end mov [IDE_Channel_2], 1 - call reserve_ok2 + call reserve_ok2 mov [ChannelNumber], 2 mov [DiskNumber], 1 mov [cdpos], 4 - call GetEvent_StatusNotification + call GetEvent_StatusNotification cmp [CDDataBuf+4], byte 1 - je .eject_ide3 - call syscall_cdaudio.free - jmp .ide2_1 + je .eject_ide3 + call syscall_cdaudio.free + jmp .ide2_1 .eject_ide3: - call .eject - call syscall_cdaudio.free - jmp .ide2_1 + call .eject + call syscall_cdaudio.free + jmp .ide2_1 .ide2: - cli + cli cmp [ATAPI_IDE2_lock], 1 - jne .ide1_1 + jne .ide1_1 cmp [IDE_Channel_2], 0 - jne .ide1_1 + jne .ide1_1 cmp [cd_status], 0 - jne .end + jne .end mov [IDE_Channel_2], 1 - call reserve_ok2 + call reserve_ok2 mov [ChannelNumber], 2 mov [DiskNumber], 0 mov [cdpos], 3 - call GetEvent_StatusNotification + call GetEvent_StatusNotification cmp [CDDataBuf+4], byte 1 - je .eject_ide2 - call syscall_cdaudio.free - jmp .ide1_1 + je .eject_ide2 + call syscall_cdaudio.free + jmp .ide1_1 .eject_ide2: - call .eject - call syscall_cdaudio.free - jmp .ide1_1 + call .eject + call syscall_cdaudio.free + jmp .ide1_1 .ide1: - cli + cli cmp [ATAPI_IDE1_lock], 1 - jne .ide0_1 + jne .ide0_1 cmp [IDE_Channel_1], 0 - jne .end + jne .end cmp [cd_status], 0 - jne .end + jne .end mov [IDE_Channel_1], 1 - call reserve_ok2 + call reserve_ok2 mov [ChannelNumber], 1 mov [DiskNumber], 1 mov [cdpos], 2 - call GetEvent_StatusNotification + call GetEvent_StatusNotification cmp [CDDataBuf+4], byte 1 - je .eject_ide1 - call syscall_cdaudio.free - jmp .ide0_1 + je .eject_ide1 + call syscall_cdaudio.free + jmp .ide0_1 .eject_ide1: - call .eject - call syscall_cdaudio.free - jmp .ide0_1 + call .eject + call syscall_cdaudio.free + jmp .ide0_1 .ide0: - cli + cli cmp [ATAPI_IDE0_lock], 1 - jne .end + jne .end cmp [IDE_Channel_1], 0 - jne .end + jne .end cmp [cd_status], 0 - jne .end + jne .end mov [IDE_Channel_1], 1 - call reserve_ok2 + call reserve_ok2 mov [ChannelNumber], 1 mov [DiskNumber], 0 mov [cdpos], 1 - call GetEvent_StatusNotification + call GetEvent_StatusNotification cmp [CDDataBuf+4], byte 1 - je .eject_ide0 - call syscall_cdaudio.free - jmp .end + je .eject_ide0 + call syscall_cdaudio.free + jmp .end .eject_ide0: - call .eject - call syscall_cdaudio.free - jmp .end + call .eject + call syscall_cdaudio.free + jmp .end .eject: - call clear_CD_cache - call allow_medium_removal + call clear_CD_cache + call allow_medium_removal mov [ignore_CD_eject_wait], 1 - call EjectMedium + call EjectMedium mov [ignore_CD_eject_wait], 0 ret iglobal @@ -853,78 +867,78 @@ ATAPI_IDE3_lock db 0 ignore_CD_eject_wait db 0 endg ;************************************************* -;* * -;* * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * +;* Получить сообщение о событии или состоянии * +;* устройства * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * ;************************************************* GetEvent_StatusNotification: pusha mov [CDDataBuf_pointer], CDDataBuf -; - call clear_packet_buffer -; +; Очистить буфер пакетной команды + 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 -; -; +; Очистить буфер пакетной команды + 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 +; Подать команду + 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 diff --git a/kernel/branches/Kolibri-acpi/blkdev/cdrom.inc b/kernel/branches/Kolibri-acpi/blkdev/cdrom.inc index 5130d2aa4..466c17d9c 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/cdrom.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/cdrom.inc @@ -11,9 +11,9 @@ $Revision$ sys_cd_audio: cmp word [cdbase], word 0 - jnz @f + jnz @f mov eax, 1 - ret + ret @@: ; eax=1 cdplay at ebx 0x00FFSSMM @@ -21,48 +21,48 @@ sys_cd_audio: ; eax=3 stop/pause playing cmp eax, 1 - jnz nocdp - call sys_cdplay - ret + jnz nocdp + call sys_cdplay + ret nocdp: cmp eax, 2 - jnz nocdtl + jnz nocdtl mov edi, [TASK_BASE] add edi, TASKDATA.mem_start add ebx, [edi] - call sys_cdtracklist - ret + call sys_cdtracklist + ret nocdtl: cmp eax, 3 - jnz nocdpause - call sys_cdpause - ret + jnz nocdpause + call sys_cdpause + ret nocdpause: mov eax, 0xffffff01 - ret + ret sys_cd_atapi_command: - pushad + pushad mov dx, word [cdbase] add dx, 6 mov ax, word [cdid] out dx, al mov esi, 10 - call delay_ms + call delay_ms mov dx, word [cdbase] add dx, 7 in al, dx and al, 0x80 cmp al, 0 - jnz res - jmp cdl6 + jnz res + jmp cdl6 res: mov dx, word [cdbase] add dx, 7 @@ -73,27 +73,27 @@ sys_cd_atapi_command: mov al, 0xe out dx, al mov esi, 1 - call delay_ms + call delay_ms mov dx, word [cdbase] add dx, 0x206 mov al, 0x8 out dx, al mov esi, 30 - call delay_ms + call delay_ms xor cx, cx cdl5: - inc cx + inc cx cmp cx, 10 - jz cdl6 + jz cdl6 mov dx, word [cdbase] add dx, 7 in al, dx and al, 0x88 cmp al, 0x00 - jz cdl5 + jz cdl5 mov esi, 100 - call delay_ms - jmp cdl5 + call delay_ms + jmp cdl5 cdl6: mov dx, word [cdbase] add dx, 4 @@ -108,7 +108,7 @@ sys_cd_atapi_command: mov al, 0xec out dx, al mov esi, 5 - call delay_ms + call delay_ms mov dx, word [cdbase] add dx, 1 mov al, 0 @@ -132,30 +132,30 @@ sys_cd_atapi_command: mov dx, word [cdbase] add dx, 7 cdl1: - inc cx + inc cx cmp cx, 100 - jz cdl2 + jz cdl2 in al, dx and ax, 0x88 cmp al, 0x8 - jz cdl2 + jz cdl2 mov esi, 2 - call delay_ms - jmp cdl1 + call delay_ms + jmp cdl1 cdl2: - popad - ret + popad + ret sys_cdplay: mov ax, 5 - push ax - push ebx + push ax + push ebx cdplay: - call sys_cd_atapi_command - cli + call sys_cd_atapi_command + cli mov dx, word [cdbase] mov ax, 0x0047 out dx, ax @@ -171,31 +171,31 @@ sys_cdplay: mov ax, 0x0000 out dx, ax mov esi, 10 - call delay_ms - sti + call delay_ms + sti add dx, 7 in al, dx test al, 1 - jz cdplayok + jz cdplayok mov ax, [esp+4] - dec ax + dec ax mov [esp+4], ax cmp ax, 0 - jz cdplayfail - jmp cdplay + jz cdplayfail + jmp cdplay cdplayfail: cdplayok: - pop ebx - pop ax - xor eax, eax - ret + pop ebx + pop ax + xor eax, eax + ret sys_cdtracklist: - push ebx + push ebx tcdplay: - call sys_cd_atapi_command + call sys_cd_atapi_command mov dx, word [cdbase] mov ax, 0x43+2*256 out dx, ax @@ -213,15 +213,15 @@ sys_cdtracklist: mov cx, 1000 mov dx, word [cdbase] add dx, 7 - cld + cld cdtrnwewait: mov esi, 10 - call delay_ms + call delay_ms in al, dx and al, 128 cmp al, 0 - jz cdtrl1 - loop cdtrnwewait + jz cdtrl1 + loop cdtrnwewait cdtrl1: ; read the result mov ecx, [esp+0] @@ -231,21 +231,21 @@ sys_cdtracklist: in al, dx and al, 8 cmp al, 8 - jnz cdtrdone + jnz cdtrdone sub dx, 7 in ax, dx mov [ecx], ax add ecx, 2 - jmp cdtrread + jmp cdtrread cdtrdone: - pop ecx - xor eax, eax - ret + pop ecx + xor eax, eax + ret sys_cdpause: - call sys_cd_atapi_command + call sys_cd_atapi_command mov dx, word [cdbase] mov ax, 0x004B @@ -262,10 +262,10 @@ sys_cdpause: out dx, ax mov esi, 10 - call delay_ms + call delay_ms add dx, 7 in al, dx - xor eax, eax - ret + xor eax, eax + ret diff --git a/kernel/branches/Kolibri-acpi/blkdev/disk.inc b/kernel/branches/Kolibri-acpi/blkdev/disk.inc index f9bf46d27..ce39048b3 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/disk.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/disk.inc @@ -5,16 +5,16 @@ ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -$Revision: 2257 $ +$Revision: 3460 $ ; ============================================================================= ; ================================= Constants ================================= ; ============================================================================= ; Error codes for callback functions. -DISK_STATUS_OK = 0 ; success +DISK_STATUS_OK = 0 ; success DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters -DISK_STATUS_NO_MEDIA = 2 ; no media present +DISK_STATUS_NO_MEDIA = 2 ; no media present DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data ; Driver flags. Represent bits in DISK.DriverFlags. DISK_NO_INSERT_NOTIFICATION = 1 @@ -33,7 +33,7 @@ MAX_NUM_PARTITIONS = 256 ; This structure defines all callback functions for working with the physical ; device. They are implemented by a driver. Objects with this structure reside ; in a driver. -struct DISKFUNC +struct DISKFUNC strucsize dd ? ; Size of the structure. This field is intended for possible extensions of ; this structure. If a new function is added to this structure and a driver @@ -88,7 +88,7 @@ ends ; This structure holds information on a medium. ; Objects with this structure are allocated by the kernel as a part of the DISK ; structure and are filled by a driver in the 'querymedia' callback. -struct DISKMEDIAINFO +struct DISKMEDIAINFO Flags dd ? ; Combination of DISK_MEDIA_* bits. SectorSize dd ? @@ -100,7 +100,7 @@ ends ; This structure represents the disk cache. To follow the old implementation, ; there are two distinct caches for a disk, one for "system" data,and the other ; for "application" data. -struct DISKCACHE +struct DISKCACHE mutex MUTEX ; Lock to protect the cache. ; The following fields are inherited from data32.inc:cache_ideX. @@ -114,7 +114,7 @@ ends ; This structure represents a disk device and its media for the kernel. ; This structure is allocated by the kernel in the 'disk_add' function, ; freed in the 'disk_dereference' function. -struct DISK +struct DISK ; Fields of disk object Next dd ? Prev dd ? @@ -177,7 +177,7 @@ ends ; This structure represents one partition for the kernel. This is a base ; template, the actual contents after common fields is determined by the ; file system code for this partition. -struct PARTITION +struct PARTITION FirstSector dq ? ; First sector of the partition. Length dq ? @@ -193,7 +193,7 @@ struct PARTITION ends ; This is an external structure, it represents an entry in the partition table. -struct PARTITION_TABLE_ENTRY +struct PARTITION_TABLE_ENTRY Bootable db ? ; 80h = bootable partition, 0 = non-bootable partition, other values = invalid FirstHead db ? @@ -221,8 +221,8 @@ iglobal ; The pseudo-item for the list of all DISK structures. ; Initialized to the empty list. disk_list: - dd disk_list - dd disk_list + dd disk_list + dd disk_list endg uglobal ; This mutex guards all operations with the global list of DISK structures. @@ -284,14 +284,14 @@ iglobal ; decrements the value when the job is done. Otherwise, it immediately ; decrements the value and uses buffers from the heap, allocated in the ; beginning and freed in the end. -partition_buffer_users dd -1 +partition_buffer_users dd -1 endg uglobal ; The static buffers for MBR, bootsector and fs-temporary sector data. align 16 -mbr_buffer rb 512 -bootsect_buffer rb 512 -fs_tmp_buffer rb 512 +mbr_buffer rb 512 +bootsect_buffer rb 512 +fs_tmp_buffer rb 512 endg iglobal @@ -300,13 +300,13 @@ iglobal ; have the default implementations. align 4 disk_default_callbacks: - dd disk_default_close - dd disk_default_closemedia - dd disk_default_querymedia - dd disk_default_read - dd disk_default_write - dd disk_default_flush - dd disk_default_adjust_cache_size + dd disk_default_close + dd disk_default_closemedia + dd disk_default_querymedia + dd disk_default_read + dd disk_default_write + dd disk_default_flush + dd disk_default_adjust_cache_size endg ; ============================================================================= @@ -332,82 +332,82 @@ endg ; in the operations with other Disk* functions. ; The handle is the pointer to the internal structure DISK. disk_add: - push ebx esi ; save used registers to be stdcall + push ebx esi ; save used registers to be stdcall ; 1. Allocate the DISK structure. ; 1a. Call the heap manager. - push sizeof.DISK - pop eax - call malloc + push sizeof.DISK + pop eax + call malloc ; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0. - test eax, eax - jz .nothing + test eax, eax + jz .nothing ; 2. Copy the disk name to the DISK structure. ; 2a. Get length of the name, including the terminating zero. - mov ebx, [esp+8+8] ; ebx = pointer to name - push eax ; save allocated pointer to DISK - xor eax, eax ; the argument of malloc() is in eax + mov ebx, [esp+8+8] ; ebx = pointer to name + push eax ; save allocated pointer to DISK + xor eax, eax ; the argument of malloc() is in eax @@: - inc eax - cmp byte [ebx+eax-1], 0 - jnz @b + inc eax + cmp byte [ebx+eax-1], 0 + jnz @b ; 2b. Call the heap manager. Note that it can change ebx. push ebx - call malloc + call malloc pop ebx ; 2c. Check the result. If allocation failed, go to 7. - pop esi ; restore allocated pointer to DISK - test eax, eax - jz .free + pop esi ; restore allocated pointer to DISK + test eax, eax + jz .free ; 2d. Store the allocated pointer to the DISK structure. - mov [esi+DISK.Name], eax + mov [esi+DISK.Name], eax ; 2e. Copy the name. @@: - mov dl, [ebx] - mov [eax], dl - inc ebx - inc eax - test dl, dl - jnz @b + mov dl, [ebx] + mov [eax], dl + inc ebx + inc eax + test dl, dl + jnz @b ; 3. Copy other arguments of the function to the DISK structure. - mov eax, [esp+4+8] - mov [esi+DISK.Functions], eax - mov eax, [esp+12+8] - mov [esi+DISK.UserData], eax - mov eax, [esp+16+8] - mov [esi+DISK.DriverFlags], eax + mov eax, [esp+4+8] + mov [esi+DISK.Functions], eax + mov eax, [esp+12+8] + mov [esi+DISK.UserData], eax + mov eax, [esp+16+8] + mov [esi+DISK.DriverFlags], eax ; 4. Initialize other fields of the DISK structure. ; Media is not inserted, reference counter is 1. - lea ecx, [esi+DISK.MediaLock] - call mutex_init - xor eax, eax - mov dword [esi+DISK.MediaInserted], eax + lea ecx, [esi+DISK.MediaLock] + call mutex_init + xor eax, eax + mov dword [esi+DISK.MediaInserted], eax mov [esi+DISK.MediaRefCount], eax - inc eax - mov [esi+DISK.RefCount], eax + inc eax + mov [esi+DISK.RefCount], eax ; The DISK structure is initialized. ; 5. Insert the new structure to the global list. ; 5a. Acquire the mutex. - mov ecx, disk_list_mutex - call mutex_lock + mov ecx, disk_list_mutex + call mutex_lock ; 5b. Insert item to the tail of double-linked list. - mov edx, disk_list - list_add_tail esi, edx ;esi= new edx= list head + mov edx, disk_list + list_add_tail esi, edx ;esi= new edx= list head ; 5c. Release the mutex. - call mutex_unlock + call mutex_unlock ; 6. Return with eax = pointer to DISK. - xchg eax, esi - jmp .nothing + xchg eax, esi + jmp .nothing .free: ; Memory allocation for DISK structure succeeded, but for disk name failed. ; 7. Free the DISK structure. - xchg eax, esi - call free + xchg eax, esi + call free ; 8. Return with eax = 0. - xor eax, eax + xor eax, eax .nothing: ; 9. Return. - pop esi ebx ; restore used registers to be stdcall - ret 16 ; purge 4 dword arguments to be stdcall + pop esi ebx ; restore used registers to be stdcall + ret 16 ; purge 4 dword arguments to be stdcall ; This function deletes a disk device from the global filesystem. ; This includes: @@ -418,29 +418,29 @@ disk_add: ; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. ; Return value: none. disk_del: - push esi ; save used registers to be stdcall + push esi ; save used registers to be stdcall ; 1. Force media to be removed. If the media is already removed, the ; call does nothing. mov esi, [esp+4+4] ; esi = handle of the disk - stdcall disk_media_changed, esi, 0 + stdcall disk_media_changed, esi, 0 ; 2. Delete the structure from the global list. ; 2a. Acquire the mutex. - mov ecx, disk_list_mutex - call mutex_lock + mov ecx, disk_list_mutex + call mutex_lock ; 2b. Delete item from double-linked list. - mov eax, [esi+DISK.Next] - mov edx, [esi+DISK.Prev] - mov [eax+DISK.Prev], edx - mov [edx+DISK.Next], eax + mov eax, [esi+DISK.Next] + mov edx, [esi+DISK.Prev] + mov [eax+DISK.Prev], edx + mov [edx+DISK.Next], eax ; 2c. Release the mutex. - call mutex_unlock + call mutex_unlock ; 3. The structure still has one reference created in disk_add. Remove this ; reference. If there are no other references, disk_dereference will free the ; structure. - call disk_dereference + call disk_dereference ; 4. Return. - pop esi ; restore used registers to be stdcall - ret 4 ; purge 1 dword argument to be stdcall + pop esi ; restore used registers to be stdcall + ret 4 ; purge 1 dword argument to be stdcall ; This is an internal function which removes a previously obtained reference ; to the disk. If this is the last reference, this function lets the driver @@ -452,20 +452,20 @@ disk_dereference: lock dec [esi+DISK.RefCount] ; 2. If the result is nonzero, there are other references, so nothing to do. ; In this case, return (go to 4). - jnz .nothing + jnz .nothing ; 3. If we are here, we just removed the last reference and must destroy the ; disk object. ; 3a. Call the driver. - mov al, DISKFUNC.close - stdcall disk_call_driver + mov al, DISKFUNC.close + stdcall disk_call_driver ; 3b. Free the structure. - xchg eax, esi + xchg eax, esi push ebx - call free + call free pop ebx ; 4. Return. .nothing: - ret + ret ; This is an internal function which removes a previously obtained reference ; to the media. If this is the last reference, this function calls 'closemedia' @@ -478,7 +478,7 @@ disk_media_dereference: lock dec [esi+DISK.MediaRefCount] ; 2. If the result is nonzero, there are other references, so nothing to do. ; In this case, return (go to 4). - jnz .nothing + jnz .nothing ; 3. If we are here, we just removed the last reference and must destroy the ; media object. ; Note that the same place inside the DISK structure is reused for all media @@ -497,27 +497,27 @@ disk_media_dereference: ; does not matter when this flag is cleared. In the second case this flag must ; be cleared after all other actions, including call to 'closemedia'. ; 3a. Free all partitions. - push esi edi - mov edi, [esi+DISK.NumPartitions] - mov esi, [esi+DISK.Partitions] - test edi, edi - jz .nofree + push esi edi + mov edi, [esi+DISK.NumPartitions] + mov esi, [esi+DISK.Partitions] + test edi, edi + jz .nofree .freeloop: - lodsd - call free - dec edi - jnz .freeloop + lodsd + call free + dec edi + jnz .freeloop .nofree: - pop edi esi + pop edi esi ; 3b. Free the cache. - call disk_free_cache + call disk_free_cache ; 3c. Call the driver. - mov al, DISKFUNC.closemedia - stdcall disk_call_driver + mov al, DISKFUNC.closemedia + stdcall disk_call_driver ; 3d. Clear the flag. - mov [esi+DISK.MediaUsed], 0 + mov [esi+DISK.MediaUsed], 0 .nothing: - ret + ret ; This function is called by the driver and informs the kernel that the media ; has changed. If the media is non-removable, it is called exactly once @@ -526,28 +526,28 @@ disk_media_dereference: ; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. ; [esp+8] = new status of the media: zero = no media, nonzero = media inserted. disk_media_changed: - push ebx esi edi ; save used registers to be stdcall + push ebx esi edi ; save used registers to be stdcall ; 1. Remove the existing media, if it is present. - mov esi, [esp+4+12] ; esi = pointer to DISK + mov esi, [esp+4+12] ; esi = pointer to DISK ; 1a. Check whether it is present. Since DISK.MediaInserted is changed only ; in this function and calls to this function are synchronized, no lock is ; required for checking. - cmp [esi+DISK.MediaInserted], 0 - jz .noremove + cmp [esi+DISK.MediaInserted], 0 + jz .noremove ; We really need to remove the media. ; 1b. Acquire mutex. - lea ecx, [esi+DISK.MediaLock] - call mutex_lock + lea ecx, [esi+DISK.MediaLock] + call mutex_lock ; 1c. Clear the flag. - mov [esi+DISK.MediaInserted], 0 + mov [esi+DISK.MediaInserted], 0 ; 1d. Release mutex. - call mutex_unlock + call mutex_unlock ; 1e. Remove the "lifetime" reference and possibly destroy the structure. - call disk_media_dereference + call disk_media_dereference .noremove: ; 2. Test whether there is new media. - cmp dword [esp+8+12], 0 - jz .noinsert + cmp dword [esp+8+12], 0 + jz .noinsert ; Yep, there is. ; 3. Process the new media. We assume that all media fields are available to ; use, see comments in 'disk_media_dereference' (this covers using by previous @@ -555,28 +555,28 @@ disk_media_changed: ; (this covers using by new media referencers). ; 3a. Call the 'querymedia' callback. ; .Flags are set to zero for possible future extensions. - lea edx, [esi+DISK.MediaInfo] - and [edx+DISKMEDIAINFO.Flags], 0 - mov al, DISKFUNC.querymedia - stdcall disk_call_driver, edx + lea edx, [esi+DISK.MediaInfo] + and [edx+DISKMEDIAINFO.Flags], 0 + mov al, DISKFUNC.querymedia + stdcall disk_call_driver, edx ; 3b. Check the result of the callback. Abort if it failed. - test eax, eax - jnz .noinsert + test eax, eax + jnz .noinsert ; 3c. Allocate the cache unless disabled by the driver. Abort if failed. - call disk_init_cache - test al, al - jz .noinsert + call disk_init_cache + test al, al + jz .noinsert ; 3d. Acquire the lifetime reference for the media object. - inc [esi+DISK.MediaRefCount] + inc [esi+DISK.MediaRefCount] ; 3e. Scan for partitions. Ignore result; the list of partitions is valid even ; on errors. - call disk_scan_partitions + call disk_scan_partitions ; 3f. Media is inserted and available for use. - inc [esi+DISK.MediaInserted] + inc [esi+DISK.MediaInserted] .noinsert: ; 4. Return. - pop edi esi ebx ; restore used registers to be stdcall - ret 8 ; purge 2 dword arguments to be stdcall + pop edi esi ebx ; restore used registers to be stdcall + ret 8 ; purge 2 dword arguments to be stdcall ; This function is a thunk for all functions of a disk driver. ; It checks whether the referenced function is implemented in the driver. @@ -587,45 +587,45 @@ disk_media_changed: ; stack is the same as for the corresponding function except that the ; first parameter (void* userdata) is prepended automatically. disk_call_driver: - movzx eax, al ; eax = offset of function in the DISKFUNC structure + movzx eax, al ; eax = offset of function in the DISKFUNC structure ; 1. Prepend the first argument to the stack. - pop ecx ; ecx = return address - push [esi+DISK.UserData] ; add argument - push ecx ; save return address + pop ecx ; ecx = return address + push [esi+DISK.UserData] ; add argument + push ecx ; save return address ; 2. Check that the required function is inside the table. If not, go to 5. - mov ecx, [esi+DISK.Functions] - cmp eax, [ecx+DISKFUNC.strucsize] - jae .default + mov ecx, [esi+DISK.Functions] + cmp eax, [ecx+DISKFUNC.strucsize] + jae .default ; 3. Check that the required function is implemented. If not, go to 5. - mov ecx, [ecx+eax] - test ecx, ecx - jz .default + mov ecx, [ecx+eax] + test ecx, ecx + jz .default ; 4. Jump to the required function. - jmp ecx + jmp ecx .default: ; 5. Driver does not implement the required function; use default implementation. - jmp dword [disk_default_callbacks+eax-4] + jmp dword [disk_default_callbacks+eax-4] ; The default implementation of DISKFUNC.querymedia. disk_default_querymedia: - push DISK_STATUS_INVALID_CALL - pop eax - ret 8 + push DISK_STATUS_INVALID_CALL + pop eax + ret 8 ; The default implementation of DISKFUNC.read and DISKFUNC.write. disk_default_read: disk_default_write: - push DISK_STATUS_INVALID_CALL - pop eax - ret 20 + push DISK_STATUS_INVALID_CALL + pop eax + ret 20 ; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and ; DISKFUNC.flush. disk_default_close: disk_default_closemedia: disk_default_flush: - xor eax, eax - ret 4 + xor eax, eax + ret 4 ; The default implementation of DISKFUNC.adjust_cache_size. disk_default_adjust_cache_size: @@ -639,26 +639,26 @@ disk_default_adjust_cache_size: ; esi = pointer to the DISK structure. disk_scan_partitions: ; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list. - and [esi+DISK.NumPartitions], 0 - and [esi+DISK.Partitions], 0 + and [esi+DISK.NumPartitions], 0 + and [esi+DISK.Partitions], 0 ; 2. Currently we can work only with 512-bytes sectors. Check this restriction. ; The only exception is 2048-bytes CD/DVD, but they are not supported yet by ; this code. - cmp [esi+DISK.MediaInfo.SectorSize], 512 - jz .doscan - DEBUGF 1,'K : sector size is %d, only 512 is supported\n',[esi+DISK.MediaInfo.SectorSize] - ret + cmp [esi+DISK.MediaInfo.SectorSize], 512 + jz .doscan + DEBUGF 1,'K : sector size is %d, only 512 is supported\n',[esi+DISK.MediaInfo.SectorSize] + ret .doscan: ; 3. Acquire the buffer for MBR and bootsector tests. See the comment before ; the 'partition_buffer_users' variable. - mov ebx, mbr_buffer ; assume the global buffer is free + mov ebx, mbr_buffer ; assume the global buffer is free lock inc [partition_buffer_users] - jz .buffer_acquired ; yes, it is free + jz .buffer_acquired ; yes, it is free lock dec [partition_buffer_users] ; no, we must allocate - stdcall kernel_alloc, 512*3 - test eax, eax - jz .nothing - xchg eax, ebx + stdcall kernel_alloc, 512*3 + test eax, eax + jz .nothing + xchg eax, ebx .buffer_acquired: ; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no ; more than MAX_NUM_PARTITION times. @@ -667,32 +667,32 @@ disk_scan_partitions: ; [esp] will hold the sector number for current extended partition, if there ; is one. ; [esp+4] will hold the counter that prevents long loops. - push ebp ; save ebp - push MAX_NUM_PARTITIONS ; the counter of max MBRs to process - xor ebp, ebp ; start from sector zero - push ebp ; no extended partition yet + push ebp ; save ebp + push MAX_NUM_PARTITIONS ; the counter of max MBRs to process + xor ebp, ebp ; start from sector zero + push ebp ; no extended partition yet .new_mbr: ; 5. Read the current sector. ; Note that 'read' callback operates with 64-bit sector numbers, so we must ; push additional zero as a high dword of sector number. - mov al, DISKFUNC.read - push 1 - stdcall disk_call_driver, ebx, ebp, 0, esp - pop ecx + mov al, DISKFUNC.read + push 1 + stdcall disk_call_driver, ebx, ebp, 0, esp + pop ecx ; 6. If the read has failed, abort the loop. - dec ecx - jnz .mbr_failed + dec ecx + jnz .mbr_failed ; 7. Check the MBR/EBR signature. If it is wrong, abort the loop. ; Soon we will access the partition table which starts at ebx+0x1BE, ; so we can fill its address right now. If we do it now, then the addressing ; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset. - lea ecx, [ebx+0x1be] ; ecx -> partition table - cmp word [ecx+0x40], 0xaa55 - jnz .mbr_failed + lea ecx, [ebx+0x1be] ; ecx -> partition table + cmp word [ecx+0x40], 0xaa55 + jnz .mbr_failed ; 8. The MBR is treated differently from EBRs. For MBR we additionally need to ; execute step 9 and possibly step 10. - test ebp, ebp - jnz .mbr + test ebp, ebp + jnz .mbr ; The partition table can be present or not present. In the first case, we just ; read the MBR. In the second case, we just read the bootsector for a ; filesystem. @@ -706,88 +706,88 @@ disk_scan_partitions: ; C. Otherwise, this is an MBR. ; 9. Test for MBR vs bootsector. ; 9a. Check entries. If any is invalid, go to 10 (rule A). - call is_partition_table_entry - jc .notmbr - add ecx, 10h - call is_partition_table_entry - jc .notmbr - add ecx, 10h - call is_partition_table_entry - jc .notmbr - add ecx, 10h - call is_partition_table_entry - jc .notmbr + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr ; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C). - mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type] - or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type] - or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type] - or al, [ecx+PARTITION_TABLE_ENTRY.Type] - jnz .mbr + mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx+PARTITION_TABLE_ENTRY.Type] + jnz .mbr ; 9c. Empty partition table or bootsector with many zeroes? (rule B) - cmp byte [ebx], 0EBh - jz .notmbr - cmp byte [ebx], 0E9h - jnz .mbr + cmp byte [ebx], 0EBh + jz .notmbr + cmp byte [ebx], 0E9h + jnz .mbr .notmbr: ; 10. This is not an MBR. The media is not partitioned. Create one partition ; which covers all the media and abort the loop. - stdcall disk_add_partition, 0, 0, \ - dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4] - jmp .done + stdcall disk_add_partition, 0, 0, \ + dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4] + jmp .done .mbr: ; 11. Process all entries of the new MBR/EBR - lea ecx, [ebx+0x1be] ; ecx -> partition table - push 0 ; assume no extended partition - call process_partition_table_entry - add ecx, 10h - call process_partition_table_entry - add ecx, 10h - call process_partition_table_entry - add ecx, 10h - call process_partition_table_entry - pop ebp + lea ecx, [ebx+0x1be] ; ecx -> partition table + push 0 ; assume no extended partition + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + pop ebp ; 12. Test whether we found a new EBR and should continue the loop. ; 12a. If there was no next EBR, return. - test ebp, ebp - jz .done + test ebp, ebp + jz .done ; Ok, we have EBR. ; 12b. EBRs addresses are relative to the start of extended partition. ; For simplicity, just abort if an 32-bit overflow occurs; large disks ; are most likely partitioned with GPT, not MBR scheme, since the precise ; calculation here would increase limit just twice at the price of big ; compatibility problems. - pop eax ; load extended partition - add ebp, eax - jc .mbr_failed + pop eax ; load extended partition + add ebp, eax + jc .mbr_failed ; 12c. If extended partition has not yet started, start it. - test eax, eax - jnz @f - mov eax, ebp + test eax, eax + jnz @f + mov eax, ebp @@: ; 12c. If the limit is not exceeded, continue the loop. - dec dword [esp] - push eax ; store extended partition - jnz .new_mbr + dec dword [esp] + push eax ; store extended partition + jnz .new_mbr .mbr_failed: .done: ; 13. Cleanup after the loop. - pop eax ; not important anymore - pop eax ; not important anymore - pop ebp ; restore ebp + pop eax ; not important anymore + pop eax ; not important anymore + pop ebp ; restore ebp ; 14. Release the buffer. ; 14a. Test whether it is the global buffer or we have allocated it. - cmp ebx, mbr_buffer - jz .release_partition_buffer + cmp ebx, mbr_buffer + jz .release_partition_buffer ; 14b. If we have allocated it, free it. - xchg eax, ebx - call free - jmp .nothing + xchg eax, ebx + call free + jmp .nothing ; 14c. Otherwise, release reference. .release_partition_buffer: lock dec [partition_buffer_users] .nothing: ; 15. Return. - ret + ret ; This is an internal function called from disk_scan_partitions. It checks ; whether the entry pointed to by ecx is a valid entry of partition table. @@ -797,33 +797,33 @@ disk_scan_partitions: ; greater than the real size. is_partition_table_entry: ; 1. Check .Bootable field. - mov al, [ecx+PARTITION_TABLE_ENTRY.Bootable] - and al, 7Fh - jnz .invalid + mov al, [ecx+PARTITION_TABLE_ENTRY.Bootable] + and al, 7Fh + jnz .invalid ; 3. Calculate first sector + length. Note that .FirstAbsSector is relative ; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length. - mov eax, ebp - xor edx, edx - add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] - adc edx, 0 - add eax, [ecx+PARTITION_TABLE_ENTRY.Length] - adc edx, 0 + mov eax, ebp + xor edx, edx + add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + adc edx, 0 + add eax, [ecx+PARTITION_TABLE_ENTRY.Length] + adc edx, 0 ; 4. Divide by two. - shr edx, 1 - rcr eax, 1 + shr edx, 1 + rcr eax, 1 ; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not ; overflow, this is bad. - sub eax, dword [esi+DISK.MediaInfo.Capacity] - sbb edx, dword [esi+DISK.MediaInfo.Capacity+4] - jnc .invalid + sub eax, dword [esi+DISK.MediaInfo.Capacity] + sbb edx, dword [esi+DISK.MediaInfo.Capacity+4] + jnc .invalid .valid: ; 5. Return success: CF is cleared. - clc - ret + clc + ret .invalid: ; 6. Return fail: CF is set. - stc - ret + stc + ret ; This is an internal function called from disk_scan_partitions. It processes ; the entry pointed to by ecx. @@ -836,48 +836,48 @@ is_partition_table_entry: ; fs-specific checks do this more reliably. process_partition_table_entry: ; 1. Check for valid entry. If invalid, return (go to 5). - call is_partition_table_entry - jc .nothing + call is_partition_table_entry + jc .nothing ; 2. Check for empty entry. If invalid, return (go to 5). - mov al, [ecx+PARTITION_TABLE_ENTRY.Type] - test al, al - jz .nothing + mov al, [ecx+PARTITION_TABLE_ENTRY.Type] + test al, al + jz .nothing ; 3. Check for extended partition. If extended, go to 6. irp type,\ - 0x05,\ ; DOS: extended partition - 0x0f,\ ; WIN95: extended partition, LBA-mapped - 0xc5,\ ; DRDOS/secured: extended partition - 0xd5 ; Old Multiuser DOS secured: extended partition + 0x05,\ ; DOS: extended partition + 0x0f,\ ; WIN95: extended partition, LBA-mapped + 0xc5,\ ; DRDOS/secured: extended partition + 0xd5 ; Old Multiuser DOS secured: extended partition { - cmp al, type - jz .extended + cmp al, type + jz .extended } ; 4. If we are here, that is a normal partition. Add it to the list. ; Note that the first sector is relative to MBR/EBR. - mov eax, ebp - xor edx, edx - add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] - adc edx, 0 - push ecx - stdcall disk_add_partition, eax, edx, \ - [ecx+PARTITION_TABLE_ENTRY.Length], 0 - pop ecx + mov eax, ebp + xor edx, edx + add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + adc edx, 0 + push ecx + stdcall disk_add_partition, eax, edx, \ + [ecx+PARTITION_TABLE_ENTRY.Length], 0 + pop ecx .nothing: ; 5. Return. - ret + ret .extended: ; 6. If we are here, that is an extended partition. Store the address. - mov eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] - mov [esp+4], eax - ret + mov eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + mov [esp+4], eax + ret ; This is an internal function called from disk_scan_partitions and ; process_partition_table_entry. It adds one partition to the list of ; partitions for the media. proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword ; 1. Check that this partition will not exceed the limit on total number. - cmp [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS - jae .nothing + cmp [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS + jae .nothing ; 2. Check that this partition does not overlap with any already registered ; partition. Since any file system assumes that the disk data will not change ; outside of its control, such overlap could be destructive. @@ -885,88 +885,88 @@ proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword ; to be large, the simple linear search is sufficient. ; 2a. Prepare the loop: edi will point to the current item of .Partitions ; array, ecx will be the current item, ebx will hold number of items left. - mov edi, [esi+DISK.Partitions] - mov ebx, [esi+DISK.NumPartitions] - test ebx, ebx - jz .partitionok + mov edi, [esi+DISK.Partitions] + mov ebx, [esi+DISK.NumPartitions] + test ebx, ebx + jz .partitionok .scan_existing: ; 2b. Get the next partition. - mov ecx, [edi] - add edi, 4 + mov ecx, [edi] + add edi, 4 ; The range [.FirstSector, .FirstSector+.Length) must be either entirely to ; the left of [start, start+length) or entirely to the right. ; 2c. Subtract .FirstSector - start. The possible overflow distinguish between ; cases "to the left" (2e) and "to the right" (2d). - mov eax, dword [ecx+PARTITION.FirstSector] - mov edx, dword [ecx+PARTITION.FirstSector+4] - sub eax, dword [start] - sbb edx, dword [start+4] - jb .less + mov eax, dword [ecx+PARTITION.FirstSector] + mov edx, dword [ecx+PARTITION.FirstSector+4] + sub eax, dword [start] + sbb edx, dword [start+4] + jb .less ; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector ; is greater than or equal to start+length; the subtraction ; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is ; good or to 2f in the other case. - sub eax, dword [length] - sbb edx, dword [length+4] - jb .overlap - jmp .next_existing + sub eax, dword [length] + sbb edx, dword [length+4] + jb .overlap + jmp .next_existing .less: ; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less ; than or equal to start. If the addition (.FirstSector-start) + .Length does ; not cause overflow, then .FirstSector + .Length is strictly less than start; ; since the equality is also valid, use decrement preliminarily. Go to 2g or ; 2f depending on the overflow. - sub eax, 1 - sbb edx, 0 - add eax, dword [ecx+PARTITION.Length] - adc edx, dword [ecx+PARTITION.Length+4] - jnc .next_existing + sub eax, 1 + sbb edx, 0 + add eax, dword [ecx+PARTITION.Length] + adc edx, dword [ecx+PARTITION.Length+4] + jnc .next_existing .overlap: ; 2f. The partition overlaps with previously registered partition. Say warning ; and return with nothing done. - dbgstr 'two partitions overlap, ignoring the last one' - jmp .nothing + dbgstr 'two partitions overlap, ignoring the last one' + jmp .nothing .next_existing: ; 2g. The partition does not overlap with the current partition. Continue the ; loop. - dec ebx - jnz .scan_existing + dec ebx + jnz .scan_existing .partitionok: ; 3. The partition has passed tests. Reallocate the partitions array for a new ; entry. ; 3a. Call the allocator. - mov eax, [esi+DISK.NumPartitions] - inc eax ; one more entry - shl eax, 2 ; each entry is dword - call malloc + mov eax, [esi+DISK.NumPartitions] + inc eax ; one more entry + shl eax, 2 ; each entry is dword + call malloc ; 3b. Test the result. If failed, return with nothing done. - test eax, eax - jz .nothing + test eax, eax + jz .nothing ; 3c. Copy the old array to the new array. - mov edi, eax - push esi - mov ecx, [esi+DISK.NumPartitions] - mov esi, [esi+DISK.Partitions] - rep movsd - pop esi + mov edi, eax + push esi + mov ecx, [esi+DISK.NumPartitions] + mov esi, [esi+DISK.Partitions] + rep movsd + pop esi ; 3d. Set the field in the DISK structure to the new array. - xchg [esi+DISK.Partitions], eax + xchg [esi+DISK.Partitions], eax ; 3e. Free the old array. - call free + call free ; 4. Recognize the file system. ; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure ; with possible filesystem-specific fields. - call disk_detect_partition + call disk_detect_partition ; 4b. Check return value. If zero, return with list not changed; so far only ; the array was reallocated, this is ok for other code. - test eax, eax - jz .nothing + test eax, eax + jz .nothing ; 5. Insert the new partition to the list. - stosd - inc [esi+DISK.NumPartitions] + stosd + inc [esi+DISK.NumPartitions] ; 6. Return. .nothing: - ret + ret endp ; This is an internal function called from disk_add_partition. @@ -977,8 +977,8 @@ disk_detect_partition: ; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp ; and [ebp+4]=return address. virtual at ebp+8 -.start dq ? -.length dq ? +.start dq ? +.length dq ? end virtual ; When disk_add_partition is called, ebx contains a pointer to ; a two-sectors-sized buffer. This function saves ebx in the stack @@ -1002,26 +1002,26 @@ end virtual jnz .success ; 3. No file system has recognized the volume, so just allocate the PARTITION ; structure without extra fields. - push sizeof.PARTITION - pop eax - call malloc - test eax, eax - jz .nothing - mov edx, dword [.start] - mov dword [eax+PARTITION.FirstSector], edx - mov edx, dword [.start+4] - mov dword [eax+PARTITION.FirstSector+4], edx - mov edx, dword [.length] - mov dword [eax+PARTITION.Length], edx - mov edx, dword [.length+4] - mov dword [eax+PARTITION.Length+4], edx + push sizeof.PARTITION + pop eax + call malloc + test eax, eax + jz .nothing + mov edx, dword [.start] + mov dword [eax+PARTITION.FirstSector], edx + mov edx, dword [.start+4] + mov dword [eax+PARTITION.FirstSector+4], edx + mov edx, dword [.length] + mov dword [eax+PARTITION.Length], edx + mov edx, dword [.length+4] + mov dword [eax+PARTITION.Length+4], edx mov [eax+PARTITION.Disk], esi and [eax+PARTITION.FSUserFunctions], 0 .success: .nothing: ; 4. Return with eax = pointer to PARTITION or NULL. pop ecx - ret + ret ; This function is called from file_system_lfn. ; This handler gets the control each time when fn 70 is called @@ -1032,177 +1032,177 @@ end virtual ; but instead pop return address and return directly to the caller ; otherwise simply return dyndisk_handler: - push ebx edi ; save registers used in file_system_lfn + push ebx edi ; save registers used in file_system_lfn ; 1. Acquire the mutex. - mov ecx, disk_list_mutex - call mutex_lock + mov ecx, disk_list_mutex + call mutex_lock ; 2. Loop over the list of DISK structures. ; 2a. Initialize. - mov ebx, disk_list + mov ebx, disk_list .scan: ; 2b. Get the next item. - mov ebx, [ebx+DISK.Next] + mov ebx, [ebx+DISK.Next] ; 2c. Check whether the list is done. If so, go to 3. - cmp ebx, disk_list - jz .notfound + cmp ebx, disk_list + jz .notfound ; 2d. Compare names. If names match, go to 5. - mov edi, [ebx+DISK.Name] - push esi + mov edi, [ebx+DISK.Name] + push esi @@: ; esi points to the name from fs operation; it is terminated by zero or slash. - lodsb - test al, al - jz .eoin_dec - cmp al, '/' - jz .eoin + lodsb + test al, al + jz .eoin_dec + cmp al, '/' + jz .eoin ; edi points to the disk name. - inc edi + inc edi ; edi points to lowercase name, this is a requirement for the driver. ; Characters at esi can have any register. Lowercase the current character. ; This lowercasing works for latin letters and digits; since the disk name ; should not contain other symbols, this is ok. - or al, 20h - cmp al, [edi-1] - jz @b + or al, 20h + cmp al, [edi-1] + jz @b .wrongname: ; 2f. Names don't match. Continue the loop. - pop esi - jmp .scan + pop esi + jmp .scan .notfound: ; The loop is done and no name matches. ; 3. Release the mutex. - call mutex_unlock + call mutex_unlock ; 4. Return normally. - pop edi ebx ; restore registers used in file_system_lfn - ret + pop edi ebx ; restore registers used in file_system_lfn + ret ; part of 2d: the name matches partially, but we must check that this is full ; equality. .eoin_dec: - dec esi + dec esi .eoin: - cmp byte [edi], 0 - jnz .wrongname + cmp byte [edi], 0 + jnz .wrongname ; We found the addressed DISK structure. ; 5. Reference the disk. lock inc [ebx+DISK.RefCount] ; 6. Now we are sure that the DISK structure is not going to die at least ; while we are working with it, so release the global mutex. - call mutex_unlock + call mutex_unlock pop ecx ; pop from the stack saved value of esi ; 7. Acquire the mutex for media object. - pop edi ; restore edi - lea ecx, [ebx+DISK.MediaLock] - call mutex_lock + pop edi ; restore edi + lea ecx, [ebx+DISK.MediaLock] + call mutex_lock ; 8. Get the media object. If it is not NULL, reference it. - xor edx, edx - cmp [ebx+DISK.MediaInserted], dl - jz @f - mov edx, ebx - inc [ebx+DISK.MediaRefCount] + xor edx, edx + cmp [ebx+DISK.MediaInserted], dl + jz @f + mov edx, ebx + inc [ebx+DISK.MediaRefCount] @@: ; 9. Now we are sure that the media object, if it exists, is not going to die ; at least while we are working with it, so release the mutex for media object. - call mutex_unlock - mov ecx, ebx - pop ebx eax ; restore ebx, pop return address + call mutex_unlock + mov ecx, ebx + pop ebx eax ; restore ebx, pop return address ; 10. Check whether the fs operation wants to enumerate partitions (go to 11) ; or work with some concrete partition (go to 12). - cmp byte [esi], 0 - jnz .haspartition + cmp byte [esi], 0 + jnz .haspartition ; 11. The fs operation wants to enumerate partitions. ; 11a. Only "list directory" operation is applicable to / path. Check ; the operation code. If wrong, go to 13. - cmp dword [ebx], 1 - jnz .access_denied + cmp dword [ebx], 1 + jnz .access_denied ; 11b. If the media is inserted, use 'fs_dyndisk_next' as an enumeration ; procedure. Otherwise, use 'fs_dyndisk_next_nomedia'. - mov esi, fs_dyndisk_next_nomedia - test edx, edx - jz @f - mov esi, fs_dyndisk_next + mov esi, fs_dyndisk_next_nomedia + test edx, edx + jz @f + mov esi, fs_dyndisk_next @@: ; 11c. Let the procedure from fs_lfn.inc do the job. - jmp file_system_lfn.maindir_noesi + jmp file_system_lfn.maindir_noesi .haspartition: ; 12. The fs operation has specified some partition. ; 12a. Store parameters for callback functions. - push edx - push ecx + push edx + push ecx ; 12b. Store callback functions. - push dyndisk_cleanup - push fs_dyndisk - mov edi, esp + push dyndisk_cleanup + push fs_dyndisk + mov edi, esp ; 12c. Let the procedure from fs_lfn.inc do the job. - jmp file_system_lfn.found2 + jmp file_system_lfn.found2 .access_denied: ; 13. Fail the operation with the appropriate code. - mov dword [esp+32], ERROR_ACCESS_DENIED + mov dword [esp+32], ERROR_ACCESS_DENIED .cleanup: ; 14. Cleanup. - mov esi, ecx ; disk*dereference assume that esi points to DISK + mov esi, ecx ; disk*dereference assume that esi points to DISK .cleanup_esi: - test edx, edx ; if there are no media, we didn't reference it - jz @f - call disk_media_dereference + test edx, edx ; if there are no media, we didn't reference it + jz @f + call disk_media_dereference @@: - call disk_dereference + call disk_dereference ; 15. Return. - ret + ret ; This is a callback for cleaning up things called from file_system_lfn.found2. dyndisk_cleanup: - mov esi, [edi+8] - mov edx, [edi+12] - jmp dyndisk_handler.cleanup_esi + mov esi, [edi+8] + mov edx, [edi+12] + jmp dyndisk_handler.cleanup_esi ; This is a callback for enumerating partitions called from ; file_system_lfn.maindir in the case of inserted media. ; It just increments eax until DISK.NumPartitions reached and then ; cleans up. fs_dyndisk_next: - cmp eax, [ecx+DISK.NumPartitions] - jae .nomore - inc eax - clc - ret + cmp eax, [ecx+DISK.NumPartitions] + jae .nomore + inc eax + clc + ret .nomore: - pusha - mov esi, ecx - call disk_media_dereference - call disk_dereference - popa - stc - ret + pusha + mov esi, ecx + call disk_media_dereference + call disk_dereference + popa + stc + ret ; This is a callback for enumerating partitions called from ; file_system_lfn.maindir in the case of missing media. ; In this case we create one pseudo-partition. fs_dyndisk_next_nomedia: - cmp eax, 1 - jae .nomore - inc eax - clc - ret + cmp eax, 1 + jae .nomore + inc eax + clc + ret .nomore: - pusha - mov esi, ecx - call disk_dereference - popa - stc - ret + pusha + mov esi, ecx + call disk_dereference + popa + stc + ret ; This is a callback for doing real work with selected partition. ; Currently this is just placeholder, since no file systems are supported. ; edi = esp -> {dd fs_dyndisk, dd dyndisk_cleanup, dd pointer to DISK, dd media object} ; ecx = partition number, esi+ebp = ASCIIZ name fs_dyndisk: - dec ecx ; convert to zero-based partition index - pop edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx - test eax, eax - jz .nomedia + dec ecx ; convert to zero-based partition index + pop edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx + test eax, eax + jz .nomedia .main: - cmp ecx, [edx+DISK.NumPartitions] - jae .notfound + cmp ecx, [edx+DISK.NumPartitions] + jae .notfound mov eax, [edx+DISK.Partitions] mov eax, [eax+ecx*4] mov edi, [eax+PARTITION.FSUserFunctions] @@ -1220,50 +1220,50 @@ fs_dyndisk: mov dword [esp+32], eax mov dword [esp+20], ebx .cleanup: - mov esi, edx - call disk_media_dereference - call disk_dereference - ret + mov esi, edx + call disk_media_dereference + call disk_dereference + ret .nofs: mov dword [esp+32], ERROR_UNKNOWN_FS jmp .cleanup .notfound: - mov dword [esp+32], ERROR_FILE_NOT_FOUND - jmp .cleanup + mov dword [esp+32], ERROR_FILE_NOT_FOUND + jmp .cleanup .unsupported: mov dword [esp+32], ERROR_UNSUPPORTED_FS jmp .cleanup .nomedia: - test ecx, ecx - jnz .notfound - test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION - jz .deverror + test ecx, ecx + jnz .notfound + test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION + jz .deverror ; if the driver does not support insert notifications and we are the only fs ; operation with this disk, issue the fake insert notification; if media is ; still not inserted, 'disk_media_changed' will detect this and do nothing - lea ecx, [edx+DISK.MediaLock] - call mutex_lock - cmp [edx+DISK.MediaRefCount], 1 - jnz .noluck - call mutex_unlock - push edx - stdcall disk_media_changed, edx, 1 - pop edx - lea ecx, [edx+DISK.MediaLock] - call mutex_lock - cmp [edx+DISK.MediaInserted], 0 - jz .noluck + lea ecx, [edx+DISK.MediaLock] + call mutex_lock + cmp [edx+DISK.MediaRefCount], 1 + jnz .noluck + call mutex_unlock + push edx + stdcall disk_media_changed, edx, 1 + pop edx + lea ecx, [edx+DISK.MediaLock] + call mutex_lock + cmp [edx+DISK.MediaInserted], 0 + jz .noluck lock inc [edx+DISK.MediaRefCount] - call mutex_unlock - xor ecx, ecx - jmp .main + call mutex_unlock + xor ecx, ecx + jmp .main .noluck: - call mutex_unlock + call mutex_unlock .deverror: - mov dword [esp+32], ERROR_DEVICE - mov esi, edx - call disk_dereference - ret + mov dword [esp+32], ERROR_DEVICE + mov esi, edx + call disk_dereference + ret ; This function is called from file_system_lfn. ; This handler is called when virtual root is enumerated @@ -1273,34 +1273,34 @@ fs_dyndisk: ; out: eax = 0 => no more items ; eax != 0 => buffer pointed to by edi contains name of item dyndisk_enum_root: - push edx ; save register used in file_system_lfn - mov ecx, disk_list_mutex ; it will be useful + push edx ; save register used in file_system_lfn + mov ecx, disk_list_mutex ; it will be useful ; 1. If this is the first call, acquire the mutex and initialize. - test eax, eax - jnz .notfirst - call mutex_lock - mov eax, disk_list + test eax, eax + jnz .notfirst + call mutex_lock + mov eax, disk_list .notfirst: ; 2. Get next item. - mov eax, [eax+DISK.Next] + mov eax, [eax+DISK.Next] ; 3. If there are no more items, go to 6. - cmp eax, disk_list - jz .last + cmp eax, disk_list + jz .last ; 4. Copy name from the DISK structure to edi. - push eax esi - mov esi, [eax+DISK.Name] + push eax esi + mov esi, [eax+DISK.Name] @@: - lodsb - stosb - test al, al - jnz @b - pop esi eax + lodsb + stosb + test al, al + jnz @b + pop esi eax ; 5. Return with eax = item. - pop edx ; restore register used in file_system_lfn - ret + pop edx ; restore register used in file_system_lfn + ret .last: ; 6. Release the mutex and return with eax = 0. - call mutex_unlock - xor eax, eax - pop edx ; restore register used in file_system_lfn - ret + call mutex_unlock + xor eax, eax + pop edx ; restore register used in file_system_lfn + ret diff --git a/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc b/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc index 28d377f29..678417fee 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc +++ b/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 @@ -167,7 +167,7 @@ end virtual mov edi, ebx mov ecx, 512/4 - rep movsd ; move data + rep movsd ; move data xor eax, eax ; successful read .read_done: mov ecx, [.cache] @@ -325,7 +325,7 @@ end virtual mov esi, ebx mov ecx, 512/4 - rep movsd ; move data + rep movsd ; move data xor eax, eax ; success .hd_write_access_denied: mov ecx, [.cache] @@ -397,7 +397,7 @@ saved_esi_pos = 16+12 ; size of local variables + size of registers before esi mov dword [esi+8], 1 ; same as in hd mov eax, [esi] mov edx, [esi+4] ; edx:eax = sector to write -; +; Объединяем запись цепочки последовательных секторов в одно обращение к диску cmp ecx, 1 jz .nonext cmp dword [esi+12+8], 2 @@ -560,7 +560,7 @@ disk_init_cache: mov edi, [esi+DISK.SysCache.pointer] lea ecx, [ecx*3] xor eax, eax - rep stosd + rep stosd pop edi mov eax, [esi+DISK.AppCache.data_size] @@ -575,7 +575,7 @@ disk_init_cache: mov edi, [esi+DISK.AppCache.pointer] lea ecx, [ecx*3] xor eax, eax - rep stosd + rep stosd pop edi ; 4. Return with nonzero al. diff --git a/kernel/branches/Kolibri-acpi/blkdev/fdc.inc b/kernel/branches/Kolibri-acpi/blkdev/fdc.inc index b06518570..2f659b624 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/fdc.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/fdc.inc @@ -10,62 +10,62 @@ $Revision$ iglobal ;function pointers. - fdc_irq_func dd fdc_null + fdc_irq_func dd fdc_null endg uglobal - dmasize db 0x0 - dmamode db 0x0 + dmasize db 0x0 + dmamode db 0x0 endg -fdc_init: ;start with clean tracks. +fdc_init: ;start with clean tracks. mov edi, OS_BASE+0xD201 mov al, 0 mov ecx, 160 - rep stosb + rep stosb ret fdc_irq: - call [fdc_irq_func] + call [fdc_irq_func] fdc_null: ret save_image: - call reserve_flp - call restorefatchain - pusha - call check_label + call reserve_flp + call restorefatchain + pusha + call check_label cmp [FDC_Status], 0 - jne unnecessary_save_image - mov [FDD_Track], 0; - mov [FDD_Head], 0; - mov [FDD_Sector], 1; + jne unnecessary_save_image + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 1; Сектор mov esi, RAMDISK - call SeekTrack + call SeekTrack save_image_1: - push esi - call take_data_from_application_1 - pop esi + push esi + call take_data_from_application_1 + pop esi add esi, 512 - call WriteSectWithRetr + call WriteSectWithRetr ; call WriteSector cmp [FDC_Status], 0 - jne unnecessary_save_image - inc [FDD_Sector] + jne unnecessary_save_image + inc [FDD_Sector] cmp [FDD_Sector], 19 - jne save_image_1 + jne save_image_1 mov [FDD_Sector], 1 - inc [FDD_Head] + inc [FDD_Head] cmp [FDD_Head], 2 - jne save_image_1 + jne save_image_1 mov [FDD_Head], 0 - inc [FDD_Track] - call SeekTrack + inc [FDD_Track] + call SeekTrack cmp [FDD_Track], 80 - jne save_image_1 + jne save_image_1 unnecessary_save_image: mov [fdc_irq_func], fdc_null - popa + popa mov [flp_status], 0 - ret + ret diff --git a/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc b/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc index 7a2242f86..abe594fcb 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc @@ -9,12 +9,12 @@ $Revision$ ;********************************************************** -; +; Непосредственная работа с контроллером гибкого диска ;********************************************************** -; . -; Mario79 +; Автор исходного текста Кулаков Владимир Геннадьевич. +; Адаптация и доработка Mario79 -;give_back_application_data: ; +;give_back_application_data: ; переслать приложению ; mov edi,[TASK_BASE] ; mov edi,[edi+TASKDATA.mem_start] ; add edi,ecx @@ -22,11 +22,11 @@ give_back_application_data_1: mov esi, FDD_BUFF;FDD_DataBuffer ;0x40000 xor ecx, ecx mov cx, 128 - cld - rep movsd - ret + cld + rep movsd + ret -;take_data_from_application: ; +;take_data_from_application: ; взять из приложени ; mov esi,[TASK_BASE] ; mov esi,[esi+TASKDATA.mem_start] ; add esi,ecx @@ -34,41 +34,41 @@ take_data_from_application_1: mov edi, FDD_BUFF;FDD_DataBuffer ;0x40000 xor ecx, ecx mov cx, 128 - cld - rep movsd - ret + cld + rep movsd + ret -; (FDC_Status) -FDC_Normal equ 0 ; -FDC_TimeOut equ 1 ; - -FDC_DiskNotFound equ 2 ; -FDC_TrackNotFound equ 3 ; -FDC_SectorNotFound equ 4 ; +; Коды завершения операции с контроллером (FDC_Status) +FDC_Normal equ 0 ;нормальное завершение +FDC_TimeOut equ 1 ;ошибка тайм-аута +FDC_DiskNotFound equ 2 ;в дисководе нет диска +FDC_TrackNotFound equ 3 ;дорожка не найдена +FDC_SectorNotFound equ 4 ;сектор не найден -; ( -; -; 1,44 ) +; Максимальные значения координат сектора (заданные +; значения соответствуют параметрам стандартного +; трехдюймового гибкого диска объемом 1,44 Мб) MAX_Track equ 79 MAX_Head equ 1 MAX_Sector equ 18 uglobal -; +; Счетчик тиков таймера TickCounter dd ? -; +; Код завершения операции с контроллером НГМД FDC_Status DB ? -; +; Флаг прерывания от НГМД FDD_IntFlag DB ? -; +; Момент начала последней операции с НГМД FDD_Time DD ? -; +; Номер дисковода FDD_Type db 0 -; +; Координаты сектора FDD_Track DB ? FDD_Head DB ? FDD_Sector DB ? -; +; Блок результата операции FDC_ST0 DB ? FDC_ST1 DB ? FDC_ST2 DB ? @@ -76,18 +76,18 @@ FDC_C DB ? FDC_H DB ? FDC_R DB ? FDC_N DB ? -; +; Счетчик повторения операции чтени ReadRepCounter DB ? -; +; Счетчик повторения операции рекалибровки RecalRepCounter DB ? endg -; +; Область памяти для хранения прочитанного сектора ;FDD_DataBuffer: times 512 db 0 ;DB 512 DUP (?) fdd_motor_status db 0 timer_fdd_motor dd 0 ;************************************* -;* * +;* ИНИЦИАЛИЗАЦИЯ РЕЖИМА ПДП ДЛЯ НГМД * ;************************************* Init_FDC_DMA: pushad @@ -108,7 +108,7 @@ Init_FDC_DMA: mov al, 0 out 0x0c, al; reset flip-flop mov al, 0xff;set count (actual size -1) - out 0x5, al + out 0x5, al mov al, 0x1;[dmasize] ;(0x1ff = 511 / 0x23ff =9215) out 0x5, al mov al, 2 @@ -117,62 +117,62 @@ Init_FDC_DMA: ret ;*********************************** -;* FDC * -;* : * -;* AL - . * +;* ЗАПИСАТЬ БАЙТ В ПОРТ ДАННЫХ FDC * +;* Параметры: * +;* AL - выводимый байт. * ;*********************************** FDCDataOutput: ; pusha - push eax ecx edx - mov AH, AL ; AH -; + push eax ecx edx + 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 -; + jmp @@End_5 +; Вывести байт в порт данных @@OutByteToFDC: inc DX mov AL, AH out DX, AL @@End_5: ; popa - pop edx ecx eax + pop edx ecx eax 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 -; + jmp @@End_6 +; Ввести байт из порта данных @@GetByteFromFDC: inc DX in AL, DX @@ -182,45 +182,45 @@ FDCDataInput: ret ;********************************************* -;* * +;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД * ;********************************************* FDCInterrupt: -; +; Установить флаг прерывани mov [FDD_IntFlag], 1 ret ;****************************************** -;* * -;* * +;* УСТАНОВИТЬ НОВЫЙ ОБРАБОТЧИК ПРЕРЫВАНИЙ * +;* НГМД * ;****************************************** SetUserInterrupts: mov [fdc_irq_func], FDCInterrupt - ret + ret ;******************************************* -;* * +;* ОЖИДАНИЕ ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД * ;******************************************* WaitFDCInterrupt: pusha -; +; Сбросить байт состояния операции mov [FDC_Status], FDC_Normal -; +; Сбросить флаг прерывани mov [FDD_IntFlag], 0 -; +; Обнулить счетчик тиков mov eax, [timer_ticks] mov [TickCounter], eax -; +; Ожидать установки флага прерывания НГМД @@TestRS_2: cmp [FDD_IntFlag], 0 - jnz @@End_7 ; + jnz @@End_7 ;прерывание произошло call change_task mov eax, [timer_ticks] sub eax, [TickCounter] - cmp eax, 50 ;25 ;5 ; 5 + cmp eax, 50 ;25 ;5 ;ожидать 5 тиков jb @@TestRS_2 ; jl @@TestRS_2 -; - +; Ошибка тайм-аута mov [FDC_Status], FDC_TimeOut ; mov [flp_status],0 @@End_7: @@ -228,7 +228,7 @@ WaitFDCInterrupt: ret ;********************************* -;* "A:" * +;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" * ;********************************* FDDMotorON: pusha @@ -237,11 +237,11 @@ FDDMotorON: mov al, [flp_number] cmp [fdd_motor_status], al je fdd_motor_on -; - mov DX, 3F2h; +; Произвести сброс контроллера НГМД + mov DX, 3F2h;порт управления двигателями mov AL, 0 out DX, AL -; +; Выбрать и включить мотор дисковода cmp [flp_number], 1 jne FDDMotorON_B ; call FDDMotorOFF_B @@ -252,10 +252,10 @@ FDDMotorON_B: mov AL, 2Dh ; Floppy B FDDMotorON_1: out DX, AL -; +; Обнулить счетчик тиков mov eax, [timer_ticks] mov [TickCounter], eax -; 0,5 +; Ожидать 0,5 с @@dT: call change_task mov eax, [timer_ticks] @@ -274,7 +274,7 @@ fdd_motor_on: ret ;***************************************** -;* * +;* СОХРАНЕНИЕ УКАЗАТЕЛЯ ВРЕМЕНИ * ;***************************************** save_timer_fdd_motor: mov eax, [timer_ticks] @@ -282,8 +282,26 @@ save_timer_fdd_motor: 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 @@ end_check_fdd_motor_status: ret ;********************************** -;* * +;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА * ;********************************** FDDMotorOFF: push AX @@ -314,35 +332,35 @@ FDDMotorOFF_1: FDDMotorOFF_2: pop DX pop AX - ; + ; сброс флагов кеширования в связи с устареванием информации mov [root_read], 0 mov [flp_fat], 0 ret FDDMotorOFF_A: - mov DX, 3F2h; + mov DX, 3F2h;порт управления двигателями mov AL, 0Ch ; Floppy A out DX, AL ret FDDMotorOFF_B: - mov DX, 3F2h; + mov DX, 3F2h;порт управления двигателями mov AL, 5h ; Floppy B out DX, AL ret ;******************************* -;* "A:" * +;* РЕКАЛИБРОВКА ДИСКОВОДА "A:" * ;******************************* RecalibrateFDD: pusha call save_timer_fdd_motor -; "" +; Подать команду "Рекалибровка" mov AL, 07h call FDCDataOutput mov AL, 00h call FDCDataOutput -; +; Ожидать завершения операции call WaitFDCInterrupt ; cmp [FDC_Status],0 ; je no_fdc_status_error @@ -353,54 +371,54 @@ RecalibrateFDD: ret ;***************************************************** -;* * -;* : * -;* FDD_Track - (0-79); * -;* FDD_Head - (0-1). * -;* FDC_Status. * +;* ПОИСК ДОРОЖКИ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1). * +;* Результат операции заносится в FDC_Status. * ;***************************************************** SeekTrack: pusha call save_timer_fdd_motor -; "" +; Подать команду "Поиск" mov AL, 0Fh call FDCDataOutput - ; / + ; Передать байт номера головки/накопител mov AL, [FDD_Head] shl AL, 2 call FDCDataOutput - ; + ; Передать байт номера дорожки mov AL, [FDD_Track] call FDCDataOutput -; +; Ожидать завершения операции call WaitFDCInterrupt cmp [FDC_Status], FDC_Normal jne @@Exit -; +; Сохранить результат поиска mov AL, 08h call FDCDataOutput call FDCDataInput mov [FDC_ST0], AL call FDCDataInput mov [FDC_C], AL -; - ; ? +; Проверить результат поиска + ; Поиск завершен? test [FDC_ST0], 100000b je @@Err - ; ? + ; Заданный трек найден? mov AL, [FDC_C] cmp AL, [FDD_Track] jne @@Err - ; ? + ; Номер головки совпадает с заданным? mov AL, [FDC_ST0] and AL, 100b shr AL, 2 cmp AL, [FDD_Head] jne @@Err - ; + ; Операция завершена успешно mov [FDC_Status], FDC_Normal - jmp @@Exit -@@Err: ; + jmp @@Exit +@@Err: ; Трек не найден mov [FDC_Status], FDC_TrackNotFound ; mov [flp_status],0 @@Exit: @@ -409,27 +427,27 @@ SeekTrack: ret ;******************************************************* -;* * -;* : * -;* FDD_Track - (0-79); * -;* FDD_Head - (0-1); * -;* FDD_Sector - (1-18). * -;* FDC_Status. * -;* * -;* FDD_DataBuffer. * +;* ЧТЕНИЕ СЕКТОРА ДАННЫХ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции чтения * +;* содержимое сектора будет занесено в FDD_DataBuffer. * ;******************************************************* ReadSector: pushad call save_timer_fdd_motor -; 500 / +; Установить скорость передачи 500 Кбайт/с mov AX, 0 mov DX, 03F7h out DX, AL -; +; Инициализировать канал прямого доступа к памяти mov [dmamode], 0x46 call Init_FDC_DMA -; " " - mov AL, 0E6h ; +; Подать команду "Чтение данных" + mov AL, 0E6h ;чтение в мультитрековом режиме call FDCDataOutput mov AL, [FDD_Head] shl AL, 2 @@ -440,24 +458,24 @@ ReadSector: call FDCDataOutput mov AL, [FDD_Sector] call FDCDataOutput - mov AL, 2 ; (512 ) + mov AL, 2 ;код размера сектора (512 байт) call FDCDataOutput - mov AL, 18 ;+1; 3Fh ; + mov AL, 18 ;+1; 3Fh ;число секторов на дорожке call FDCDataOutput - mov AL, 1Bh ; GPL + mov AL, 1Bh ;значение GPL call FDCDataOutput - mov AL, 0FFh; DTL + mov AL, 0FFh;значение DTL call FDCDataOutput -; +; Ожидаем прерывание по завершении операции call WaitFDCInterrupt cmp [FDC_Status], FDC_Normal jne @@Exit_1 -; +; Считываем статус завершения операции call GetStatusInfo test [FDC_ST0], 11011000b jnz @@Err_1 mov [FDC_Status], FDC_Normal - jmp @@Exit_1 + jmp @@Exit_1 @@Err_1: mov [FDC_Status], FDC_SectorNotFound ; mov [flp_status],0 @@ -467,21 +485,21 @@ ReadSector: ret ;******************************************************* -;* ( ) * -;* : * -;* FDD_Track - (0-79); * -;* FDD_Head - (0-1); * -;* FDD_Sector - (1-18). * -;* FDC_Status. * -;* * -;* FDD_DataBuffer. * +;* ЧТЕНИЕ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции чтения * +;* содержимое сектора будет занесено в FDD_DataBuffer. * ;******************************************************* ReadSectWithRetr: pusha -; +; Обнулить счетчик повторения операции рекалибровки mov [RecalRepCounter], 0 @@TryAgain: -; +; Обнулить счетчик повторения операции чтени mov [ReadRepCounter], 0 @@ReadSector_1: call ReadSector @@ -489,11 +507,11 @@ ReadSectWithRetr: je @@Exit_2 cmp [FDC_Status], 1 je @@Err_3 - ; + ; Троекратное повторение чтени inc [ReadRepCounter] cmp [ReadRepCounter], 3 jb @@ReadSector_1 - ; + ; Троекратное повторение рекалибровки call RecalibrateFDD call SeekTrack inc [RecalRepCounter] @@ -509,27 +527,27 @@ ReadSectWithRetr: ret ;******************************************************* -;* * -;* : * -;* FDD_Track - (0-79); * -;* FDD_Head - (0-1); * -;* FDD_Sector - (1-18). * -;* FDC_Status. * -;* * -;* FDD_DataBuffer . * +;* ЗАПИСЬ СЕКТОРА ДАННЫХ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции записи * +;* содержимое FDD_DataBuffer будет занесено в сектор. * ;******************************************************* WriteSector: pushad call save_timer_fdd_motor -; 500 / +; Установить скорость передачи 500 Кбайт/с mov AX, 0 mov DX, 03F7h out DX, AL -; +; Инициализировать канал прямого доступа к памяти mov [dmamode], 0x4A call Init_FDC_DMA -; " " - mov AL, 0xC5 ;0x45 ; +; Подать команду "Запись данных" + mov AL, 0xC5 ;0x45 ;запись в мультитрековом режиме call FDCDataOutput mov AL, [FDD_Head] shl AL, 2 @@ -540,24 +558,24 @@ WriteSector: call FDCDataOutput mov AL, [FDD_Sector] call FDCDataOutput - mov AL, 2 ; (512 ) + mov AL, 2 ;код размера сектора (512 байт) call FDCDataOutput - mov AL, 18; 3Fh ; + mov AL, 18; 3Fh ;число секторов на дорожке call FDCDataOutput - mov AL, 1Bh ; GPL + mov AL, 1Bh ;значение GPL call FDCDataOutput - mov AL, 0FFh; DTL + mov AL, 0FFh;значение DTL call FDCDataOutput -; +; Ожидаем прерывание по завершении операции call WaitFDCInterrupt cmp [FDC_Status], FDC_Normal jne @@Exit_3 -; +; Считываем статус завершения операции call GetStatusInfo test [FDC_ST0], 11000000b ;11011000b jnz @@Err_2 mov [FDC_Status], FDC_Normal - jmp @@Exit_3 + jmp @@Exit_3 @@Err_2: mov [FDC_Status], FDC_SectorNotFound @@Exit_3: @@ -566,21 +584,21 @@ WriteSector: ret ;******************************************************* -;* ( ) * -;* : * -;* FDD_Track - (0-79); * -;* FDD_Head - (0-1); * -;* FDD_Sector - (1-18). * -;* FDC_Status. * -;* * -;* FDD_DataBuffer . * +;* ЗАПИСЬ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции записи * +;* содержимое FDD_DataBuffer будет занесено в сектор. * ;******************************************************* WriteSectWithRetr: pusha -; +; Обнулить счетчик повторения операции рекалибровки mov [RecalRepCounter], 0 @@TryAgain_1: -; +; Обнулить счетчик повторения операции чтени mov [ReadRepCounter], 0 @@WriteSector_1: call WriteSector @@ -588,11 +606,11 @@ WriteSectWithRetr: je @@Exit_4 cmp [FDC_Status], 1 je @@Err_4 - ; + ; Троекратное повторение чтени inc [ReadRepCounter] cmp [ReadRepCounter], 3 jb @@WriteSector_1 - ; + ; Троекратное повторение рекалибровки call RecalibrateFDD call SeekTrack inc [RecalRepCounter] @@ -607,7 +625,7 @@ WriteSectWithRetr: ret ;********************************************* -;* * +;* ПОЛУЧИТЬ ИНФОРМАЦИЮ О РЕЗУЛЬТАТЕ ОПЕРАЦИИ * ;********************************************* GetStatusInfo: push AX diff --git a/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc b/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc index 39132111e..9c05db729 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc @@ -19,11 +19,11 @@ hd_read: ; ebx = destination ;----------------------------------------------------------- and [hd_error], 0 - push ecx esi edi ; scan cache + push ecx esi edi ; scan cache ; mov ecx,cache_max ; entries in cache ; mov esi,HD_CACHE+8 - call calculate_cache + call calculate_cache add esi, 8 mov edi, 1 @@ -31,21 +31,21 @@ hd_read: hdreadcache: cmp dword [esi+4], 0; empty - je nohdcache + je nohdcache cmp [esi], eax ; correct sector - je yeshdcache + je yeshdcache nohdcache: add esi, 8 - inc edi - dec ecx - jnz hdreadcache + inc edi + dec ecx + jnz hdreadcache - call find_empty_slot ; ret in edi + call find_empty_slot ; ret in edi cmp [hd_error], 0 - jne return_01 + jne return_01 ; Read through BIOS? cmp [hdpos], 0x80 jae .bios @@ -72,7 +72,7 @@ hd_read: jne return_01 ; lea esi,[edi*8+HD_CACHE] ; push eax - call calculate_cache_1 + call calculate_cache_1 lea esi, [edi*8+esi] ; pop eax @@ -84,78 +84,78 @@ hd_read: mov esi, edi shl esi, 9 ; add esi,HD_CACHE+65536 - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add esi, eax - pop eax + pop eax mov edi, ebx mov ecx, 512/4 - cld - rep movsd ; move data + cld + rep movsd ; move data return_01: - pop edi esi ecx - ret + pop edi esi ecx + ret align 4 hd_read_pio: - push eax edx + push eax edx - call wait_for_hd_idle + call wait_for_hd_idle cmp [hd_error], 0 - jne hd_read_error + jne hd_read_error - cli + cli xor eax, eax mov edx, [hdbase] - inc edx - out dx, al; ATAFeatures ॣ "ᮡ⥩" - inc edx - inc eax - out dx, al; ATASectorCount 稪 ᥪ஢ - inc edx + inc edx + out dx, al; ATAFeatures регистр "особенностей" + inc edx + inc eax + 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 樫 (訩 ) + inc edx + out dx, al; ATACylinder номер цилиндра (младший байт) shr eax, 8 - inc edx - out dx, al; 樫 (訩 ) + inc edx + out dx, al; номер цилиндра (старший байт) shr eax, 8 - inc edx + inc edx and al, 1+2+4+8 add al, byte [hdid] add al, 128+64+32 - out dx, al; / ᪠ - inc edx + out dx, al; номер головки/номер диска + inc edx mov al, 20h - out dx, al; ATACommand ॣ - sti + out dx, al; ATACommand регистр команд + sti - call wait_for_sector_buffer + call wait_for_sector_buffer cmp [hd_error], 0 - jne hd_read_error + jne hd_read_error - cli - push edi + cli + push edi shl edi, 9 ; add edi,HD_CACHE+65536 - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add edi, eax - pop eax + pop eax mov ecx, 256 mov edx, [hdbase] - cld - rep insw - pop edi - sti + cld + rep insw + pop edi + sti - pop edx eax - ret + pop edx eax + ret disable_ide_int: ; mov edx,[hdbase] @@ -179,13 +179,13 @@ hd_write: ; input : eax = block ; ebx = pointer to memory ;----------------------------------------------------------- - push ecx esi edi + push ecx esi edi ; check if the cache already has the sector and overwrite it ; mov ecx,cache_max ; mov esi,HD_CACHE+8 - call calculate_cache + call calculate_cache add esi, 8 mov edi, 1 @@ -193,28 +193,28 @@ hd_write: hdwritecache: cmp dword [esi+4], 0; if cache slot is empty - je not_in_cache_write + je not_in_cache_write cmp [esi], eax ; if the slot has the sector - je yes_in_cache_write + je yes_in_cache_write not_in_cache_write: add esi, 8 - inc edi - dec ecx - jnz hdwritecache + inc edi + dec ecx + jnz hdwritecache ; sector not found in cache ; write the block to a new location - call find_empty_slot ; ret in edi + call find_empty_slot ; ret in edi cmp [hd_error], 0 - jne hd_write_access_denied + jne hd_write_access_denied ; lea esi,[edi*8+HD_CACHE] ; push eax - call calculate_cache_1 + call calculate_cache_1 lea esi, [edi*8+esi] ; pop eax @@ -226,106 +226,106 @@ hd_write: shl edi, 9 ; add edi,HD_CACHE+65536 - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add edi, eax - pop eax + pop eax mov esi, ebx mov ecx, 512/4 - cld - rep movsd ; move data + cld + rep movsd ; move data hd_write_access_denied: - pop edi esi ecx - ret + pop edi esi ecx + ret align 4 cache_write_pio: cmp dword[esi], 0x10000000 - jae .bad + jae .bad ; call disable_ide_int - call wait_for_hd_idle + call wait_for_hd_idle cmp [hd_error], 0 - jne hd_write_error + jne hd_write_error - cli + cli xor eax, eax mov edx, [hdbase] - inc edx + inc edx out dx, al - inc edx - inc eax + inc edx + inc eax out dx, al - inc edx + inc edx mov eax, [esi] ; eax = sector to write out dx, al shr eax, 8 - inc edx + inc edx out dx, al shr eax, 8 - inc edx + inc edx out dx, al shr eax, 8 - inc edx + inc edx and al, 1+2+4+8 add al, byte [hdid] add al, 128+64+32 out dx, al - inc edx + inc edx mov al, 30h out dx, al - sti + sti - call wait_for_sector_buffer + call wait_for_sector_buffer cmp [hd_error], 0 - jne hd_write_error + jne hd_write_error - push ecx esi + push ecx esi - cli + cli mov esi, edi shl esi, 9 ; add esi,HD_CACHE+65536 ; esi = from memory position - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add esi, eax - pop eax + pop eax mov ecx, 256 mov edx, [hdbase] - cld - rep outsw - sti + cld + rep outsw + sti ; call enable_ide_int - pop esi ecx + pop esi ecx - ret + ret .bad: - inc [hd_error] - ret + inc [hd_error] + ret save_hd_wait_timeout: - push eax + push eax mov eax, [timer_ticks] add eax, 300 ; 3 sec timeout mov [hd_wait_timeout], eax - pop eax - ret + pop eax + ret align 4 check_hd_wait_timeout: - push eax + push eax mov eax, [hd_wait_timeout] - cmp [timer_ticks], eax - jg hd_timeout_error - pop eax + cmp [timer_ticks], eax + jg hd_timeout_error + pop eax mov [hd_error], 0 - ret + ret ;iglobal ; hd_timeout_str db 'K : FS - HD timeout',0 @@ -347,8 +347,8 @@ hd_timeout_error: end if mov [hd_error], 1 - pop eax - ret + pop eax + ret hd_read_error: @@ -361,8 +361,8 @@ hd_read_error: else DEBUGF 1,"K : FS - HD read error\n" end if - pop edx eax - ret + pop edx eax + ret hd_write_error: @@ -375,7 +375,7 @@ hd_write_error: else DEBUGF 1,"K : FS - HD write error\n" end if - ret + ret hd_write_error_dma: ; call clear_hd_cache @@ -400,69 +400,69 @@ hd_lba_error: else DEBUGF 1,"K : FS - HD LBA error\n" end if - jmp LBA_read_ret + jmp LBA_read_ret align 4 wait_for_hd_idle: - push eax edx + push eax edx - call save_hd_wait_timeout + call save_hd_wait_timeout mov edx, [hdbase] add edx, 0x7 wfhil1: - call check_hd_wait_timeout + call check_hd_wait_timeout cmp [hd_error], 0 - jne @f + jne @f in al, dx test al, 128 - jnz wfhil1 + jnz wfhil1 @@: - pop edx eax - ret + pop edx eax + ret align 4 wait_for_sector_buffer: - push eax edx + push eax edx mov edx, [hdbase] add edx, 0x7 - call save_hd_wait_timeout + call save_hd_wait_timeout hdwait_sbuf: ; wait for sector buffer to be ready - call check_hd_wait_timeout + call check_hd_wait_timeout cmp [hd_error], 0 - jne @f + jne @f in al, dx test al, 8 - jz hdwait_sbuf + jz hdwait_sbuf mov [hd_error], 0 cmp [hd_setup], 1 ; do not mark error for setup request - je buf_wait_ok + je buf_wait_ok test al, 1 ; previous command ended up with an error - jz buf_wait_ok + jz buf_wait_ok @@: mov [hd_error], 1 buf_wait_ok: - pop edx eax - ret + pop edx eax + ret ; \begin{Mario79} align 4 @@ -615,21 +615,21 @@ hd_read_dma: mov esi, eax shl edi, 9 ; add edi, HD_CACHE+0x10000 - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add edi, eax - pop eax + pop eax mov ecx, 512/4 cld - rep movsd + rep movsd pop edi esi ecx pop edx pop eax ret .notread: mov eax, IDE_descriptor_table - mov dword [eax], IDE_DMA + mov dword [eax], IDE_DMA mov word [eax+4], 0x2000 sub eax, OS_BASE mov dx, [IDEContrRegsBaseAddr] @@ -848,14 +848,14 @@ bd_read: mov esi, eax shl edi, 9 ; add edi, HD_CACHE+0x10000 - push eax - call calculate_cache_2 + push eax + call calculate_cache_2 add edi, eax - pop eax + pop eax mov ecx, 512/4 cld - rep movsd + rep movsd pop edi esi ecx pop edx pop eax @@ -892,7 +892,7 @@ bd_write_cache_chain: movzx ecx, [cache_chain_size] push ecx shl ecx, 9-2 - rep movsd + rep movsd pop ecx mov dl, 43h mov eax, [cache_chain_ptr] @@ -929,7 +929,7 @@ int13_call: mov edi, ebx mov ecx, sizeof.v86_regs/4 xor eax, eax - rep stosd + rep stosd mov byte [ebx+v86_regs.eax+1], dl mov eax, [hdpos] lea eax, [BiosDisksData+(eax-80h)*4] @@ -953,9 +953,9 @@ int13_call: mov [ebx+v86_regs.eflags], 20200h mov esi, [sys_v86_machine] mov ecx, 0x502 - push fs + push fs call v86_start - pop fs + pop fs and [bios_hdpos], 0 pop edi esi ecx ebx movzx edx, byte [OS_BASE + 512h] diff --git a/kernel/branches/Kolibri-acpi/blkdev/ide_cache.inc b/kernel/branches/Kolibri-acpi/blkdev/ide_cache.inc index 5c7414a80..522e79f02 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/ide_cache.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/ide_cache.inc @@ -29,78 +29,78 @@ write_cache: ;----------------------------------------------------------- ; write all changed sectors to disk ;----------------------------------------------------------- - push eax ecx edx esi edi + push eax ecx edx esi edi ; write difference ( 2 ) from cache to hd - call calculate_cache + call calculate_cache add esi, 8 mov edi, 1 write_cache_more: cmp dword [esi+4], 2; if cache slot is not different - jne .write_chain + jne .write_chain mov dword [esi+4], 1; same as in hd mov eax, [esi] ; eax = sector to write cmp eax, [PARTITION_START] - jb danger + jb danger cmp eax, [PARTITION_END] - ja danger + ja danger cmp [hdpos], 0x80 jae @f ; DMA write is permitted only if [allow_dma_access]=1 - cmp [allow_dma_access], 2 - jae .nodma - cmp [dma_hdd], 1 - jnz .nodma + cmp [allow_dma_access], 2 + jae .nodma + cmp [dma_hdd], 1 + jnz .nodma @@: -; ꥤ塞 楯窨 ᫥⥫ ᥪ஢ 饭 - cmp ecx, 1 - jz .nonext - cmp dword [esi+8+4], 2 - jnz .nonext - push eax - inc eax - cmp eax, [esi+8] - pop eax - jnz .nonext - cmp [cache_chain_started], 1 - jz @f - mov [cache_chain_started], 1 - mov [cache_chain_size], 0 - mov [cache_chain_pos], edi - mov [cache_chain_ptr], esi +; Объединяем запись цепочки последовательных секторов в одно обращение к диску + cmp ecx, 1 + jz .nonext + cmp dword [esi+8+4], 2 + jnz .nonext + push eax + inc eax + cmp eax, [esi+8] + pop eax + jnz .nonext + cmp [cache_chain_started], 1 + jz @f + mov [cache_chain_started], 1 + mov [cache_chain_size], 0 + mov [cache_chain_pos], edi + mov [cache_chain_ptr], esi @@: - inc [cache_chain_size] - cmp [cache_chain_size], 16 - jnz .continue - jmp .write_chain + inc [cache_chain_size] + cmp [cache_chain_size], 16 + jnz .continue + jmp .write_chain .nonext: - call flush_cache_chain - mov [cache_chain_size], 1 - mov [cache_chain_ptr], esi - call write_cache_sector - jmp .continue + call flush_cache_chain + mov [cache_chain_size], 1 + mov [cache_chain_ptr], esi + call write_cache_sector + jmp .continue .nodma: - call cache_write_pio + call cache_write_pio .write_chain: - call flush_cache_chain + call flush_cache_chain .continue: danger: add esi, 8 - inc edi - dec ecx - jnz write_cache_more - call flush_cache_chain + inc edi + dec ecx + jnz write_cache_more + call flush_cache_chain return_02: - pop edi esi edx ecx eax - ret + pop edi esi edx ecx eax + ret flush_cache_chain: - cmp [cache_chain_started], 0 - jz @f - call write_cache_chain - mov [cache_chain_started], 0 + cmp [cache_chain_started], 0 + jz @f + call write_cache_chain + mov [cache_chain_started], 0 @@: - ret + ret ;-------------------------------------------------------------------- align 4 find_empty_slot: @@ -111,33 +111,33 @@ find_empty_slot: ; push ecx esi search_again: - call calculate_cache_3 + call calculate_cache_3 shr ecx, 3 search_for_empty: - inc edi - call calculate_cache_4 - jbe inside_cache + inc edi + call calculate_cache_4 + jbe inside_cache mov edi, 1 inside_cache: - push esi - call calculate_cache_1 + push esi + call calculate_cache_1 cmp dword [edi*8+esi+4], 2 - pop esi - jb found_slot ; it's empty or read - dec ecx - jnz search_for_empty - call write_cache ; no empty slots found, write all + pop esi + jb found_slot ; it's empty or read + dec ecx + jnz search_for_empty + call write_cache ; no empty slots found, write all cmp [hd_error], 0 - jne found_slot_access_denied - jmp search_again ; and start again + jne found_slot_access_denied + jmp search_again ; and start again found_slot: - call calculate_cache_5 + call calculate_cache_5 found_slot_access_denied: - ret + ret ;-------------------------------------------------------------------- align 4 clear_hd_cache: - ret + ret ;-------------------------------------------------------------------- align 4 calculate_cache: @@ -147,77 +147,77 @@ calculate_cache: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov ecx, [cache_ide0_system_sad_size] mov esi, [cache_ide0_pointer] - ret + ret .ide0_appl_data: mov ecx, [cache_ide0_appl_sad_size] mov esi, [cache_ide0_data_pointer] - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov ecx, [cache_ide1_system_sad_size] mov esi, [cache_ide1_pointer] - ret + ret .ide1_appl_data: mov ecx, [cache_ide1_appl_sad_size] mov esi, [cache_ide1_data_pointer] - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov ecx, [cache_ide2_system_sad_size] mov esi, [cache_ide2_pointer] - ret + ret .ide2_appl_data: mov ecx, [cache_ide2_appl_sad_size] mov esi, [cache_ide2_data_pointer] - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov ecx, [cache_ide3_system_sad_size] mov esi, [cache_ide3_pointer] - ret + ret .ide3_appl_data: mov ecx, [cache_ide3_appl_sad_size] mov esi, [cache_ide3_data_pointer] - ret + ret .noide: - push eax + push eax mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data mov ecx, [cache_ide0_system_sad_size-cache_ide0+eax] mov esi, [cache_ide0_pointer-cache_ide0+eax] - pop eax - ret + pop eax + ret .bd_appl_data: mov ecx, [cache_ide0_appl_sad_size-cache_ide0+eax] mov esi, [cache_ide0_data_pointer-cache_ide0+eax] - pop eax - ret + pop eax + ret ;-------------------------------------------------------------------- align 4 calculate_cache_1: @@ -225,67 +225,67 @@ calculate_cache_1: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov esi, [cache_ide0_pointer] - ret + ret .ide0_appl_data: mov esi, [cache_ide0_data_pointer] - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov esi, [cache_ide1_pointer] - ret + ret .ide1_appl_data: mov esi, [cache_ide1_data_pointer] - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov esi, [cache_ide2_pointer] - ret + ret .ide2_appl_data: mov esi, [cache_ide2_data_pointer] - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov esi, [cache_ide3_pointer] - ret + ret .ide3_appl_data: mov esi, [cache_ide3_data_pointer] - ret + ret .noide: - push eax + push eax mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data mov esi, [cache_ide0_pointer-cache_ide0+eax] - pop eax - ret + pop eax + ret .bd_appl_data: mov esi, [cache_ide0_data_pointer-cache_ide0+eax] - pop eax - ret + pop eax + ret ;-------------------------------------------------------------------- align 4 @@ -294,64 +294,64 @@ calculate_cache_2: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov eax, [cache_ide0_system_data] - ret + ret .ide0_appl_data: mov eax, [cache_ide0_appl_data] - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov eax, [cache_ide1_system_data] - ret + ret .ide1_appl_data: mov eax, [cache_ide1_appl_data] - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov eax, [cache_ide2_system_data] - ret + ret .ide2_appl_data: mov eax, [cache_ide2_appl_data] - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov eax, [cache_ide3_system_data] - ret + ret .ide3_appl_data: mov eax, [cache_ide3_appl_data] - ret + ret .noide: mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data mov eax, [cache_ide0_system_data-cache_ide0+eax] - ret + ret .bd_appl_data: mov eax, [cache_ide0_appl_data-cache_ide0+eax] - ret + ret ;-------------------------------------------------------------------- align 4 calculate_cache_3: @@ -361,77 +361,77 @@ calculate_cache_3: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov ecx, [cache_ide0_system_sad_size] mov edi, [cache_ide0_search_start] - ret + ret .ide0_appl_data: mov ecx, [cache_ide0_appl_sad_size] mov edi, [cache_ide0_appl_search_start] - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov ecx, [cache_ide1_system_sad_size] mov edi, [cache_ide1_search_start] - ret + ret .ide1_appl_data: mov ecx, [cache_ide1_appl_sad_size] mov edi, [cache_ide1_appl_search_start] - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov ecx, [cache_ide2_system_sad_size] mov edi, [cache_ide2_search_start] - ret + ret .ide2_appl_data: mov ecx, [cache_ide2_appl_sad_size] mov edi, [cache_ide2_appl_search_start] - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov ecx, [cache_ide3_system_sad_size] mov edi, [cache_ide3_search_start] - ret + ret .ide3_appl_data: mov ecx, [cache_ide3_appl_sad_size] mov edi, [cache_ide3_appl_search_start] - ret + ret .noide: - push eax + push eax mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data mov ecx, [cache_ide0_system_sad_size-cache_ide0+eax] mov edi, [cache_ide0_search_start-cache_ide0+eax] - pop eax - ret + pop eax + ret .bd_appl_data: mov ecx, [cache_ide0_appl_sad_size-cache_ide0+eax] mov edi, [cache_ide0_appl_search_start-cache_ide0+eax] - pop eax - ret + pop eax + ret ;-------------------------------------------------------------------- align 4 calculate_cache_4: @@ -439,67 +439,67 @@ calculate_cache_4: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data cmp edi, [cache_ide0_system_sad_size] - ret + ret .ide0_appl_data: cmp edi, [cache_ide0_appl_sad_size] - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data cmp edi, [cache_ide1_system_sad_size] - ret + ret .ide1_appl_data: cmp edi, [cache_ide1_appl_sad_size] - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data cmp edi, [cache_ide2_system_sad_size] - ret + ret .ide2_appl_data: cmp edi, [cache_ide2_appl_sad_size] - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data cmp edi, [cache_ide3_system_sad_size] - ret + ret .ide3_appl_data: cmp edi, [cache_ide3_appl_sad_size] - ret + ret .noide: - push eax + push eax mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data cmp edi, [cache_ide0_system_sad_size-cache_ide0+eax] - pop eax - ret + pop eax + ret .bd_appl_data: cmp edi, [cache_ide0_appl_sad_size-cache_ide0+eax] - pop eax - ret + pop eax + ret ;-------------------------------------------------------------------- align 4 @@ -508,67 +508,67 @@ calculate_cache_5: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [hdpos], 1 - jne .ide1 + jne .ide1 cmp [hdd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov [cache_ide0_search_start], edi - ret + ret .ide0_appl_data: mov [cache_ide0_appl_search_start], edi - ret + ret .ide1: cmp [hdpos], 2 - jne .ide2 + jne .ide2 cmp [hdd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov [cache_ide1_search_start], edi - ret + ret .ide1_appl_data: mov [cache_ide1_appl_search_start], edi - ret + ret .ide2: cmp [hdpos], 3 - jne .ide3 + jne .ide3 cmp [hdd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov [cache_ide2_search_start], edi - ret + ret .ide2_appl_data: mov [cache_ide2_appl_search_start], edi - ret + ret .ide3: cmp [hdpos], 4 - jne .noide + jne .noide cmp [hdd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov [cache_ide3_search_start], edi - ret + ret .ide3_appl_data: mov [cache_ide3_appl_search_start], edi - ret + ret .noide: - push eax + push eax mov eax, [hdpos] sub eax, 80h - cmp byte [BiosDisksData+eax*4+2], -1 - jz @f + cmp byte [BiosDisksData+eax*4+2], -1 + jz @f movzx eax, byte [BiosDisksData+eax*4+2] imul eax, cache_ide1-cache_ide0 add eax, cache_ide0 - jmp .get + jmp .get @@: imul eax, cache_ide1-cache_ide0 add eax, BiosDiskCaches .get: cmp [hdd_appl_data], 0 - jne .bd_appl_data + jne .bd_appl_data mov [cache_ide0_search_start-cache_ide0+eax], edi - pop eax - ret + pop eax + ret .bd_appl_data: mov [cache_ide0_appl_search_start-cache_ide0+eax], edi - pop eax - ret + pop eax + ret ;-------------------------------------------------------------------- align 4 @@ -578,69 +578,69 @@ find_empty_slot_CD_cache: ; output : edi = cache slot ;----------------------------------------------------------- .search_again: - call cd_calculate_cache_3 + call cd_calculate_cache_3 .search_for_empty: - inc edi - call cd_calculate_cache_4 - jbe .inside_cache + inc edi + call cd_calculate_cache_4 + jbe .inside_cache mov edi, 1 .inside_cache: - call cd_calculate_cache_5 - ret + call cd_calculate_cache_5 + ret ;-------------------------------------------------------------------- clear_CD_cache: - pusha + pusha .ide0: xor eax, eax cmp [cdpos], 1 - jne .ide1 + jne .ide1 mov [cache_ide0_search_start], eax mov ecx, [cache_ide0_system_sad_size] mov edi, [cache_ide0_pointer] - call .clear + call .clear mov [cache_ide0_appl_search_start], eax mov ecx, [cache_ide0_appl_sad_size] mov edi, [cache_ide0_data_pointer] - jmp .continue + jmp .continue .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 mov [cache_ide1_search_start], eax mov ecx, [cache_ide1_system_sad_size] mov edi, [cache_ide1_pointer] - call .clear + call .clear mov [cache_ide1_appl_search_start], eax mov ecx, [cache_ide1_appl_sad_size] mov edi, [cache_ide1_data_pointer] - jmp .continue + jmp .continue .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 mov [cache_ide2_search_start], eax mov ecx, [cache_ide2_system_sad_size] mov edi, [cache_ide2_pointer] - call .clear + call .clear mov [cache_ide2_appl_search_start], eax mov ecx, [cache_ide2_appl_sad_size] mov edi, [cache_ide2_data_pointer] - jmp .continue + jmp .continue .ide3: mov [cache_ide3_search_start], eax mov ecx, [cache_ide3_system_sad_size] mov edi, [cache_ide3_pointer] - call .clear + call .clear mov [cache_ide3_appl_search_start], eax mov ecx, [cache_ide3_appl_sad_size] mov edi, [cache_ide3_data_pointer] .continue: - call .clear - popa - ret + call .clear + popa + ret .clear: shl ecx, 1 - cld - rep stosd - ret + cld + rep stosd + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache: @@ -650,50 +650,50 @@ cd_calculate_cache: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov ecx, [cache_ide0_system_sad_size] mov esi, [cache_ide0_pointer] - ret + ret .ide0_appl_data: mov ecx, [cache_ide0_appl_sad_size] mov esi, [cache_ide0_data_pointer] - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov ecx, [cache_ide1_system_sad_size] mov esi, [cache_ide1_pointer] - ret + ret .ide1_appl_data: mov ecx, [cache_ide1_appl_sad_size] mov esi, [cache_ide1_data_pointer] - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov ecx, [cache_ide2_system_sad_size] mov esi, [cache_ide2_pointer] - ret + ret .ide2_appl_data: mov ecx, [cache_ide2_appl_sad_size] mov esi, [cache_ide2_data_pointer] - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov ecx, [cache_ide3_system_sad_size] mov esi, [cache_ide3_pointer] - ret + ret .ide3_appl_data: mov ecx, [cache_ide3_appl_sad_size] mov esi, [cache_ide3_data_pointer] - ret + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache_1: @@ -701,42 +701,42 @@ cd_calculate_cache_1: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov esi, [cache_ide0_pointer] - ret + ret .ide0_appl_data: mov esi, [cache_ide0_data_pointer] - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov esi, [cache_ide1_pointer] - ret + ret .ide1_appl_data: mov esi, [cache_ide1_data_pointer] - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov esi, [cache_ide2_pointer] - ret + ret .ide2_appl_data: mov esi, [cache_ide2_data_pointer] - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov esi, [cache_ide3_pointer] - ret + ret .ide3_appl_data: mov esi, [cache_ide3_data_pointer] - ret + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache_2: @@ -744,42 +744,42 @@ cd_calculate_cache_2: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov eax, [cache_ide0_system_data] - ret + ret .ide0_appl_data: mov eax, [cache_ide0_appl_data] - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov eax, [cache_ide1_system_data] - ret + ret .ide1_appl_data: mov eax, [cache_ide1_appl_data] - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov eax, [cache_ide2_system_data] - ret + ret .ide2_appl_data: mov eax, [cache_ide2_appl_data] - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov eax, [cache_ide3_system_data] - ret + ret .ide3_appl_data: mov eax, [cache_ide3_appl_data] - ret + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache_3: @@ -789,42 +789,42 @@ cd_calculate_cache_3: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov edi, [cache_ide0_search_start] - ret + ret .ide0_appl_data: mov edi, [cache_ide0_appl_search_start] - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov edi, [cache_ide1_search_start] - ret + ret .ide1_appl_data: mov edi, [cache_ide1_appl_search_start] - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov edi, [cache_ide2_search_start] - ret + ret .ide2_appl_data: mov edi, [cache_ide2_appl_search_start] - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov edi, [cache_ide3_search_start] - ret + ret .ide3_appl_data: mov edi, [cache_ide3_appl_search_start] - ret + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache_4: @@ -832,42 +832,42 @@ cd_calculate_cache_4: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data cmp edi, [cache_ide0_system_sad_size] - ret + ret .ide0_appl_data: cmp edi, [cache_ide0_appl_sad_size] - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data cmp edi, [cache_ide1_system_sad_size] - ret + ret .ide1_appl_data: cmp edi, [cache_ide1_appl_sad_size] - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data cmp edi, [cache_ide2_system_sad_size] - ret + ret .ide2_appl_data: cmp edi, [cache_ide2_appl_sad_size] - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data cmp edi, [cache_ide3_system_sad_size] - ret + ret .ide3_appl_data: cmp edi, [cache_ide3_appl_sad_size] - ret + ret ;-------------------------------------------------------------------- align 4 cd_calculate_cache_5: @@ -875,42 +875,42 @@ cd_calculate_cache_5: ; 1 - IDE0 ... 4 - IDE3 .ide0: cmp [cdpos], 1 - jne .ide1 + jne .ide1 cmp [cd_appl_data], 0 - jne .ide0_appl_data + jne .ide0_appl_data mov [cache_ide0_search_start], edi - ret + ret .ide0_appl_data: mov [cache_ide0_appl_search_start], edi - ret + ret .ide1: cmp [cdpos], 2 - jne .ide2 + jne .ide2 cmp [cd_appl_data], 0 - jne .ide1_appl_data + jne .ide1_appl_data mov [cache_ide1_search_start], edi - ret + ret .ide1_appl_data: mov [cache_ide1_appl_search_start], edi - ret + ret .ide2: cmp [cdpos], 3 - jne .ide3 + jne .ide3 cmp [cd_appl_data], 0 - jne .ide2_appl_data + jne .ide2_appl_data mov [cache_ide2_search_start], edi - ret + ret .ide2_appl_data: mov [cache_ide2_appl_search_start], edi - ret + ret .ide3: cmp [cd_appl_data], 0 - jne .ide3_appl_data + jne .ide3_appl_data mov [cache_ide3_search_start], edi - ret + ret .ide3_appl_data: mov [cache_ide3_appl_search_start], edi - ret + ret ;-------------------------------------------------------------------- ;align 4 ;calculate_linear_to_real: diff --git a/kernel/branches/Kolibri-acpi/blkdev/rd.inc b/kernel/branches/Kolibri-acpi/blkdev/rd.inc index 614ced2a7..c7f0a1080 100644 --- a/kernel/branches/Kolibri-acpi/blkdev/rd.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/rd.inc @@ -16,7 +16,7 @@ $Revision$ calculatefatchain: - pushad + pushad mov esi, RAMDISK+512 mov edi, RAMDISK_FAT @@ -45,15 +45,15 @@ calculatefatchain: add esi, 12 cmp edi, RAMDISK_FAT+2856*2;2849 clusters - jnz fcnew + jnz fcnew - popad - ret + popad + ret restorefatchain: ; restore fat chain - pushad + pushad mov esi, RAMDISK_FAT mov edi, RAMDISK+512 @@ -73,16 +73,16 @@ restorefatchain: ; restore fat chain add esi, 8 cmp edi, RAMDISK+512+4278;4274 bytes - all used FAT - jb fcnew2 + jb fcnew2 mov esi, RAMDISK+512 ; duplicate fat chain mov edi, RAMDISK+512+0x1200 mov ecx, 1069;4274/4 - cld - rep movsd + cld + rep movsd - popad - ret + popad + ret ramdisk_free_space: @@ -92,25 +92,25 @@ ramdisk_free_space: ; rewr.by Mihasik ;--------------------------------------------- - push eax ebx ecx + push eax ebx ecx mov edi, RAMDISK_FAT;start of FAT xor ax, ax;Free cluster=0x0000 in FAT xor ebx, ebx;counter mov ecx, 2849;2849 clusters - cld + cld rdfs1: - repne scasw - jnz rdfs2 ;if last cluster not 0 - inc ebx - test ecx, ecx - jnz rdfs1 + repne scasw + jnz rdfs2 ;if last cluster not 0 + inc ebx + test ecx, ecx + jnz rdfs1 rdfs2: shl ebx, 9;free clusters*512 mov edi, ebx - pop ecx ebx eax - ret + pop ecx ebx eax + ret expand_filename: @@ -120,7 +120,7 @@ expand_filename: ; eax - pointer to filename ;--------------------------------------------- - push esi edi ebx + push esi edi ebx mov edi, esp ; check for '.' in the name add edi, 12+8 @@ -135,10 +135,10 @@ expand_filename: flr1: cmp [esi], byte '.' - jne flr2 + jne flr2 mov edi, eax add edi, 7 - jmp flr3 + jmp flr3 flr2: @@ -147,17 +147,17 @@ expand_filename: flr3: - inc esi - inc edi + inc esi + inc edi mov ebx, eax add ebx, 11 cmp edi, ebx - jbe flr1 + jbe flr1 - pop ebx edi esi - ret + pop ebx edi esi + ret fileread: ;---------------------------------------------------------------- @@ -175,35 +175,35 @@ fileread: ; ;-------------------------------------------------------------- test ebx, ebx;if ebx=0 - set to 1 - jnz frfl5 - inc ebx + jnz frfl5 + inc ebx frfl5: test ecx, ecx;if ecx=0 - set to 1 - jnz frfl6 - inc ecx + jnz frfl6 + inc ecx frfl6: test esi, esi ; return ramdisk root - jnz fr_noroot ;if not root + jnz fr_noroot ;if not root cmp ebx, 14 ;14 clusters=root dir - ja oorr + ja oorr cmp ecx, 14 - ja oorr - jmp fr_do + ja oorr + jmp fr_do oorr: mov eax, 5 ;out of root range (fnf) xor ebx, ebx - dec ebx ;0xffffffff - ret + dec ebx ;0xffffffff + ret - fr_do: ;reading rootdir + fr_do: ;reading rootdir mov edi, edx - dec ebx - push edx + dec ebx + push edx mov edx, ecx add edx, ebx cmp edx, 15 ;ebx+ecx=14+1 - pushf - jbe fr_do1 + pushf + jbe fr_do1 sub edx, 14 sub ecx, edx fr_do1: @@ -211,33 +211,33 @@ fileread: mov esi, RAMDISK+512*19 add esi, ebx shl ecx, 7 - cld - rep movsd - popf - pop edx - jae fr_do2 + cld + rep movsd + popf + pop edx + jae fr_do2 xor eax, eax; ok read xor ebx, ebx - ret - fr_do2: ;if last cluster + ret + fr_do2: ;if last cluster mov eax, 6;end of file xor ebx, ebx - ret + ret fr_noroot: sub esp, 32 - call expand_filename + call expand_filename - dec ebx + dec ebx - push eax + push eax - push eax ebx ecx edx esi edi - call rd_findfile - je fifound + push eax ebx ecx edx esi edi + call rd_findfile + je fifound add esp, 32+28 ;if file not found - ret + ret fifound: @@ -257,46 +257,46 @@ fileread: mov ecx, 512 ;[esp+4] cmp [esp+16], dword 0 ; wanted cluster ? - jne frfl7 - call memmove + jne frfl7 + call memmove add [esp+8], dword 512 - dec dword [esp+12] ; last wanted cluster ? - je frnoread - jmp frfl8 + dec dword [esp+12] ; last wanted cluster ? + je frnoread + jmp frfl8 frfl7: - dec dword [esp+16] + dec dword [esp+16] frfl8: movzx eax, word [edi*2+RAMDISK_FAT] ; find next cluster from FAT mov edi, eax cmp edi, 4095 ;eof - cluster - jz frnoread2 + jz frnoread2 cmp [esp+24], dword 512 ;eof - size - jb frnoread + jb frnoread sub [esp+24], dword 512 - jmp frnew + jmp frnew frnoread2: cmp [esp+16], dword 0 ; eof without read ? - je frnoread + je frnoread - pop edi esi edx ecx + pop edi esi edx ecx add esp, 4 - pop ebx ; ebx <- eax : size of file + pop ebx ; ebx <- eax : size of file add esp, 36 mov eax, 6 ; end of file - ret + ret frnoread: - pop edi esi edx ecx + pop edi esi edx ecx add esp, 4 - pop ebx ; ebx <- eax : size of file + pop ebx ; ebx <- eax : size of file add esp, 36 xor eax, eax;read ok - ret + ret @@ -305,21 +305,21 @@ fileread: ;IN: eax - pointer to filename OUT: filestring+11 in edi or notZero in flags and fnf in eax,ebx mov edi, RAMDISK+512*18+512;Point at directory - cld + cld rd_newsearch: mov esi, eax mov ecx, 11 - rep cmpsb - je rd_ff + rep cmpsb + je rd_ff add cl, 21 add edi, ecx cmp edi, RAMDISK+512*33 - jb rd_newsearch + jb rd_newsearch mov eax, 5 ;if file not found - eax=5 xor ebx, ebx - dec ebx ;ebx=0xffffffff and zf=0 + dec ebx ;ebx=0xffffffff and zf=0 rd_ff: - ret + ret ; \begin{diamond} @@ -327,107 +327,107 @@ uni2ansi_str: ; convert UNICODE zero-terminated string to ASCII-string (codepage 866) ; in: esi->source, edi->buffer (may be esi=edi) ; destroys: eax,esi,edi - lodsw - test ax, ax - jz .done - cmp ax, 0x80 - jb .ascii - cmp ax, 0x401 - jz .yo1 - cmp ax, 0x451 - jz .yo2 - cmp ax, 0x410 - jb .unk - cmp ax, 0x440 - jb .rus1 - cmp ax, 0x450 - jb .rus2 + lodsw + test ax, ax + jz .done + cmp ax, 0x80 + jb .ascii + cmp ax, 0x401 + jz .yo1 + cmp ax, 0x451 + jz .yo2 + cmp ax, 0x410 + jb .unk + cmp ax, 0x440 + jb .rus1 + cmp ax, 0x450 + jb .rus2 .unk: - mov al, '_' - jmp .doit + mov al, '_' + jmp .doit .yo1: - mov al, '' - jmp .doit + mov al, 0xF0 ; 'Ё' + jmp .doit .yo2: - mov al, '' - jmp .doit + mov al, 0xF1 ; 'ё' + jmp .doit .rus1: ; 0x410-0x43F -> 0x80-0xAF - add al, 0x70 - jmp .doit + add al, 0x70 + jmp .doit .rus2: ; 0x440-0x44F -> 0xE0-0xEF - add al, 0xA0 + add al, 0xA0 .ascii: .doit: - stosb - jmp uni2ansi_str + stosb + jmp uni2ansi_str .done: - mov byte [edi], 0 - ret + mov byte [edi], 0 + ret ansi2uni_char: ; convert ANSI character in al to UNICODE character in ax, using cp866 encoding - mov ah, 0 + mov ah, 0 ; 0x00-0x7F - trivial map - cmp al, 0x80 - jb .ret + cmp al, 0x80 + jb .ret ; 0x80-0xAF -> 0x410-0x43F - cmp al, 0xB0 - jae @f - add ax, 0x410-0x80 + cmp al, 0xB0 + jae @f + add ax, 0x410-0x80 .ret: - ret + ret @@: ; 0xE0-0xEF -> 0x440-0x44F - cmp al, 0xE0 - jb .unk - cmp al, 0xF0 - jae @f - add ax, 0x440-0xE0 - ret + cmp al, 0xE0 + jb .unk + cmp al, 0xF0 + jae @f + add ax, 0x440-0xE0 + ret ; 0xF0 -> 0x401 ; 0xF1 -> 0x451 @@: - cmp al, '' - jz .yo1 - cmp al, '' - jz .yo2 + cmp al, 0xF0 ; 'Ё' + jz .yo1 + cmp al, 0xF1 ; 'ё' + jz .yo2 .unk: - mov al, '_' ; ah=0 - ret + mov al, '_' ; ah=0 + ret .yo1: - mov ax, 0x401 - ret + mov ax, 0x401 + ret .yo2: - mov ax, 0x451 - ret + mov ax, 0x451 + ret char_toupper: ; convert character to uppercase, using cp866 encoding ; in: al=symbol ; out: al=converted symbol - cmp al, 'a' - jb .ret - cmp al, 'z' - jbe .az - cmp al, '' + cmp al, 'a' + jb .ret + cmp al, 'z' + jbe .az + cmp al, 0xF1 ; 'ё' jz .yo1 - cmp al, '' - jb .ret - cmp al, '' - jb .rus1 - cmp al, '' - ja .ret + cmp al, 0xA0 ; 'а' + jb .ret + cmp al, 0xE0 ; 'р' + jb .rus1 + cmp al, 0xEF ; 'я' + ja .ret ; 0xE0-0xEF -> 0x90-0x9F - sub al, ''-'' + sub al, 0xE0-0x90 .ret: - ret + ret .rus1: ; 0xA0-0xAF -> 0x80-0x8F .az: - and al, not 0x20 - ret + and al, not 0x20 + ret .yo1: ; 0xF1 -> 0xF0 dec ax @@ -440,148 +440,148 @@ fat_get_name: ; (maximum length of filename is 255 (wide) symbols without trailing 0, ; but implementation requires buffer 261 words) ; destroys eax - cmp byte [edi], 0 - jz .no - cmp byte [edi], 0xE5 - jnz @f + cmp byte [edi], 0 + jz .no + cmp byte [edi], 0xE5 + jnz @f .no: - stc - ret + stc + ret @@: - cmp byte [edi+11], 0xF - jz .longname - test byte [edi+11], 8 - jnz .no - push ecx - push edi ebp - test byte [ebp-4], 1 - jnz .unicode_short + cmp byte [edi+11], 0xF + jz .longname + test byte [edi+11], 8 + jnz .no + push ecx + push edi ebp + test byte [ebp-4], 1 + jnz .unicode_short - mov eax, [edi] - mov ecx, [edi+4] - mov [ebp], eax - mov [ebp+4], ecx + mov eax, [edi] + mov ecx, [edi+4] + mov [ebp], eax + mov [ebp+4], ecx - mov ecx, 8 + mov ecx, 8 @@: - cmp byte [ebp+ecx-1], ' ' - loope @b + cmp byte [ebp+ecx-1], ' ' + loope @b - mov eax, [edi+8] - cmp al, ' ' - je .done - shl eax, 8 - mov al, '.' + mov eax, [edi+8] + cmp al, ' ' + je .done + shl eax, 8 + mov al, '.' - lea ebp, [ebp+ecx+1] - mov [ebp], eax - mov ecx, 3 + lea ebp, [ebp+ecx+1] + mov [ebp], eax + mov ecx, 3 @@: - rol eax, 8 - cmp al, ' ' - jne .done - loop @b - dec ebp + rol eax, 8 + cmp al, ' ' + jne .done + loop @b + dec ebp .done: - and byte [ebp+ecx+1], 0 ; CF=0 - pop ebp edi ecx - ret + and byte [ebp+ecx+1], 0 ; CF=0 + pop ebp edi ecx + ret .unicode_short: - mov ecx, 8 - push ecx + mov ecx, 8 + push ecx @@: - mov al, [edi] - inc edi - call ansi2uni_char - mov [ebp], ax - inc ebp - inc ebp - loop @b - pop ecx + mov al, [edi] + inc edi + call ansi2uni_char + mov [ebp], ax + inc ebp + inc ebp + loop @b + pop ecx @@: - cmp word [ebp-2], ' ' - jnz @f - dec ebp - dec ebp - loop @b + cmp word [ebp-2], ' ' + jnz @f + dec ebp + dec ebp + loop @b @@: - mov word [ebp], '.' - inc ebp - inc ebp - mov ecx, 3 - push ecx + mov word [ebp], '.' + inc ebp + inc ebp + mov ecx, 3 + push ecx @@: - mov al, [edi] - inc edi - call ansi2uni_char - mov [ebp], ax - inc ebp - inc ebp - loop @b - pop ecx + mov al, [edi] + inc edi + call ansi2uni_char + mov [ebp], ax + inc ebp + inc ebp + loop @b + pop ecx @@: - cmp word [ebp-2], ' ' - jnz @f - dec ebp - dec ebp - loop @b - dec ebp - dec ebp + cmp word [ebp-2], ' ' + jnz @f + dec ebp + dec ebp + loop @b + dec ebp + dec ebp @@: - and word [ebp], 0 ; CF=0 - pop ebp edi ecx - ret + and word [ebp], 0 ; CF=0 + pop ebp edi ecx + ret .longname: ; LFN - mov al, byte [edi] - and eax, 0x3F - dec eax - cmp al, 20 - jae .no ; ignore invalid entries - mov word [ebp+260*2], 0 ; force null-terminating for orphans - imul eax, 13*2 - add ebp, eax - test byte [edi], 0x40 - jz @f - mov word [ebp+13*2], 0 + mov al, byte [edi] + and eax, 0x3F + dec eax + cmp al, 20 + jae .no ; ignore invalid entries + mov word [ebp+260*2], 0 ; force null-terminating for orphans + imul eax, 13*2 + add ebp, eax + test byte [edi], 0x40 + jz @f + mov word [ebp+13*2], 0 @@: - push eax + push eax ; now copy name from edi to ebp ... - mov eax, [edi+1] - mov [ebp], eax ; symbols 1,2 - mov eax, [edi+5] - mov [ebp+4], eax ; 3,4 - mov eax, [edi+9] - mov [ebp+8], ax ; 5 - mov eax, [edi+14] - mov [ebp+10], eax ; 6,7 - mov eax, [edi+18] - mov [ebp+14], eax ; 8,9 - mov eax, [edi+22] - mov [ebp+18], eax ; 10,11 - mov eax, [edi+28] - mov [ebp+22], eax ; 12,13 + mov eax, [edi+1] + mov [ebp], eax ; symbols 1,2 + mov eax, [edi+5] + mov [ebp+4], eax ; 3,4 + mov eax, [edi+9] + mov [ebp+8], ax ; 5 + mov eax, [edi+14] + mov [ebp+10], eax ; 6,7 + mov eax, [edi+18] + mov [ebp+14], eax ; 8,9 + mov eax, [edi+22] + mov [ebp+18], eax ; 10,11 + mov eax, [edi+28] + mov [ebp+22], eax ; 12,13 ; ... done - pop eax - sub ebp, eax - test eax, eax - jz @f + pop eax + sub ebp, eax + test eax, eax + jz @f ; if this is not first entry, more processing required - stc - ret + stc + ret @@: ; if this is first entry: - test byte [ebp-4], 1 - jnz .ret + test byte [ebp-4], 1 + jnz .ret ; buffer at ebp contains UNICODE name, convert it to ANSI - push esi edi - mov esi, ebp - mov edi, ebp - call uni2ansi_str - pop edi esi + push esi edi + mov esi, ebp + mov edi, ebp + call uni2ansi_str + pop edi esi .ret: - clc - ret + clc + ret fat_compare_name: ; compares ASCIIZ-names, case-insensitive (cp866 encoding) @@ -589,300 +589,300 @@ fat_compare_name: ; out: if names match: ZF=1 and esi->next component of name ; else: ZF=0, esi is not changed ; destroys eax - push ebp esi + push ebp esi .loop: - mov al, [ebp] - inc ebp - call char_toupper - push eax - lodsb - call char_toupper - cmp al, [esp] - jnz .done - pop eax - test al, al - jnz .loop - dec esi - pop eax - pop ebp - xor eax, eax ; set ZF flag - ret + mov al, [ebp] + inc ebp + call char_toupper + push eax + lodsb + call char_toupper + cmp al, [esp] + jnz .done + pop eax + test al, al + jnz .loop + dec esi + pop eax + pop ebp + xor eax, eax ; set ZF flag + ret .done: - cmp al, '/' - jnz @f - cmp byte [esp], 0 - jnz @f - mov [esp+4], esi + cmp al, '/' + jnz @f + cmp byte [esp], 0 + jnz @f + mov [esp+4], esi @@: - pop eax - pop esi ebp - ret + pop eax + pop esi ebp + ret fat_time_to_bdfe: ; in: eax=FAT time ; out: eax=BDFE time - push ecx edx - mov ecx, eax - mov edx, eax - shr eax, 11 - shl eax, 16 ; hours - and edx, 0x1F - add edx, edx - mov al, dl ; seconds - shr ecx, 5 - and ecx, 0x3F - mov ah, cl ; minutes - pop edx ecx - ret + push ecx edx + mov ecx, eax + mov edx, eax + shr eax, 11 + shl eax, 16 ; hours + and edx, 0x1F + add edx, edx + mov al, dl ; seconds + shr ecx, 5 + and ecx, 0x3F + mov ah, cl ; minutes + pop edx ecx + ret fat_date_to_bdfe: - push ecx edx - mov ecx, eax - mov edx, eax - shr eax, 9 - add ax, 1980 - shl eax, 16 ; year - and edx, 0x1F - mov al, dl ; day - shr ecx, 5 - and ecx, 0xF - mov ah, cl ; month - pop edx ecx - ret + push ecx edx + mov ecx, eax + mov edx, eax + shr eax, 9 + add ax, 1980 + shl eax, 16 ; year + and edx, 0x1F + mov al, dl ; day + shr ecx, 5 + and ecx, 0xF + mov ah, cl ; month + pop edx ecx + ret bdfe_to_fat_time: - push edx - mov edx, eax - shr eax, 16 - and dh, 0x3F - shl eax, 6 - or al, dh - shr dl, 1 - and dl, 0x1F - shl eax, 5 - or al, dl - pop edx - ret + push edx + mov edx, eax + shr eax, 16 + and dh, 0x3F + shl eax, 6 + or al, dh + shr dl, 1 + and dl, 0x1F + shl eax, 5 + or al, dl + pop edx + ret bdfe_to_fat_date: - push edx - mov edx, eax - shr eax, 16 - sub ax, 1980 - and dh, 0xF - shl eax, 4 - or al, dh - and dl, 0x1F - shl eax, 5 - or al, dl - pop edx - ret + push edx + mov edx, eax + shr eax, 16 + sub ax, 1980 + and dh, 0xF + shl eax, 4 + or al, dh + and dl, 0x1F + shl eax, 5 + or al, dl + pop edx + ret fat_entry_to_bdfe: ; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi ; destroys eax - mov eax, [ebp-4] - mov [esi+4], eax ; ASCII/UNICODE name + mov eax, [ebp-4] + mov [esi+4], eax ; ASCII/UNICODE name fat_entry_to_bdfe2: - movzx eax, byte [edi+11] - mov [esi], eax ; attributes - movzx eax, word [edi+14] - call fat_time_to_bdfe - mov [esi+8], eax ; creation time - movzx eax, word [edi+16] - call fat_date_to_bdfe - mov [esi+12], eax ; creation date - and dword [esi+16], 0 ; last access time is not supported on FAT - movzx eax, word [edi+18] - call fat_date_to_bdfe - mov [esi+20], eax ; last access date - movzx eax, word [edi+22] - call fat_time_to_bdfe - mov [esi+24], eax ; last write time - movzx eax, word [edi+24] - call fat_date_to_bdfe - mov [esi+28], eax ; last write date - mov eax, [edi+28] - mov [esi+32], eax ; file size (low dword) - xor eax, eax - mov [esi+36], eax ; file size (high dword) - test ebp, ebp - jz .ret - push ecx edi - lea edi, [esi+40] - mov esi, ebp - test byte [esi-4], 1 - jz .ansi - mov ecx, 260/2 - rep movsd - mov [edi-2], ax + movzx eax, byte [edi+11] + mov [esi], eax ; attributes + movzx eax, word [edi+14] + call fat_time_to_bdfe + mov [esi+8], eax ; creation time + movzx eax, word [edi+16] + call fat_date_to_bdfe + mov [esi+12], eax ; creation date + and dword [esi+16], 0 ; last access time is not supported on FAT + movzx eax, word [edi+18] + call fat_date_to_bdfe + mov [esi+20], eax ; last access date + movzx eax, word [edi+22] + call fat_time_to_bdfe + mov [esi+24], eax ; last write time + movzx eax, word [edi+24] + call fat_date_to_bdfe + mov [esi+28], eax ; last write date + mov eax, [edi+28] + mov [esi+32], eax ; file size (low dword) + xor eax, eax + mov [esi+36], eax ; file size (high dword) + test ebp, ebp + jz .ret + push ecx edi + lea edi, [esi+40] + mov esi, ebp + test byte [esi-4], 1 + jz .ansi + mov ecx, 260/2 + rep movsd + mov [edi-2], ax @@: - mov esi, edi - pop edi ecx + mov esi, edi + pop edi ecx .ret: - ret + ret .ansi: - mov ecx, 264/4 - rep movsd - mov [edi-1], al - jmp @b + mov ecx, 264/4 + rep movsd + mov [edi-1], al + jmp @b bdfe_to_fat_entry: ; convert BDFE at edx to FAT entry at edi ; destroys eax ; attributes byte - test byte [edi+11], 8 ; volume label? - jnz @f - mov al, [edx] - and al, 0x27 - and byte [edi+11], 0x10 - or byte [edi+11], al + test byte [edi+11], 8 ; volume label? + jnz @f + mov al, [edx] + and al, 0x27 + and byte [edi+11], 0x10 + or byte [edi+11], al @@: - mov eax, [edx+8] - call bdfe_to_fat_time - mov [edi+14], ax ; creation time - mov eax, [edx+12] - call bdfe_to_fat_date - mov [edi+16], ax ; creation date - mov eax, [edx+20] - call bdfe_to_fat_date - mov [edi+18], ax ; last access date - mov eax, [edx+24] - call bdfe_to_fat_time - mov [edi+22], ax ; last write time - mov eax, [edx+28] - call bdfe_to_fat_date - mov [edi+24], ax ; last write date - ret + mov eax, [edx+8] + call bdfe_to_fat_time + mov [edi+14], ax ; creation time + mov eax, [edx+12] + call bdfe_to_fat_date + mov [edi+16], ax ; creation date + mov eax, [edx+20] + call bdfe_to_fat_date + mov [edi+18], ax ; last access date + mov eax, [edx+24] + call bdfe_to_fat_time + mov [edi+22], ax ; last write time + mov eax, [edx+28] + call bdfe_to_fat_date + mov [edi+24], ax ; last write date + ret ramdisk_root_first: - mov edi, RAMDISK+512*19 - clc - ret + mov edi, RAMDISK+512*19 + clc + ret ramdisk_root_next: - add edi, 0x20 - cmp edi, RAMDISK+512*33 - cmc - ret + add edi, 0x20 + cmp edi, RAMDISK+512*33 + cmc + ret ramdisk_root_extend_dir: - stc - ret + stc + ret uglobal ; this is for delete support -rd_prev_sector dd ? -rd_prev_prev_sector dd ? +rd_prev_sector dd ? +rd_prev_prev_sector dd ? endg ramdisk_notroot_next: - add edi, 0x20 - test edi, 0x1FF - jz ramdisk_notroot_next_sector - ret ; CF=0 + add edi, 0x20 + test edi, 0x1FF + jz ramdisk_notroot_next_sector + ret ; CF=0 ramdisk_notroot_next_sector: - push ecx - mov ecx, [eax] - push [rd_prev_sector] - pop [rd_prev_prev_sector] - mov [rd_prev_sector], ecx - mov ecx, [ecx*2+RAMDISK_FAT] - and ecx, 0xFFF - cmp ecx, 2849 - jae ramdisk_notroot_first.err2 - mov [eax], ecx - pop ecx + push ecx + mov ecx, [eax] + push [rd_prev_sector] + pop [rd_prev_prev_sector] + mov [rd_prev_sector], ecx + mov ecx, [ecx*2+RAMDISK_FAT] + and ecx, 0xFFF + cmp ecx, 2849 + jae ramdisk_notroot_first.err2 + mov [eax], ecx + pop ecx ramdisk_notroot_first: - mov eax, [eax] - cmp eax, 2 - jb .err - cmp eax, 2849 - jae .err - shl eax, 9 - lea edi, [eax+(31 shl 9)+RAMDISK] - clc - ret + mov eax, [eax] + cmp eax, 2 + jb .err + cmp eax, 2849 + jae .err + shl eax, 9 + lea edi, [eax+(31 shl 9)+RAMDISK] + clc + ret .err2: - pop ecx + pop ecx .err: - stc - ret + stc + ret ramdisk_notroot_next_write: - test edi, 0x1FF - jz ramdisk_notroot_next_sector + test edi, 0x1FF + jz ramdisk_notroot_next_sector ramdisk_root_next_write: - ret + ret ramdisk_notroot_extend_dir: - pusha - xor eax, eax - mov edi, RAMDISK_FAT - mov ecx, 2849 - repnz scasw - jnz .notfound - mov word [edi-2], 0xFFF - sub edi, RAMDISK_FAT - shr edi, 1 - dec edi - mov eax, [esp+28] - mov ecx, [eax] - mov [RAMDISK_FAT+ecx*2], di - mov [eax], edi - shl edi, 9 - add edi, (31 shl 9)+RAMDISK - mov [esp], edi - xor eax, eax - mov ecx, 128 - rep stosd - popa - clc - ret + pusha + xor eax, eax + mov edi, RAMDISK_FAT + mov ecx, 2849 + repnz scasw + jnz .notfound + mov word [edi-2], 0xFFF + sub edi, RAMDISK_FAT + shr edi, 1 + dec edi + mov eax, [esp+28] + mov ecx, [eax] + mov [RAMDISK_FAT+ecx*2], di + mov [eax], edi + shl edi, 9 + add edi, (31 shl 9)+RAMDISK + mov [esp], edi + xor eax, eax + mov ecx, 128 + rep stosd + popa + clc + ret .notfound: - popa - stc - ret + popa + stc + ret rd_find_lfn: ; in: esi+ebp -> name ; out: CF=1 - file not found ; else CF=0 and edi->direntry - push esi edi - push 0 - push ramdisk_root_first - push ramdisk_root_next + push esi edi + push 0 + push ramdisk_root_first + push ramdisk_root_next .loop: - call fat_find_lfn - jc .notfound - cmp byte [esi], 0 - jz .found + call fat_find_lfn + jc .notfound + cmp byte [esi], 0 + jz .found .continue: - test byte [edi+11], 10h - jz .notfound - movzx eax, word [edi+26] - mov [esp+8], eax - mov dword [esp+4], ramdisk_notroot_first - mov dword [esp], ramdisk_notroot_next - test eax, eax - jnz .loop - mov dword [esp+4], ramdisk_root_first - mov dword [esp], ramdisk_notroot_next - jmp .loop + test byte [edi+11], 10h + jz .notfound + movzx eax, word [edi+26] + mov [esp+8], eax + mov dword [esp+4], ramdisk_notroot_first + mov dword [esp], ramdisk_notroot_next + test eax, eax + jnz .loop + mov dword [esp+4], ramdisk_root_first + mov dword [esp], ramdisk_notroot_next + jmp .loop .notfound: - add esp, 12 - pop edi esi - stc - ret + add esp, 12 + pop edi esi + stc + ret .found: - test ebp, ebp - jz @f - mov esi, ebp - xor ebp, ebp - jmp .continue + test ebp, ebp + jz @f + mov esi, ebp + xor ebp, ebp + jmp .continue @@: - mov eax, [esp+8] - add esp, 16 ; CF=0 - pop esi - ret + mov eax, [esp+8] + add esp, 16 ; CF=0 + pop esi + ret ;---------------------------------------------------------------- ; @@ -899,81 +899,81 @@ rd_find_lfn: ; ;-------------------------------------------------------------- fs_RamdiskRead: - cmp byte [esi], 0 - jnz @f - or ebx, -1 - mov eax, 10 ; access denied - ret + cmp byte [esi], 0 + jnz @f + or ebx, -1 + mov eax, 10 ; access denied + ret @@: - push edi - call rd_find_lfn - jnc .found - pop edi - or ebx, -1 - mov eax, 5 ; file not found - ret + push edi + call rd_find_lfn + jnc .found + pop edi + or ebx, -1 + mov eax, 5 ; file not found + ret .found: - test ebx, ebx - jz .l1 - cmp dword [ebx+4], 0 - jz @f - xor ebx, ebx + test ebx, ebx + jz .l1 + cmp dword [ebx+4], 0 + jz @f + xor ebx, ebx .reteof: - mov eax, 6 ; EOF - pop edi - ret + mov eax, 6 ; EOF + pop edi + ret @@: - mov ebx, [ebx] + mov ebx, [ebx] .l1: - push ecx edx - push 0 - mov eax, [edi+28] - sub eax, ebx - jb .eof - cmp eax, ecx - jae @f - mov ecx, eax - mov byte [esp], 6 ; EOF + push ecx edx + push 0 + mov eax, [edi+28] + sub eax, ebx + jb .eof + cmp eax, ecx + jae @f + mov ecx, eax + mov byte [esp], 6 ; EOF @@: - movzx edi, word [edi+26] ; cluster + movzx edi, word [edi+26] ; cluster .new: - jecxz .done - test edi, edi - jz .eof - cmp edi, 0xFF8 - jae .eof - lea eax, [edi+31] ; bootsector+2*fat+filenames - shl eax, 9 ; *512 - add eax, RAMDISK ; image base + jecxz .done + test edi, edi + jz .eof + cmp edi, 0xFF8 + jae .eof + lea eax, [edi+31] ; bootsector+2*fat+filenames + shl eax, 9 ; *512 + add eax, RAMDISK ; image base ; now eax points to data of cluster - sub ebx, 512 - jae .skip - lea eax, [eax+ebx+512] - neg ebx - push ecx - cmp ecx, ebx - jbe @f - mov ecx, ebx + sub ebx, 512 + jae .skip + lea eax, [eax+ebx+512] + neg ebx + push ecx + cmp ecx, ebx + jbe @f + mov ecx, ebx @@: - mov ebx, edx - call memmove - add edx, ecx - sub [esp], ecx - pop ecx - xor ebx, ebx + mov ebx, edx + call memmove + add edx, ecx + sub [esp], ecx + pop ecx + xor ebx, ebx .skip: - movzx edi, word [edi*2+RAMDISK_FAT] ; find next cluster from FAT - jmp .new + movzx edi, word [edi*2+RAMDISK_FAT] ; find next cluster from FAT + jmp .new .eof: - mov ebx, edx - pop eax edx ecx - sub ebx, edx - jmp .reteof + mov ebx, edx + pop eax edx ecx + sub ebx, edx + jmp .reteof .done: - mov ebx, edx - pop eax edx ecx edi - sub ebx, edx - ret + mov ebx, edx + pop eax edx ecx edi + sub ebx, edx + ret ;---------------------------------------------------------------- ; @@ -991,351 +991,351 @@ fs_RamdiskRead: ; ;-------------------------------------------------------------- fs_RamdiskReadFolder: - push edi - cmp byte [esi], 0 - jz .root - call rd_find_lfn - jnc .found - pop edi - or ebx, -1 - mov eax, ERROR_FILE_NOT_FOUND - ret + push edi + cmp byte [esi], 0 + jz .root + call rd_find_lfn + jnc .found + pop edi + or ebx, -1 + mov eax, ERROR_FILE_NOT_FOUND + ret .found: - test byte [edi+11], 0x10 - jnz .found_dir - pop edi - or ebx, -1 - mov eax, ERROR_ACCESS_DENIED - ret + test byte [edi+11], 0x10 + jnz .found_dir + pop edi + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret .found_dir: - movzx eax, word [edi+26] - add eax, 31 - push 0 - jmp .doit + movzx eax, word [edi+26] + add eax, 31 + push 0 + jmp .doit .root: - mov eax, 19 - push 14 + mov eax, 19 + push 14 .doit: - push esi ecx ebp - sub esp, 262*2 ; reserve space for LFN - mov ebp, esp - push dword [ebx+4] ; for fat_get_name: read ANSI/UNICODE names - mov ebx, [ebx] + push esi ecx ebp + sub esp, 262*2 ; reserve space for LFN + mov ebp, esp + push dword [ebx+4] ; for fat_get_name: read ANSI/UNICODE names + mov ebx, [ebx] ; init header - push eax ecx - mov edi, edx - mov ecx, 32/4 - xor eax, eax - rep stosd - mov byte [edx], 1 ; version - pop ecx eax - mov esi, edi ; esi points to block of data of folder entry (BDFE) + push eax ecx + mov edi, edx + mov ecx, 32/4 + xor eax, eax + rep stosd + mov byte [edx], 1 ; version + pop ecx eax + mov esi, edi ; esi points to block of data of folder entry (BDFE) .main_loop: - mov edi, eax - shl edi, 9 - add edi, RAMDISK - push eax + mov edi, eax + shl edi, 9 + add edi, RAMDISK + push eax .l1: - call fat_get_name - jc .l2 - cmp byte [edi+11], 0xF - jnz .do_bdfe - add edi, 0x20 - test edi, 0x1FF - jnz .do_bdfe - pop eax - inc eax - dec byte [esp+262*2+16] - jz .done - jns @f + call fat_get_name + jc .l2 + cmp byte [edi+11], 0xF + jnz .do_bdfe + add edi, 0x20 + test edi, 0x1FF + jnz .do_bdfe + pop eax + inc eax + dec byte [esp+262*2+16] + jz .done + jns @f ; read next sector from FAT - mov eax, [(eax-31-1)*2+RAMDISK_FAT] - and eax, 0xFFF - cmp eax, 0xFF8 - jae .done - add eax, 31 - mov byte [esp+262*2+16], 0 + mov eax, [(eax-31-1)*2+RAMDISK_FAT] + and eax, 0xFFF + cmp eax, 0xFF8 + jae .done + add eax, 31 + mov byte [esp+262*2+16], 0 @@: - mov edi, eax - shl edi, 9 - add edi, RAMDISK - push eax + mov edi, eax + shl edi, 9 + add edi, RAMDISK + push eax .do_bdfe: - inc dword [edx+8] ; new file found - dec ebx - jns .l2 - dec ecx - js .l2 - inc dword [edx+4] ; new file block copied - call fat_entry_to_bdfe + inc dword [edx+8] ; new file found + dec ebx + jns .l2 + dec ecx + js .l2 + inc dword [edx+4] ; new file block copied + call fat_entry_to_bdfe .l2: - add edi, 0x20 - test edi, 0x1FF - jnz .l1 - pop eax - inc eax - dec byte [esp+262*2+16] - jz .done - jns @f + add edi, 0x20 + test edi, 0x1FF + jnz .l1 + pop eax + inc eax + dec byte [esp+262*2+16] + jz .done + jns @f ; read next sector from FAT - mov eax, [(eax-31-1)*2+RAMDISK_FAT] - and eax, 0xFFF - cmp eax, 0xFF8 - jae .done - add eax, 31 - mov byte [esp+262*2+16], 0 + mov eax, [(eax-31-1)*2+RAMDISK_FAT] + and eax, 0xFFF + cmp eax, 0xFF8 + jae .done + add eax, 31 + mov byte [esp+262*2+16], 0 @@: - jmp .main_loop + jmp .main_loop .done: - add esp, 262*2+4 - pop ebp - mov ebx, [edx+4] - xor eax, eax - dec ecx - js @f - mov al, ERROR_END_OF_FILE + add esp, 262*2+4 + pop ebp + mov ebx, [edx+4] + xor eax, eax + dec ecx + js @f + mov al, ERROR_END_OF_FILE @@: - pop ecx esi edi edi - ret + pop ecx esi edi edi + ret iglobal label fat_legal_chars byte ; 0 = not allowed ; 1 = allowed only in long names ; 3 = allowed - times 32 db 0 + times 32 db 0 ; ! " # $ % & ' ( ) * + , - . / - db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0 + db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0 ; 0 1 2 3 4 5 6 7 8 9 : ; < = > ? - db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0 + db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0 ; @ A B C D E F G H I J K L M N O - db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; P Q R S T U V W X Y Z [ \ ] ^ _ - db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3 + db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3 ; ` a b c d e f g h i j k l m n o - db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; p q r s t u v w x y z { | } ~ - db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0 + db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0 endg fat_name_is_legal: ; in: esi->(long) name ; out: CF set <=> legal ; destroys eax - push esi - xor eax, eax + push esi + xor eax, eax @@: - lodsb - test al, al - jz .done - cmp al, 80h - jae .big - test [fat_legal_chars+eax], 1 - jnz @b + lodsb + test al, al + jz .done + cmp al, 80h + jae .big + test [fat_legal_chars+eax], 1 + jnz @b .err: - pop esi - clc - ret + pop esi + clc + ret .big: ; 0x80-0xAF, 0xE0-0xEF - cmp al, 0xB0 - jb @b - cmp al, 0xE0 - jb .err - cmp al, 0xF0 - jb @b - jmp .err + cmp al, 0xB0 + jb @b + cmp al, 0xE0 + jb .err + cmp al, 0xF0 + jb @b + jmp .err .done: - sub esi, [esp] - cmp esi, 257 - pop esi - ret + sub esi, [esp] + cmp esi, 257 + pop esi + ret fat_next_short_name: ; in: edi->8+3 name ; out: name corrected ; CF=1 <=> error - pushad - mov ecx, 8 - mov al, '~' - std - push edi - add edi, 7 - repnz scasb - pop edi - cld - jz .tilde + pushad + mov ecx, 8 + mov al, '~' + std + push edi + add edi, 7 + repnz scasb + pop edi + cld + jz .tilde ; tilde is not found, insert "~1" at end - add edi, 6 - cmp word [edi], ' ' - jnz .insert_tilde + add edi, 6 + cmp word [edi], ' ' + jnz .insert_tilde @@: dec edi - cmp byte [edi], ' ' - jz @b - inc edi + cmp byte [edi], ' ' + jz @b + inc edi .insert_tilde: - mov word [edi], '~1' - popad - clc - ret + mov word [edi], '~1' + popad + clc + ret .tilde: - push edi - add edi, 7 - xor ecx, ecx + push edi + add edi, 7 + xor ecx, ecx @@: ; after tilde may be only digits and trailing spaces - cmp byte [edi], '~' - jz .break - cmp byte [edi], ' ' - jz .space - cmp byte [edi], '9' - jnz .found - dec edi - jmp @b + cmp byte [edi], '~' + jz .break + cmp byte [edi], ' ' + jz .space + cmp byte [edi], '9' + jnz .found + dec edi + jmp @b .space: - dec edi - inc ecx - jmp @b + dec edi + inc ecx + jmp @b .found: - inc byte [edi] - add dword [esp], 8 - jmp .zerorest + inc byte [edi] + add dword [esp], 8 + jmp .zerorest .break: - jecxz .noplace - inc edi - mov al, '1' + jecxz .noplace + inc edi + mov al, '1' @@: - xchg al, [edi] - inc edi - cmp al, ' ' - mov al, '0' - jnz @b + xchg al, [edi] + inc edi + cmp al, ' ' + mov al, '0' + jnz @b .succ: - pop edi - popad - clc - ret + pop edi + popad + clc + ret .noplace: - dec edi - cmp edi, [esp] - jz .err - add dword [esp], 8 - mov word [edi], '~1' - inc edi - inc edi + dec edi + cmp edi, [esp] + jz .err + add dword [esp], 8 + mov word [edi], '~1' + inc edi + inc edi @@: - mov byte [edi], '0' + mov byte [edi], '0' .zerorest: - inc edi - cmp edi, [esp] - jb @b - pop edi - popad - ;clc ; automatically - ret + inc edi + cmp edi, [esp] + jb @b + pop edi + popad + ;clc ; automatically + ret .err: - pop edi - popad - stc - ret + pop edi + popad + stc + ret fat_gen_short_name: ; in: esi->long name ; edi->buffer (8+3=11 chars) ; out: buffer filled - pushad - mov eax, ' ' - push edi - stosd - stosd - stosd - pop edi - xor eax, eax - push 8 - pop ebx - lea ecx, [edi+8] + pushad + mov eax, ' ' + push edi + stosd + stosd + stosd + pop edi + xor eax, eax + push 8 + pop ebx + lea ecx, [edi+8] .loop: - lodsb - test al, al - jz .done - call char_toupper - cmp al, ' ' - jz .space - cmp al, 80h - ja .big - test [fat_legal_chars+eax], 2 - jnz .symbol + lodsb + test al, al + jz .done + call char_toupper + cmp al, ' ' + jz .space + cmp al, 80h + ja .big + test [fat_legal_chars+eax], 2 + jnz .symbol .inv_symbol: - mov al, '_' - or bh, 1 + mov al, '_' + or bh, 1 .symbol: - cmp al, '.' - jz .dot + cmp al, '.' + jz .dot .normal_symbol: - dec bl - jns .store - mov bl, 0 + dec bl + jns .store + mov bl, 0 .space: - or bh, 1 - jmp .loop + or bh, 1 + jmp .loop .store: - stosb - jmp .loop + stosb + jmp .loop .big: - cmp al, 0xB0 - jb .normal_symbol - cmp al, 0xE0 - jb .inv_symbol - cmp al, 0xF0 - jb .normal_symbol - jmp .inv_symbol + cmp al, 0xB0 + jb .normal_symbol + cmp al, 0xE0 + jb .inv_symbol + cmp al, 0xF0 + jb .normal_symbol + jmp .inv_symbol .dot: - test bh, 2 - jz .firstdot - pop ebx - add ebx, edi - sub ebx, ecx - push ebx - cmp ebx, ecx - jb @f - pop ebx - push ecx + test bh, 2 + jz .firstdot + pop ebx + add ebx, edi + sub ebx, ecx + push ebx + cmp ebx, ecx + jb @f + pop ebx + push ecx @@: - cmp edi, ecx - jbe .skip + cmp edi, ecx + jbe .skip @@: - dec edi - mov al, [edi] - dec ebx - mov [ebx], al - mov byte [edi], ' ' - cmp edi, ecx - ja @b + dec edi + mov al, [edi] + dec ebx + mov [ebx], al + mov byte [edi], ' ' + cmp edi, ecx + ja @b .skip: - mov bh, 3 - jmp @f + mov bh, 3 + jmp @f .firstdot: - cmp bl, 8 - jz .space - push edi - or bh, 2 + cmp bl, 8 + jz .space + push edi + or bh, 2 @@: - mov edi, ecx - mov bl, 3 - jmp .loop + mov edi, ecx + mov bl, 3 + jmp .loop .done: - test bh, 2 - jz @f - pop edi + test bh, 2 + jz @f + pop edi @@: - lea edi, [ecx-8] - test bh, 1 - jz @f - call fat_next_short_name + lea edi, [ecx-8] + test bh, 1 + jz @f + call fat_next_short_name @@: - popad - ret + popad + ret ;---------------------------------------------------------------- ; @@ -1352,444 +1352,470 @@ fat_gen_short_name: ; ;-------------------------------------------------------------- @@: - mov eax, ERROR_ACCESS_DENIED - xor ebx, ebx - ret + mov eax, ERROR_ACCESS_DENIED + xor ebx, ebx + ret fs_RamdiskCreateFolder: - mov al, 1 ; create folder - jmp fs_RamdiskRewrite.common + mov al, 1 ; create folder + jmp fs_RamdiskRewrite.common fs_RamdiskRewrite: - xor eax, eax ; create file + xor eax, eax ; create file .common: - cmp byte [esi], 0 - jz @b - pushad - xor edi, edi - push esi - test ebp, ebp - jz @f - mov esi, ebp + cmp byte [esi], 0 + jz @b + pushad + xor edi, edi + push esi + test ebp, ebp + jz @f + mov esi, ebp @@: - lodsb - test al, al - jz @f - cmp al, '/' - jnz @b - lea edi, [esi-1] - jmp @b + lodsb + test al, al + jz @f + cmp al, '/' + jnz @b + lea edi, [esi-1] + jmp @b @@: - pop esi - test edi, edi - jnz .noroot - test ebp, ebp - jnz .hasebp - push ramdisk_root_extend_dir - push ramdisk_root_next_write - push edi - push ramdisk_root_first - push ramdisk_root_next - jmp .common1 + pop esi + test edi, edi + jnz .noroot + test ebp, ebp + jnz .hasebp + push ramdisk_root_extend_dir + push ramdisk_root_next_write + push edi + push ramdisk_root_first + push ramdisk_root_next + jmp .common1 .hasebp: - mov eax, ERROR_ACCESS_DENIED - cmp byte [ebp], 0 - jz .ret1 - push ebp - xor ebp, ebp - call rd_find_lfn - pop esi - jc .notfound0 - jmp .common0 + mov eax, ERROR_ACCESS_DENIED + cmp byte [ebp], 0 + jz .ret1 + push ebp + xor ebp, ebp + call rd_find_lfn + pop esi + jc .notfound0 + jmp .common0 .noroot: - mov eax, ERROR_ACCESS_DENIED - cmp byte [edi+1], 0 - jz .ret1 + mov eax, ERROR_ACCESS_DENIED + cmp byte [edi+1], 0 + jz .ret1 ; check existence - mov byte [edi], 0 - push edi - call rd_find_lfn - pop esi - mov byte [esi], '/' - jnc @f + mov byte [edi], 0 + push edi + call rd_find_lfn + pop esi + mov byte [esi], '/' + jnc @f .notfound0: - mov eax, ERROR_FILE_NOT_FOUND + mov eax, ERROR_FILE_NOT_FOUND .ret1: - mov [esp+28], eax - popad - xor ebx, ebx - ret + mov [esp+28], eax + popad + xor ebx, ebx + ret @@: - inc esi + inc esi .common0: - test byte [edi+11], 0x10 ; must be directory - mov eax, ERROR_ACCESS_DENIED - jz .ret1 - movzx ebp, word [edi+26] ; ebp=cluster - mov eax, ERROR_FAT_TABLE - cmp ebp, 2 - jb .ret1 - cmp ebp, 2849 - jae .ret1 - push ramdisk_notroot_extend_dir - push ramdisk_notroot_next_write - push ebp - push ramdisk_notroot_first - push ramdisk_notroot_next + test byte [edi+11], 0x10 ; must be directory + mov eax, ERROR_ACCESS_DENIED + jz .ret1 + movzx ebp, word [edi+26] ; ebp=cluster + mov eax, ERROR_FAT_TABLE + cmp ebp, 2 + jb .ret1 + cmp ebp, 2849 + jae .ret1 + push ramdisk_notroot_extend_dir + push ramdisk_notroot_next_write + push ebp + push ramdisk_notroot_first + push ramdisk_notroot_next .common1: - call fat_find_lfn - jc .notfound + call fat_find_lfn + jc .notfound ; found - test byte [edi+11], 10h - jz .exists_file + test byte [edi+11], 10h + jz .exists_file ; found directory; if we are creating directory, return OK, ; if we are creating file, say "access denied" - add esp, 20 - popad - test al, al - mov eax, ERROR_ACCESS_DENIED - jz @f - mov al, 0 + add esp, 20 + popad + test al, al + mov eax, ERROR_ACCESS_DENIED + jz @f + mov al, 0 @@: - xor ebx, ebx - ret + xor ebx, ebx + ret .exists_file: ; found file; if we are creating directory, return "access denied", ; if we are creating file, delete existing file and continue - cmp byte [esp+20+28], 0 - jz @f - add esp, 20 - popad - mov eax, ERROR_ACCESS_DENIED - xor ebx, ebx - ret + cmp byte [esp+20+28], 0 + jz @f + add esp, 20 + popad + mov eax, ERROR_ACCESS_DENIED + xor ebx, ebx + ret @@: ; delete FAT chain - push edi - xor eax, eax - mov dword [edi+28], eax ; zero size - xchg ax, word [edi+26] ; start cluster - test eax, eax - jz .done1 + push edi + xor eax, eax + mov dword [edi+28], eax ; zero size + xchg ax, word [edi+26] ; start cluster + test eax, eax + jz .done1 @@: - cmp eax, 0xFF8 - jae .done1 - lea edi, [RAMDISK_FAT + eax*2] ; position in FAT - xor eax, eax - xchg ax, [edi] - jmp @b + cmp eax, 0xFF8 + jae .done1 + lea edi, [RAMDISK_FAT + eax*2] ; position in FAT + xor eax, eax + xchg ax, [edi] + jmp @b .done1: - pop edi - call get_time_for_file - mov [edi+22], ax - call get_date_for_file - mov [edi+24], ax - mov [edi+18], ax - or byte [edi+11], 20h ; set 'archive' attribute - jmp .doit + pop edi + call get_time_for_file + mov [edi+22], ax + call get_date_for_file + mov [edi+24], ax + mov [edi+18], ax + or byte [edi+11], 20h ; set 'archive' attribute + jmp .doit .notfound: ; file is not found; generate short name - call fat_name_is_legal - jc @f - add esp, 20 - popad - mov eax, ERROR_FILE_NOT_FOUND - xor ebx, ebx - ret + call fat_name_is_legal + jc @f + add esp, 20 + popad + mov eax, ERROR_FILE_NOT_FOUND + xor ebx, ebx + ret @@: - sub esp, 12 - mov edi, esp - call fat_gen_short_name + sub esp, 12 + mov edi, esp + call fat_gen_short_name .test_short_name_loop: - push esi edi ecx - mov esi, edi - lea eax, [esp+12+12+8] - mov [eax], ebp - call dword [eax-4] - jc .found + push esi edi ecx + mov esi, edi + lea eax, [esp+12+12+8] + mov [eax], ebp + call dword [eax-4] + jc .found .test_short_name_entry: - cmp byte [edi+11], 0xF - jz .test_short_name_cont - mov ecx, 11 - push esi edi - repz cmpsb - pop edi esi - jz .short_name_found + cmp byte [edi+11], 0xF + jz .test_short_name_cont + mov ecx, 11 + push esi edi + repz cmpsb + pop edi esi + jz .short_name_found .test_short_name_cont: - lea eax, [esp+12+12+8] - call dword [eax-8] - jnc .test_short_name_entry - jmp .found + lea eax, [esp+12+12+8] + call dword [eax-8] + jnc .test_short_name_entry + jmp .found .short_name_found: - pop ecx edi esi - call fat_next_short_name - jnc .test_short_name_loop + pop ecx edi esi + call fat_next_short_name + jnc .test_short_name_loop .disk_full: - add esp, 12+20 - popad - mov eax, ERROR_DISK_FULL - xor ebx, ebx - ret + add esp, 12+20 + popad + mov eax, ERROR_DISK_FULL + xor ebx, ebx + ret .found: - pop ecx edi esi + pop ecx edi esi ; now find space in directory ; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~' - mov al, '~' - push ecx edi - mov ecx, 8 - repnz scasb - push 1 - pop eax ; 1 entry - jnz .notilde + mov al, '~' + push ecx edi + mov ecx, 8 + repnz scasb + push 1 + pop eax ; 1 entry + jnz .notilde ; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total - xor eax, eax + xor eax, eax @@: - cmp byte [esi], 0 - jz @f - inc esi - inc eax - jmp @b + cmp byte [esi], 0 + jz @f + inc esi + inc eax + jmp @b @@: - sub esi, eax - add eax, 12+13 - mov ecx, 13 - push edx - cdq - div ecx - pop edx + sub esi, eax + add eax, 12+13 + mov ecx, 13 + push edx + cdq + div ecx + pop edx .notilde: - push -1 - push -1 + push -1 + push -1 ; find successive entries in directory - xor ecx, ecx - push eax - lea eax, [esp+12+8+12+8] - mov [eax], ebp - call dword [eax-4] - pop eax + xor ecx, ecx + push eax + lea eax, [esp+12+8+12+8] + mov [eax], ebp + call dword [eax-4] + pop eax .scan_dir: - cmp byte [edi], 0 - jz .free - cmp byte [edi], 0xE5 - jz .free - xor ecx, ecx + cmp byte [edi], 0 + jz .free + cmp byte [edi], 0xE5 + jz .free + xor ecx, ecx .scan_cont: - push eax - lea eax, [esp+12+8+12+8] - call dword [eax-8] - pop eax - jnc .scan_dir - push eax - lea eax, [esp+12+8+12+8] - call dword [eax+8] ; extend directory - pop eax - jnc .scan_dir - add esp, 8+8+12+20 - popad - mov eax, ERROR_DISK_FULL - xor ebx, ebx - ret + push eax + lea eax, [esp+12+8+12+8] + call dword [eax-8] + pop eax + jnc .scan_dir + push eax + lea eax, [esp+12+8+12+8] + call dword [eax+8] ; extend directory + pop eax + jnc .scan_dir + add esp, 8+8+12+20 + popad + mov eax, ERROR_DISK_FULL + xor ebx, ebx + ret .free: - test ecx, ecx - jnz @f - mov [esp], edi - mov ecx, [esp+8+8+12+8] - mov [esp+4], ecx - xor ecx, ecx + test ecx, ecx + jnz @f + mov [esp], edi + mov ecx, [esp+8+8+12+8] + mov [esp+4], ecx + xor ecx, ecx @@: - inc ecx - cmp ecx, eax - jb .scan_cont + inc ecx + cmp ecx, eax + jb .scan_cont ; found! -; calculate name checksum - push esi ecx - mov esi, [esp+8+8] - mov ecx, 11 - xor eax, eax +; 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 @@: - ror al, 1 - add al, [esi] - inc esi - loop @b - pop ecx esi - pop edi - pop dword [esp+8+12+8] + mov [esp+24+12+20+20], edi ; store the cluster somewhere +.no.preallocate.folder.data: +; calculate name checksum + mov esi, [esp+8+8] + mov ecx, 11 + xor eax, eax +@@: + ror al, 1 + add al, [esi] + inc esi + loop @b + pop ecx esi + pop edi + pop dword [esp+8+12+8] ; edi points to last entry in free chunk - dec ecx - jz .nolfn - push esi - push eax - mov al, 40h + dec ecx + jz .nolfn + push esi + push eax + mov al, 40h .writelfn: - or al, cl - mov esi, [esp+4] - push ecx - dec ecx - imul ecx, 13 - add esi, ecx - stosb - mov cl, 5 - call .read_symbols - mov ax, 0xF - stosw - mov al, [esp+4] - stosb - mov cl, 6 - call .read_symbols - xor eax, eax - stosw - mov cl, 2 - call .read_symbols - pop ecx - lea eax, [esp+8+8+12+8] - call dword [eax+4] ; next write - xor eax, eax - loop .writelfn - pop eax - pop esi + or al, cl + mov esi, [esp+4] + push ecx + dec ecx + imul ecx, 13 + add esi, ecx + stosb + mov cl, 5 + call .read_symbols + mov ax, 0xF + stosw + mov al, [esp+4] + stosb + mov cl, 6 + call .read_symbols + xor eax, eax + stosw + mov cl, 2 + call .read_symbols + pop ecx + lea eax, [esp+8+8+12+8] + call dword [eax+4] ; next write + xor eax, eax + loop .writelfn + pop eax + pop esi .nolfn: - xchg esi, [esp] - mov ecx, 11 - rep movsb - mov word [edi], 20h ; attributes - sub edi, 11 - pop esi ecx - add esp, 12 - mov byte [edi+13], 0 ; tenths of a second at file creation time - call get_time_for_file - mov [edi+14], ax ; creation time - mov [edi+22], ax ; last write time - call get_date_for_file - mov [edi+16], ax ; creation date - mov [edi+24], ax ; last write date - mov [edi+18], ax ; last access date - and word [edi+20], 0 ; high word of cluster - and word [edi+26], 0 ; low word of cluster - to be filled - and dword [edi+28], 0 ; file size - to be filled - cmp byte [esp+20+28], 0 - jz .doit + xchg esi, [esp] + mov ecx, 11 + rep movsb + mov word [edi], 20h ; attributes + sub edi, 11 + pop esi ecx + add esp, 12 + mov byte [edi+13], 0 ; tenths of a second at file creation time + call get_time_for_file + mov [edi+14], ax ; creation time + mov [edi+22], ax ; last write time + call get_date_for_file + mov [edi+16], ax ; creation date + mov [edi+24], ax ; last write date + mov [edi+18], ax ; last access date + and word [edi+20], 0 ; high word of cluster + and word [edi+26], 0 ; low word of cluster - to be filled + and dword [edi+28], 0 ; file size - to be filled + cmp byte [esp+20+28], 0 + jz .doit ; create directory - mov byte [edi+11], 10h ; attributes: folder - mov ecx, 32*2 - mov edx, edi + 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 - push edi - add edi, 26 ; edi points to low word of cluster - push edi - jecxz .done - mov ecx, 2849 - mov edi, RAMDISK_FAT + push edx + push ecx + push edi + add edi, 26 ; edi points to low word of cluster + push edi + jecxz .done + mov ecx, 2849 + mov edi, RAMDISK_FAT .write_loop: ; allocate new cluster - xor eax, eax - repnz scasw - jnz .disk_full2 - dec edi - dec edi + xor eax, eax + repnz scasw + jnz .disk_full2 +.doit2: + dec edi + dec edi ; lea eax, [edi-(RAMDISK_FAT)] - mov eax, edi - sub eax, RAMDISK_FAT + mov eax, edi + sub eax, RAMDISK_FAT - shr eax, 1 ; eax = cluster - mov word [edi], 0xFFF ; mark as last cluster - xchg edi, [esp] - stosw - pop edi - push edi - inc ecx + shr eax, 1 ; eax = cluster + mov word [edi], 0xFFF ; mark as last cluster + xchg edi, [esp] + stosw + pop edi + push edi + inc ecx ; write data - cmp byte [esp+16+20+28], 0 - jnz .writedir - shl eax, 9 - add eax, RAMDISK+31*512 + cmp byte [esp+16+20+28], 0 + jnz .writedir + shl eax, 9 + add eax, RAMDISK+31*512 .writefile: - mov ebx, edx - xchg eax, ebx - push ecx - mov ecx, 512 - cmp dword [esp+12], ecx - jae @f - mov ecx, [esp+12] + mov ebx, edx + xchg eax, ebx + push ecx + mov ecx, 512 + cmp dword [esp+12], ecx + jae @f + mov ecx, [esp+12] @@: - call memmove - add edx, ecx - sub [esp+12], ecx - pop ecx - jnz .write_loop + call memmove + add edx, ecx + sub [esp+12], ecx + pop ecx + jnz .write_loop .done: - mov ebx, edx - pop edi edi ecx edx - sub ebx, edx - mov [edi+28], ebx - add esp, 20 - mov [esp+16], ebx - popad - xor eax, eax - ret + mov ebx, edx + pop edi edi ecx edx + sub ebx, edx + mov [edi+28], ebx + add esp, 20 + mov [esp+16], ebx + popad + xor eax, eax + ret .disk_full2: - mov ebx, edx - pop edi edi ecx edx - sub ebx, edx - mov [edi+28], ebx - add esp, 20 - mov [esp+16], ebx - popad - push ERROR_DISK_FULL - pop eax - ret + mov ebx, edx + pop edi edi ecx edx + sub ebx, edx + mov [edi+28], ebx + add esp, 20 + mov [esp+16], ebx + popad + push ERROR_DISK_FULL + pop eax + ret .writedir: - mov edi, eax - shl edi, 9 - add edi, RAMDISK+31*512 - mov esi, edx - mov ecx, 32/4 - push ecx - rep movsd - mov dword [edi-32], '. ' - mov dword [edi-32+4], ' ' - mov dword [edi-32+8], ' ' - mov byte [edi-32+11], 10h - mov word [edi-32+26], ax - mov esi, edx - pop ecx - rep movsd - mov dword [edi-32], '.. ' - mov dword [edi-32+4], ' ' - mov dword [edi-32+8], ' ' - mov byte [edi-32+11], 10h - mov eax, [esp+16+8] - mov word [edi-32+26], ax - xor eax, eax - mov ecx, (512-32*2)/4 - rep stosd - pop edi edi ecx edx - add esp, 20 - popad - xor eax, eax - xor ebx, ebx - ret + mov edi, eax + shl edi, 9 + add edi, RAMDISK+31*512 + mov esi, edx + mov ecx, 32/4 + push ecx + rep movsd + mov dword [edi-32], '. ' + mov dword [edi-32+4], ' ' + mov dword [edi-32+8], ' ' + mov byte [edi-32+11], 10h + mov word [edi-32+26], ax + mov esi, edx + pop ecx + rep movsd + mov dword [edi-32], '.. ' + mov dword [edi-32+4], ' ' + mov dword [edi-32+8], ' ' + mov byte [edi-32+11], 10h + mov eax, [esp+16+8] + mov word [edi-32+26], ax + xor eax, eax + mov ecx, (512-32*2)/4 + rep stosd + pop edi edi ecx edx + add esp, 20 + popad + xor eax, eax + xor ebx, ebx + ret .read_symbol: - or ax, -1 - test esi, esi - jz .retFFFF - lodsb - test al, al - jnz ansi2uni_char - xor eax, eax - xor esi, esi + or ax, -1 + test esi, esi + jz .retFFFF + lodsb + test al, al + jnz ansi2uni_char + xor eax, eax + xor esi, esi .retFFFF: - ret + ret .read_symbols: - call .read_symbol - stosw - loop .read_symbols - ret + call .read_symbol + stosw + loop .read_symbols + ret ;---------------------------------------------------------------- ; @@ -1806,218 +1832,218 @@ fs_RamdiskRewrite: ; ;-------------------------------------------------------------- @@: - push ERROR_ACCESS_DENIED + push ERROR_ACCESS_DENIED fs_RamdiskWrite.ret0: - pop eax - xor ebx, ebx - ret + pop eax + xor ebx, ebx + ret fs_RamdiskWrite: - cmp byte [esi], 0 - jz @b - pushad - call rd_find_lfn - jnc .found - popad - push ERROR_FILE_NOT_FOUND - jmp .ret0 + cmp byte [esi], 0 + jz @b + pushad + call rd_find_lfn + jnc .found + popad + push ERROR_FILE_NOT_FOUND + jmp .ret0 .found: ; must not be directory - test byte [edi+11], 10h - jz @f - popad - push ERROR_ACCESS_DENIED - jmp .ret0 + test byte [edi+11], 10h + jz @f + popad + push ERROR_ACCESS_DENIED + jmp .ret0 @@: ; FAT does not support files larger than 4GB - test ebx, ebx - jz .l1 - cmp dword [ebx+4], 0 - jz @f + test ebx, ebx + jz .l1 + cmp dword [ebx+4], 0 + jz @f .eof: - popad - push ERROR_END_OF_FILE - jmp .ret0 + popad + push ERROR_END_OF_FILE + jmp .ret0 @@: - mov ebx, [ebx] + mov ebx, [ebx] .l1: ; now edi points to direntry, ebx=start byte to write, ; ecx=number of bytes to write, edx=data pointer - call fat_update_datetime + call fat_update_datetime ; extend file if needed - add ecx, ebx - jc .eof ; FAT does not support files larger than 4GB - push 0 ; return value=0 - cmp ecx, [edi+28] - jbe .length_ok - cmp ecx, ebx - jz .length_ok - call ramdisk_extend_file - jnc .length_ok + add ecx, ebx + jc .eof ; FAT does not support files larger than 4GB + push 0 ; return value=0 + cmp ecx, [edi+28] + jbe .length_ok + cmp ecx, ebx + jz .length_ok + call ramdisk_extend_file + jnc .length_ok ; ramdisk_extend_file can return two error codes: FAT table error or disk full. ; First case is fatal error, in second case we may write some data - mov [esp], eax - cmp al, ERROR_DISK_FULL - jz .disk_full - pop eax - mov [esp+28], eax - popad - xor ebx, ebx - ret + mov [esp], eax + cmp al, ERROR_DISK_FULL + jz .disk_full + pop eax + mov [esp+28], eax + popad + xor ebx, ebx + ret .disk_full: ; correct number of bytes to write - mov ecx, [edi+28] - cmp ecx, ebx - ja .length_ok + mov ecx, [edi+28] + cmp ecx, ebx + ja .length_ok .ret: - pop eax - mov [esp+28], eax ; eax=return value - sub edx, [esp+20] - mov [esp+16], edx ; ebx=number of written bytes - popad - ret + pop eax + mov [esp+28], eax ; eax=return value + sub edx, [esp+20] + mov [esp+16], edx ; ebx=number of written bytes + popad + ret .length_ok: ; now ebx=start pos, ecx=end pos, both lie inside file - sub ecx, ebx - jz .ret - movzx edi, word [edi+26] ; starting cluster + sub ecx, ebx + jz .ret + movzx edi, word [edi+26] ; starting cluster .write_loop: - sub ebx, 0x200 - jae .next_cluster - push ecx - neg ebx - cmp ecx, ebx - jbe @f - mov ecx, ebx + sub ebx, 0x200 + jae .next_cluster + push ecx + neg ebx + cmp ecx, ebx + jbe @f + mov ecx, ebx @@: - mov eax, edi - shl eax, 9 - add eax, RAMDISK+31*512+0x200 - sub eax, ebx - mov ebx, eax - mov eax, edx - call memmove - xor ebx, ebx - add edx, ecx - sub [esp], ecx - pop ecx - jz .ret + mov eax, edi + shl eax, 9 + add eax, RAMDISK+31*512+0x200 + sub eax, ebx + mov ebx, eax + mov eax, edx + call memmove + xor ebx, ebx + add edx, ecx + sub [esp], ecx + pop ecx + jz .ret .next_cluster: - movzx edi, word [edi*2+RAMDISK_FAT] - jmp .write_loop + movzx edi, word [edi*2+RAMDISK_FAT] + jmp .write_loop ramdisk_extend_file.zero_size: - xor eax, eax - jmp ramdisk_extend_file.start_extend + xor eax, eax + jmp ramdisk_extend_file.start_extend ; extends file on ramdisk to given size, new data area is filled by 0 ; in: edi->direntry, ecx=new size ; out: CF=0 => OK, eax=0 ; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL) ramdisk_extend_file: - push ecx + push ecx ; find the last cluster of file - movzx eax, word [edi+26] ; first cluster - mov ecx, [edi+28] - jecxz .zero_size + movzx eax, word [edi+26] ; first cluster + mov ecx, [edi+28] + jecxz .zero_size @@: - sub ecx, 0x200 - jbe @f - mov eax, [eax*2+RAMDISK_FAT] - and eax, 0xFFF - jz .fat_err - cmp eax, 0xFF8 - jb @b + sub ecx, 0x200 + jbe @f + mov eax, [eax*2+RAMDISK_FAT] + and eax, 0xFFF + jz .fat_err + cmp eax, 0xFF8 + jb @b .fat_err: - pop ecx - push ERROR_FAT_TABLE - pop eax - stc - ret + pop ecx + push ERROR_FAT_TABLE + pop eax + stc + ret @@: - push eax - mov eax, [eax*2+RAMDISK_FAT] - and eax, 0xFFF - cmp eax, 0xFF8 - pop eax - jb .fat_err + push eax + mov eax, [eax*2+RAMDISK_FAT] + and eax, 0xFFF + cmp eax, 0xFF8 + pop eax + jb .fat_err ; set length to full number of sectors and make sure that last sector is zero-padded - sub [edi+28], ecx - push eax edi - mov edi, eax - shl edi, 9 - lea edi, [edi+RAMDISK+31*512+0x200+ecx] - neg ecx - xor eax, eax - rep stosb - pop edi eax + sub [edi+28], ecx + push eax edi + mov edi, eax + shl edi, 9 + lea edi, [edi+RAMDISK+31*512+0x200+ecx] + neg ecx + xor eax, eax + rep stosb + pop edi eax .start_extend: - pop ecx + pop ecx ; now do extend - push edx esi - mov esi, RAMDISK_FAT+2*2 ; start scan from cluster 2 - mov edx, 2847 ; number of clusters to scan + push edx esi + mov esi, RAMDISK_FAT+2*2 ; start scan from cluster 2 + mov edx, 2847 ; number of clusters to scan .extend_loop: - cmp [edi+28], ecx - jae .extend_done + cmp [edi+28], ecx + jae .extend_done ; add new sector - push ecx - mov ecx, edx - push edi - mov edi, esi - jecxz .disk_full - push eax - xor eax, eax - repnz scasw - pop eax - jnz .disk_full - mov word [edi-2], 0xFFF - mov esi, edi - mov edx, ecx - sub edi, RAMDISK_FAT - shr edi, 1 - dec edi ; now edi=new cluster - test eax, eax - jz .first_cluster - mov [RAMDISK_FAT+eax*2], di - jmp @f + push ecx + mov ecx, edx + push edi + mov edi, esi + jecxz .disk_full + push eax + xor eax, eax + repnz scasw + pop eax + jnz .disk_full + mov word [edi-2], 0xFFF + mov esi, edi + mov edx, ecx + sub edi, RAMDISK_FAT + shr edi, 1 + dec edi ; now edi=new cluster + test eax, eax + jz .first_cluster + mov [RAMDISK_FAT+eax*2], di + jmp @f .first_cluster: - pop eax ; eax->direntry - push eax - mov [eax+26], di + pop eax ; eax->direntry + push eax + mov [eax+26], di @@: - push edi - shl edi, 9 - add edi, RAMDISK+31*512 - xor eax, eax - mov ecx, 512/4 - rep stosd - pop eax ; eax=new cluster - pop edi ; edi->direntry - pop ecx ; ecx=required size - add dword [edi+28], 0x200 - jmp .extend_loop + push edi + shl edi, 9 + add edi, RAMDISK+31*512 + xor eax, eax + mov ecx, 512/4 + rep stosd + pop eax ; eax=new cluster + pop edi ; edi->direntry + pop ecx ; ecx=required size + add dword [edi+28], 0x200 + jmp .extend_loop .extend_done: - mov [edi+28], ecx - pop esi edx - xor eax, eax ; CF=0 - ret + mov [edi+28], ecx + pop esi edx + xor eax, eax ; CF=0 + ret .disk_full: - pop edi ecx - pop esi edx - stc - push ERROR_DISK_FULL - pop eax - ret + pop edi ecx + pop esi edx + stc + push ERROR_DISK_FULL + pop eax + ret fat_update_datetime: - call get_time_for_file - mov [edi+22], ax ; last write time - call get_date_for_file - mov [edi+24], ax ; last write date - mov [edi+18], ax ; last access date - ret + call get_time_for_file + mov [edi+22], ax ; last write time + call get_date_for_file + mov [edi+24], ax ; last write date + mov [edi+18], ax ; last access date + ret ;---------------------------------------------------------------- ; @@ -2032,140 +2058,140 @@ fat_update_datetime: ; ;-------------------------------------------------------------- fs_RamdiskSetFileEnd: - cmp byte [esi], 0 - jnz @f + cmp byte [esi], 0 + jnz @f .access_denied: - push ERROR_ACCESS_DENIED - jmp .ret + push ERROR_ACCESS_DENIED + jmp .ret @@: - push edi - call rd_find_lfn - jnc @f - pop edi - push ERROR_FILE_NOT_FOUND + push edi + call rd_find_lfn + jnc @f + pop edi + push ERROR_FILE_NOT_FOUND .ret: - pop eax - ret + pop eax + ret @@: ; must not be directory - test byte [edi+11], 10h - jz @f - pop edi - jmp .access_denied + test byte [edi+11], 10h + jz @f + pop edi + jmp .access_denied @@: ; file size must not exceed 4Gb - cmp dword [ebx+4], 0 - jz @f - pop edi - push ERROR_END_OF_FILE - jmp .ret + cmp dword [ebx+4], 0 + jz @f + pop edi + push ERROR_END_OF_FILE + jmp .ret @@: ; set file modification date/time to current - call fat_update_datetime - mov eax, [ebx] - cmp eax, [edi+28] - jb .truncate - ja .expand - pop edi - xor eax, eax - ret + call fat_update_datetime + mov eax, [ebx] + cmp eax, [edi+28] + jb .truncate + ja .expand + pop edi + xor eax, eax + ret .expand: - push ecx - mov ecx, eax - call ramdisk_extend_file - pop ecx - pop edi - ret + push ecx + mov ecx, eax + call ramdisk_extend_file + pop ecx + pop edi + ret .truncate: - mov [edi+28], eax - push ecx - movzx ecx, word [edi+26] - test eax, eax - jz .zero_size + mov [edi+28], eax + push ecx + movzx ecx, word [edi+26] + test eax, eax + jz .zero_size ; find new last sector @@: - sub eax, 0x200 - jbe @f - movzx ecx, word [RAMDISK_FAT+ecx*2] - jmp @b + sub eax, 0x200 + jbe @f + movzx ecx, word [RAMDISK_FAT+ecx*2] + jmp @b @@: ; zero data at the end of last sector - push ecx - mov edi, ecx - shl edi, 9 - lea edi, [edi+RAMDISK+31*512+eax+0x200] - mov ecx, eax - neg ecx - xor eax, eax - rep stosb - pop ecx + push ecx + mov edi, ecx + shl edi, 9 + lea edi, [edi+RAMDISK+31*512+eax+0x200] + mov ecx, eax + neg ecx + xor eax, eax + rep stosb + pop ecx ; terminate FAT chain - lea ecx, [RAMDISK_FAT+ecx+ecx] - push dword [ecx] - mov word [ecx], 0xFFF - pop ecx - and ecx, 0xFFF - jmp .delete + lea ecx, [RAMDISK_FAT+ecx+ecx] + push dword [ecx] + mov word [ecx], 0xFFF + pop ecx + and ecx, 0xFFF + jmp .delete .zero_size: - and word [edi+26], 0 + and word [edi+26], 0 .delete: ; delete FAT chain starting with ecx ; mark all clusters as free - cmp ecx, 0xFF8 - jae .deleted - lea ecx, [RAMDISK_FAT+ecx+ecx] - push dword [ecx] - and word [ecx], 0 - pop ecx - and ecx, 0xFFF - jmp .delete + cmp ecx, 0xFF8 + jae .deleted + lea ecx, [RAMDISK_FAT+ecx+ecx] + push dword [ecx] + and word [ecx], 0 + pop ecx + and ecx, 0xFFF + jmp .delete .deleted: - pop ecx - pop edi - xor eax, eax - ret + pop ecx + pop edi + xor eax, eax + ret fs_RamdiskGetFileInfo: - cmp byte [esi], 0 - jnz @f - mov eax, 2 ; unsupported - ret + cmp byte [esi], 0 + jnz @f + mov eax, 2 ; unsupported + ret @@: - push edi - call rd_find_lfn + push edi + call rd_find_lfn fs_GetFileInfo_finish: - jnc @f - pop edi - mov eax, ERROR_FILE_NOT_FOUND - ret + jnc @f + pop edi + mov eax, ERROR_FILE_NOT_FOUND + ret @@: - push esi ebp - xor ebp, ebp - mov esi, edx - and dword [esi+4], 0 - call fat_entry_to_bdfe2 - pop ebp esi - pop edi - xor eax, eax - ret + push esi ebp + xor ebp, ebp + mov esi, edx + and dword [esi+4], 0 + call fat_entry_to_bdfe2 + pop ebp esi + pop edi + xor eax, eax + ret fs_RamdiskSetFileInfo: - cmp byte [esi], 0 - jnz @f - mov eax, 2 ; unsupported - ret + cmp byte [esi], 0 + jnz @f + mov eax, 2 ; unsupported + ret @@: - push edi - call rd_find_lfn - jnc @f - pop edi - mov eax, ERROR_FILE_NOT_FOUND - ret + push edi + call rd_find_lfn + jnc @f + pop edi + mov eax, ERROR_FILE_NOT_FOUND + ret @@: - call bdfe_to_fat_entry - pop edi - xor eax, eax - ret + call bdfe_to_fat_entry + pop edi + xor eax, eax + ret ;---------------------------------------------------------------- ; @@ -2177,99 +2203,99 @@ fs_RamdiskSetFileInfo: ; ;-------------------------------------------------------------- fs_RamdiskDelete: - cmp byte [esi], 0 - jnz @f + cmp byte [esi], 0 + jnz @f ; cannot delete root! .access_denied: - push ERROR_ACCESS_DENIED + push ERROR_ACCESS_DENIED .pop_ret: - pop eax - ret + pop eax + ret @@: - and [rd_prev_sector], 0 - and [rd_prev_prev_sector], 0 - push edi - call rd_find_lfn - jnc .found - pop edi - push ERROR_FILE_NOT_FOUND - jmp .pop_ret + and [rd_prev_sector], 0 + and [rd_prev_prev_sector], 0 + push edi + call rd_find_lfn + jnc .found + pop edi + push ERROR_FILE_NOT_FOUND + jmp .pop_ret .found: - cmp dword [edi], '. ' - jz .access_denied2 - cmp dword [edi], '.. ' - jz .access_denied2 - test byte [edi+11], 10h - jz .dodel + cmp dword [edi], '. ' + jz .access_denied2 + cmp dword [edi], '.. ' + jz .access_denied2 + test byte [edi+11], 10h + jz .dodel ; we can delete only empty folders! - movzx eax, word [edi+26] - push ebx - mov ebx, eax - shl ebx, 9 - add ebx, RAMDISK + 31*0x200 + 2*0x20 + movzx eax, word [edi+26] + push ebx + mov ebx, eax + shl ebx, 9 + add ebx, RAMDISK + 31*0x200 + 2*0x20 .checkempty: - cmp byte [ebx], 0 - jz .empty - cmp byte [ebx], 0xE5 - jnz .notempty - add ebx, 0x20 - test ebx, 0x1FF - jnz .checkempty - movzx eax, word [RAMDISK_FAT + eax*2] - test eax, eax - jz .empty - mov ebx, eax - shl ebx, 9 - add ebx, RAMDISK + 31*0x200 - jmp .checkempty + cmp byte [ebx], 0 + jz .empty + cmp byte [ebx], 0xE5 + jnz .notempty + add ebx, 0x20 + test ebx, 0x1FF + jnz .checkempty + movzx eax, word [RAMDISK_FAT + eax*2] + test eax, eax + jz .empty + mov ebx, eax + shl ebx, 9 + add ebx, RAMDISK + 31*0x200 + jmp .checkempty .notempty: - pop ebx + pop ebx .access_denied2: - pop edi - jmp .access_denied + pop edi + jmp .access_denied .empty: - pop ebx + pop ebx .dodel: - movzx eax, word [edi+26] + movzx eax, word [edi+26] ; delete folder entry - mov byte [edi], 0xE5 + mov byte [edi], 0xE5 ; delete LFN (if present) .lfndel: - test edi, 0x1FF - jnz @f - cmp [rd_prev_sector], 0 - jz @f - cmp [rd_prev_sector], -1 - jz .lfndone - mov edi, [rd_prev_sector] - push [rd_prev_prev_sector] - pop [rd_prev_sector] - or [rd_prev_prev_sector], -1 - shl edi, 9 - add edi, RAMDISK + 31*0x200 + 0x200 + test edi, 0x1FF + jnz @f + cmp [rd_prev_sector], 0 + jz @f + cmp [rd_prev_sector], -1 + jz .lfndone + mov edi, [rd_prev_sector] + push [rd_prev_prev_sector] + pop [rd_prev_sector] + or [rd_prev_prev_sector], -1 + shl edi, 9 + add edi, RAMDISK + 31*0x200 + 0x200 @@: - sub edi, 0x20 - cmp byte [edi], 0xE5 - jz .lfndone - cmp byte [edi+11], 0xF - jnz .lfndone - mov byte [edi], 0xE5 - jmp .lfndel + sub edi, 0x20 + cmp byte [edi], 0xE5 + jz .lfndone + cmp byte [edi+11], 0xF + jnz .lfndone + mov byte [edi], 0xE5 + jmp .lfndel .lfndone: ; delete FAT chain cmp eax, 2 jb .done cmp eax, 0xFF8 jae .done - lea eax, [RAMDISK_FAT + eax*2] - push dword [eax] - and word [eax], 0 - pop eax - and eax, 0xFFF - jmp .lfndone + lea eax, [RAMDISK_FAT + eax*2] + push dword [eax] + and word [eax], 0 + pop eax + and eax, 0xFFF + jmp .lfndone .done: - pop edi - xor eax, eax - ret + pop edi + xor eax, eax + ret ; \end{diamond} diff --git a/kernel/branches/Kolibri-acpi/boot/bootcode.inc b/kernel/branches/Kolibri-acpi/boot/bootcode.inc index 5971156b1..bbfbcb122 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootcode.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootcode.inc @@ -428,7 +428,7 @@ sayerr: .nopci: ; \end{Mario79} - mov al, 0xf6 ; , + mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование out 0x60, al xor cx, cx wait_loop: ; variant 2 @@ -847,21 +847,21 @@ end if xor dx, dx div bx if lang eq ru -; 5 ᥪ㭤, 4/3/2 ᥪ㭤, 1 ᥪ㭤 +; подождите 5 секунд, 4/3/2 секунды, 1 секунду cmp al, 5 mov cl, ' ' jae @f cmp al, 1 - mov cl, '' + mov cl, 0xE3 ; 'у' in cp866 jz @f - mov cl, '' + mov cl, 0xEB ; 'ы' in cp866 @@: mov [time_str+9], cl else if lang eq et cmp al, 1 ja @f - mov [time_str+9], ' ' - mov [time_str+10], ' ' + mov byte [time_str+9], ' ' + mov byte [time_str+10], ' ' @@: else if lang eq sp ; esperar 5/4/3/2 segundos, 1 segundo diff --git a/kernel/branches/Kolibri-acpi/boot/booten.inc b/kernel/branches/Kolibri-acpi/boot/booten.inc index 9ccc12f56..53d15a4e6 100644 --- a/kernel/branches/Kolibri-acpi/boot/booten.inc +++ b/kernel/branches/Kolibri-acpi/boot/booten.inc @@ -89,11 +89,11 @@ save_quest db "Remember current settings? [y/n]: ",0 loader_block_error db "Bootloader data invalid, I cannot continue. Stopped.",0 end if -_st db 186,' Ŀ',13,10,0 -_r1 db 186,' 320x200 EGA/CGA 256 colors ',13,10,0 -_r2 db 186,' 640x480 VGA 16 colors ',13,10,0 -_rs db 186,' ????x????@?? SVGA VESA ',13,10,0 -_bt db 186,' ',13,10,0 +_st:latin1 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1:latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0 +_r2:latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0 +_rs:latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt:latin1 '║ └───────────────────────────────┴─┘',13,10,0 remark1 db "Default values were selected to match most of configurations, but not all.",0 remark2 db "If the system does not boot, try to disable the item [b].",0 diff --git a/kernel/branches/Kolibri-acpi/boot/bootet.inc b/kernel/branches/Kolibri-acpi/boot/bootet.inc index bfe4c4fed..919a2b23d 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootet.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootet.inc @@ -15,87 +15,87 @@ $Revision$ d80x25_bottom: - db 186,' KolibriOS pohineb MenuetOS ja kaasas IGASUGUSE GARANTI' - db 'ITA ',186 - db 186,' Naha faili COPYING detailid ' - db ' ',186 + latin1 '║ KolibriOS pohineb MenuetOS ja kaasas IGASUGUSE GARANTI' + latin1 'ITA ║' + latin1 '║ Naha faili COPYING detailid ' + latin1 ' ║' line_full_bottom d80x25_bottom_num = 3 -msg_apm db " APM x.x ", 0 -novesa db "Ekraan: EGA/CGA",13,10,0 -s_vesa db "Vesa versioon: " +msg_apm: latin1 " APM x.x ", 0 +novesa: latin1 "Ekraan: EGA/CGA",13,10,0 +s_vesa: latin1 "Vesa versioon: " .ver db "?.?",13,10,0 -gr_mode db "Vali videomode: ",13,10,0 +gr_mode: latin1 "Vali videomode: ",13,10,0 -ask_bd db "Lisa kettad nahtavaks BIOS reziim V86? [1-jah, 2-no]: ",0 +ask_bd: latin1 "Lisa kettad nahtavaks BIOS reziim V86? [1-jah, 2-no]: ",0 if defined extended_primary_loader -bdev db "Paigalda mluketas [1-diskett; 2-kolibri.img]: ",0 +bdev: latin1 "Paigalda mäluketas [1-diskett; 2-kolibri.img]: ",0 else -bdev db "Paigalda mluketas [1-diskett; 2-C:\kolibri.img (FAT32);" - db 13,10,186," " - db "3-kasuta eellaaditud mluketast kerneli restardist;" - db 13,10,186," " - db "4-loo thi 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 ebannestus.",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 jtkamiseks",13,10,0 -time_msg db " vi oota " -time_str db " 5 sekundit" - db " automaatseks jtkamiseks",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 vrvi",0 -mode9 db "640x480, VGA 16 vrvi",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 " vljas",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 thi 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 "Jta meelde praegused seaded? [y/n]: ",0 -loader_block_error db "Alglaaduri andmed vigased, ei saa jtkata. 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 diff --git a/kernel/branches/Kolibri-acpi/boot/bootge.inc b/kernel/branches/Kolibri-acpi/boot/bootge.inc index c13b05420..dd242f829 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootge.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootge.inc @@ -22,7 +22,7 @@ d80x25_bottom: line_full_bottom d80x25_bottom_num = 3 -msg_apm db " APM x.x ", 0 +msg_apm db " APM x.x ", 0 novesa db "Anzeige: EGA/CGA ",13,10,0 s_vesa db "Vesa-Version: " .ver db "?.?",13,10,0 @@ -89,11 +89,11 @@ save_quest db "Aktuelle Einstellungen speichern? [y/n]: ",0 loader_block_error db "Bootloader Daten ungueltig, Kann nicht fortfahren. Angehalten.",0 end if -_st db 186,' Ŀ',13,10,0 -_r1 db 186,' 320x200 EGA/CGA 256 colors ',13,10,0 -_r2 db 186,' 640x480 VGA 16 colors ',13,10,0 -_rs db 186,' ????x????@?? SVGA VESA ',13,10,0 -_bt db 186,' ',13,10,0 +_st:latin1 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1:latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0 +_r2:latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0 +_rs:latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt:latin1 '║ └───────────────────────────────┴─┘',13,10,0 remark1 db "Die Standardwerte sind fur die meisten gewahlt, aber nicht fur jedermann.",0 remark2 db "Wenn das System nicht bootet, versuchen, das Element [b] deaktivieren.",0 diff --git a/kernel/branches/Kolibri-acpi/boot/bootru.inc b/kernel/branches/Kolibri-acpi/boot/bootru.inc index ffa5241ea..ab73c1376 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootru.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootru.inc @@ -15,87 +15,83 @@ $Revision$ 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: " - .ver db "?.?",13,10,0 +msg_apm: cp866 " APM x.x ", 0 +novesa: cp866 "Видеокарта: EGA/CGA",13,10,0 +s_vesa: cp866 "Версия VESA: " + .ver db "?.?",13,10,0 -gr_mode db "롥 ०: ",13,10,0 +gr_mode: cp866 "Выберите видеорежим: ",13,10,0 -ask_bd db " ᪨, १ BIOS ० V86? [1-, 2-]: ",0 +ask_bd: cp866 "Добавить диски, видимые через BIOS в режиме V86? [1-да, 2-нет]: ",0 if defined extended_primary_loader -bdev db "㧨 ࠧ [1-᪥; 2-kolibri.img 㧪]: ",0 +bdev: cp866 "Загрузить образ из [1-дискета; 2-kolibri.img из папки загрузки]: ",0 else -bdev db "㧨 ࠧ [1-᪥; 2-C:\kolibri.img (FAT32);" - db 13,10,186," " - db "3-ᯮ짮 㦥 㦥 ࠧ;" - db 13,10,186," " - db "4-ᮧ ࠧ]: ",0 +bdev: cp866 "Загрузить образ из [1-дискета; 2-C:\kolibri.img (FAT32);",13,10 + cp866 "║ 3-использовать уже загруженный образ;",13,10 + cp866 "║ 4-создать чистый образ]: ",0 end if -prnotfnd db "訡 - ० .",0 +prnotfnd: cp866 "Ошибка - Видеорежим не найден.",0 -not386 db "訡 - ॡ 386+.",0 -fatalsel db "訡 - ࠭ ० ন.",0 -pres_key db " , 室 롮 ०.",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 -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 +not386: cp866 "Ошибка - Требуется процессор 386+.",0 +fatalsel: cp866 "Ошибка - Выбранный видеорежим не поддерживается.",0 +pres_key: cp866 "Нажимите любую клавишу, для перехода в выбор режимов.",0 +badsect: cp866 13,10,"║ Ошибка - Дискета повреждена. Попробуйте другую.",0 +memmovefailed:cp866 13,10,"║ Ошибка - Int 0x15 move failed.",0 +okt: cp866 " ... OK" +linef: cp866 13,10,0 +diskload: cp866 "Загрузка дискеты: 00 %",8,8,8,8,0 +pros: cp866 "00" +backspace2:cp866 8,8,0 +boot_dev db 0 +start_msg:cp866 "Нажмите [abcd] для изменения настроек, [Enter] для продолжения загрузки",13,10,0 +time_msg: cp866 " или подождите " +time_str: cp866 " 5 секунд " + cp866 " до автоматического продолжения",13,10,0 +current_cfg_msg:cp866 "Текущие настройки:",13,10,0 +curvideo_msg:cp866 " [a] Видеорежим: ",0 -mode0 db "320x200, EGA/CGA 256 梥⮢",13,10,0 -mode9 db "640x480, VGA 16 梥⮢",13,10,0 +mode0: cp866 "320x200, EGA/CGA 256 цветов",13,10,0 +mode9: cp866 "640x480, VGA 16 цветов",13,10,0 -usebd_msg db " [b] ᪨, १ BIOS:",0 -on_msg db " ",13,10,0 -off_msg db " 몫",13,10,0 +usebd_msg:cp866 " [b] Добавить диски, видимые через BIOS:",0 +on_msg: cp866 " вкл",13,10,0 +off_msg: cp866 " выкл",13,10,0 -preboot_device_msg db " [c] ࠧ ᪥: ",0 +preboot_device_msg:cp866 " [c] Образ дискеты: ",0 if defined extended_primary_loader preboot_device_msgs dw 0,pdm1,pdm2,0 -pdm1 db " ᪥",13,10,0 -pdm2 db "kolibri.img 㧪",13,10,0 +pdm1: cp866 "настоящая дискета",13,10,0 +pdm2: cp866 "kolibri.img из папки загрузки",13,10,0 else preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4 -pdm1 db " ᪥",13,10,0 -pdm2 db "C:\kolibri.img (FAT32)",13,10,0 -pdm3 db "ᯮ짮 㦥 㦥 ࠧ",13,10,0 -pdm4 db "ᮧ ࠧ",13,10,0 +pdm1: cp866 "настоящая дискета",13,10,0 +pdm2: cp866 "C:\kolibri.img (FAT32)",13,10,0 +pdm3: cp866 "использовать уже загруженный образ",13,10,0 +pdm4: cp866 "создать чистый образ",13,10,0 end if -loading_msg db " 㧪 KolibriOS...",0 +loading_msg:cp866 "Идёт загрузка KolibriOS...",0 if ~ defined extended_primary_loader ; saving not supported in this case -save_quest db " ⥪騥 ன? [y/n]: ",0 -loader_block_error db "訡 砫쭮 稪, த .",0 +save_quest:cp866 "Запомнить текущие настройки? [y/n]: ",0 +loader_block_error:cp866 "Ошибка в данных начального загрузчика, продолжение невозможно.",0 end if -_st db 186,' Ŀ ',13,10,0 -_r1 db 186,' 320x200 EGA/CGA 256 梥⮢ ',13,10,0 -_r2 db 186,' 640x480 VGA 16 梥⮢ ',13,10,0 -_rs db 186,' ????x????@?? SVGA VESA ',13,10,0 -_bt db 186,' ',13,10,0 +_st:cp866 '║ ┌───────────────────────────────┬─┐ ',13,10,0 +_r1:cp866 '║ │ 320x200 EGA/CGA 256 цветов │ │ ',13,10,0 +_r2:cp866 '║ │ 640x480 VGA 16 цветов │ │ ',13,10,0 +_rs:cp866 '║ │ ????x????@?? SVGA VESA │ │ ',13,10,0 +_bt:cp866 '║ └───────────────────────────────┴─┘ ',13,10,0 -remark1 db "祭 㬮砭 ࠭ 㤮⢠ 設⢠, .",0 -remark2 db "᫨ 㧨 ⥬, ஡ ⪫ 㭪 [b].",0 +remark1:cp866 "Значения по умолчанию выбраны для удобства большинства, но не всех.",0 +remark2:cp866 "Если у Вас не грузится система, попробуйте отключить пункт [b].",0 remarks dw remark1, remark2 num_remarks = 2 diff --git a/kernel/branches/Kolibri-acpi/boot/bootsp.inc b/kernel/branches/Kolibri-acpi/boot/bootsp.inc index 4ec2c0327..8e9b8344c 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootsp.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootsp.inc @@ -11,93 +11,93 @@ ; ;====================================================================== -; Para modificar ste archivo es necesario abrirlo con codificacin 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 GARANTA ',186 - db 186,' Lee el archivo COPYING por ms 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 "Versin 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 ncleo;" - db 13,10,186," " - db "4-crear imagen vaca]: ",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 grficos 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 configuracin, [Enter] para continuar",13,10,0 -time_msg db " o espera " -time_str db " 5 segundos" - db " para que inicie automticamente",13,10,0 -current_cfg_msg db "Configuracin 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 vaca",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 configuracin actual? [s/n]: ",0 -loader_block_error db "Bootloader invlido, 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 opcin [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 diff --git a/kernel/branches/Kolibri-acpi/boot/bootvesa.inc b/kernel/branches/Kolibri-acpi/boot/bootvesa.inc index bf328d2c5..f6cef78ed 100644 --- a/kernel/branches/Kolibri-acpi/boot/bootvesa.inc +++ b/kernel/branches/Kolibri-acpi/boot/bootvesa.inc @@ -78,7 +78,7 @@ virtual at $A000 mi VBE_ModeInfo modes_table: end virtual -cursor_pos dw 0 ; . +cursor_pos dw 0 ;временное хранение курсора. home_cursor dw 0 ;current shows rows a table end_cursor dw 0 ;end of position current shows rows a table scroll_start dw 0 ;start position of scroll bar @@ -189,7 +189,7 @@ calc_vmodes_table: lfs si, [es:vi.VideoModePtr] mov bx, modes_table -;save no vesa mode of work 320x200, EGA/CGA 256 梥⮢ and 640x480, VGA 16 梥⮢ +;save no vesa mode of work 320x200, EGA/CGA 256 梥⮢ and 640x480, VGA 16 梥⮢ mov word [es:bx], 640 mov word [es:bx+2], 480 mov word [es:bx+6], 0x13 diff --git a/kernel/branches/Kolibri-acpi/boot/et.inc b/kernel/branches/Kolibri-acpi/boot/et.inc index adb2f7c69..c7a8fc501 100644 --- a/kernel/branches/Kolibri-acpi/boot/et.inc +++ b/kernel/branches/Kolibri-acpi/boot/et.inc @@ -9,7 +9,7 @@ $Revision$ ; Full ASCII code font -; only and added +; only õ and ä added ; Kaitz ET_FNT: fontfile file "ETFONT.FNT" diff --git a/kernel/branches/Kolibri-acpi/boot/ru.inc b/kernel/branches/Kolibri-acpi/boot/ru.inc index 29acfa144..77f7ab7d6 100644 --- a/kernel/branches/Kolibri-acpi/boot/ru.inc +++ b/kernel/branches/Kolibri-acpi/boot/ru.inc @@ -11,9 +11,9 @@ $Revision$ ; Generated by RUFNT.EXE ; By BadBugsKiller (C) ; Modifyed by BadBugsKiller 12.01.2004 17:45 -; 2- , -; . -; ASCII ('), 866. +; Шрифт уменьшен в размере и теперь состоит из 2-ух частей, +; содержащих только символы русского алфавита. +; символы в кодировке ASCII (ДОС'овская), кодовая страница 866. RU_FNT1: db 0x00, 0x00, 0x1E, 0x36, 0x66, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 db 0x00, 0x00, 0xFE, 0x62, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/fat.inc b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/fat.inc index 66130eabd..2399f48d3 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/fat.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/fat.inc @@ -31,322 +31,322 @@ ; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file has been loaded, bx=2 - file not found ; out: dx:ax = file size (0xFFFFFFFF if file not found) load_file_fat: - mov eax, [bp + root_clus - dat] - mov [bp + cur_obj - dat], root_string - push es - push bx - push cx + mov eax, [bp + root_clus - dat] + mov [bp + cur_obj - dat], root_string + push es + push bx + push cx .parse_dir_loop: ; convert name to FAT name - push [bp + cur_obj - dat] - push ax - mov [bp + cur_obj - dat], si - push ss - pop es + push [bp + cur_obj - dat] + push ax + mov [bp + cur_obj - dat], si + push ss + pop es ; convert ASCIIZ filename to FAT name - mov di, fat_filename - push di - mov cx, 8+3 - mov al, ' ' - rep stosb - pop di - mov cl, 8 ; 8 symbols per name - mov bl, 1 + mov di, fat_filename + push di + mov cx, 8+3 + mov al, ' ' + rep stosb + pop di + mov cl, 8 ; 8 symbols per name + mov bl, 1 .nameloop: - lodsb - test al, al - jz .namedone - cmp al, '/' - jz .namedone - cmp al, '.' - jz .namedot - dec cx - js .badname - cmp al, 'a' - jb @f - cmp al, 'z' - ja @f - sub al, 'a'-'A' + lodsb + test al, al + jz .namedone + cmp al, '/' + jz .namedone + cmp al, '.' + jz .namedot + dec cx + js .badname + cmp al, 'a' + jb @f + cmp al, 'z' + ja @f + sub al, 'a'-'A' @@: - stosb - jmp .nameloop + stosb + jmp .nameloop .namedot: - inc bx - jp .badname - add di, cx - mov cl, 3 - jmp .nameloop + inc bx + jp .badname + add di, cx + mov cl, 3 + jmp .nameloop .badname: - mov si, badname_msg - jmp find_error_si + mov si, badname_msg + jmp find_error_si .namedone: ; scan directory - pop ax ; eax = cluster of directory - ; high word of eax is preserved by operations above - push ds - push si + pop ax ; eax = cluster of directory + ; high word of eax is preserved by operations above + push ds + push si ; read a folder sector-by-sector and scan ; first, try to use the cache - push ss - pop ds - mov bx, -2 - mov cx, [bp + rootcache_size - dat] - cmp [bp + root_clus - dat], eax - jz .lookcache_root - mov di, foldcache_mark - xor bx, bx - mov cx, [bp + cachelimit - dat] + push ss + pop ds + mov bx, -2 + mov cx, [bp + rootcache_size - dat] + cmp [bp + root_clus - dat], eax + jz .lookcache_root + mov di, foldcache_mark + xor bx, bx + mov cx, [bp + cachelimit - dat] @@: - lea si, [di+bx] - mov edx, dword [foldcache_clus+si-foldcache_mark+bx] - cmp edx, eax - jz .cacheok - test edx, edx - jz .cacheadd ; the cache has place for new entry - inc bx - inc bx - dec cx - js @b + lea si, [di+bx] + mov edx, dword [foldcache_clus+si-foldcache_mark+bx] + cmp edx, eax + jz .cacheok + test edx, edx + jz .cacheadd ; the cache has place for new entry + inc bx + inc bx + dec cx + js @b ; the folder is not present in the cache, so add it ; the cache is full; find the oldest entry and replace it with the new one - mov bx, -2 - mov dx, [bp + cachelimit - dat] + mov bx, -2 + mov dx, [bp + cachelimit - dat] @@: - inc bx - inc bx - cmp word [di+bx], dx ; marks have values 0 through [cachelimit] - jnz @b + inc bx + inc bx + cmp word [di+bx], dx ; marks have values 0 through [cachelimit] + jnz @b .cacheadd: - or word [di+bx], 0xFFFF ; very big value, it will be changed soon - and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet - lea si, [di+bx] - mov dword [foldcache_clus+si-foldcache_mark+bx], eax + or word [di+bx], 0xFFFF ; very big value, it will be changed soon + and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet + lea si, [di+bx] + mov dword [foldcache_clus+si-foldcache_mark+bx], eax .cacheok: ; update cache marks - mov dx, [di+bx] - mov cx, [foldcache_size+di-foldcache_mark+bx] - mov di, [bp + cachelimit - dat] - add di, di + mov dx, [di+bx] + mov cx, [foldcache_size+di-foldcache_mark+bx] + mov di, [bp + cachelimit - dat] + add di, di .cacheupdate: - cmp [foldcache_mark+di], dx - adc [foldcache_mark+di], 0 - dec di - dec di - jns .cacheupdate - and [foldcache_mark+bx], 0 + cmp [foldcache_mark+di], dx + adc [foldcache_mark+di], 0 + dec di + dec di + jns .cacheupdate + and [foldcache_mark+bx], 0 ; done, bx contains (position in cache)*2 .lookcache_root: ; bx = (position in cache)*2 for non-root folders; bx = -2 for root folder - ;mov dx, bx - ;shl dx, 8 - ;add dx, 0x9200 - lea dx, [bx + 0x92] - xchg dl, dh - mov ds, dx - mov si, fat_filename ; ss:si -> filename in FAT style - call fat_scan_for_filename - jz .lookup_done + ;mov dx, bx + ;shl dx, 8 + ;add dx, 0x9200 + lea dx, [bx + 0x92] + xchg dl, dh + mov ds, dx + mov si, fat_filename ; ss:si -> filename in FAT style + call fat_scan_for_filename + jz .lookup_done ; cache miss, read folder data from disk ; we are reading parent directory, it can result in disk read errors; restore [cur_obj] - mov di, sp - mov bx, [bp + cur_obj - dat] - xchg bx, [ss:di+4] - mov [bp + cur_obj - dat], bx - mov bx, cx - add bx, 0xF - shr bx, 4 - shl cx, 5 - mov di, cx ; es:di -> free space in cache entry + mov di, sp + mov bx, [bp + cur_obj - dat] + xchg bx, [ss:di+4] + mov [bp + cur_obj - dat], bx + mov bx, cx + add bx, 0xF + shr bx, 4 + shl cx, 5 + mov di, cx ; es:di -> free space in cache entry ; external loop: scan clusters .folder_next_cluster: ; internal loop: scan sectors in cluster - movzx ecx, byte [ss:0x320D] ; BPB_SecPerClus - push eax + movzx ecx, byte [ss:0x320D] ; BPB_SecPerClus + push eax ; FAT12/16 root - special handling - test eax, eax - jnz .folder_notroot - mov cx, [ss:0x3211] ; BPB_RootEntCnt - mov dx, cx - add cx, 0xF - rcr cx, 1 - shr cx, 3 - mov eax, [bp + root_start - dat] - jmp .folder_next_sector + test eax, eax + jnz .folder_notroot + mov cx, [ss:0x3211] ; BPB_RootEntCnt + mov dx, cx + add cx, 0xF + rcr cx, 1 + shr cx, 3 + mov eax, [bp + root_start - dat] + jmp .folder_next_sector .folder_notroot: - mul ecx - add eax, [bp + data_start - dat] + mul ecx + add eax, [bp + data_start - dat] .folder_next_sector: - sub dx, 0x10 + sub dx, 0x10 ; skip first bx sectors - dec bx - jns .folder_skip_sector - push cx - push es di - push 0x8000 - pop es - xor bx, bx - mov cx, 1 - push es - call read - jc ..found_disk_error + dec bx + jns .folder_skip_sector + push cx + push es di + push 0x8000 + pop es + xor bx, bx + mov cx, 1 + push es + call read + jc ..found_disk_error ; copy data to the cache... - pop ds - pop di es - cmp di, 0x2000 ; ...if there is free space, of course - jae @f - pusha - mov cx, 0x100 - xor si, si - rep movsw - mov di, es - shr di, 8 - cmp di, 0x90 - jz .update_rootcache_size - add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache - jmp .updated_cachesize + pop ds + pop di es + cmp di, 0x2000 ; ...if there is free space, of course + jae @f + pusha + mov cx, 0x100 + xor si, si + rep movsw + mov di, es + shr di, 8 + cmp di, 0x90 + jz .update_rootcache_size + add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache + jmp .updated_cachesize .update_rootcache_size: - mov cl, 0x10 - cmp cx, dx - jb @f - mov cx, dx + mov cl, 0x10 + cmp cx, dx + jb @f + mov cx, dx @@: - add [bp + rootcache_size - dat], cx + add [bp + rootcache_size - dat], cx .updated_cachesize: - popa + popa @@: - push es - mov cl, 0x10 ; ch=0 at this point - cmp cx, dx - jb @f - mov cx, dx + push es + mov cl, 0x10 ; ch=0 at this point + cmp cx, dx + jb @f + mov cx, dx @@: - call fat_scan_for_filename - pop es - pop cx - jz .lookup_done_pop + call fat_scan_for_filename + pop es + pop cx + jz .lookup_done_pop .folder_skip_sector: - inc eax - loop .folder_next_sector - pop eax ; eax = current cluster - test eax, eax - jz @f - call [bp + get_next_cluster_ptr - dat] - jc .folder_next_cluster + inc eax + loop .folder_next_sector + pop eax ; eax = current cluster + test eax, eax + jz @f + call [bp + get_next_cluster_ptr - dat] + jc .folder_next_cluster @@: - stc - push eax + stc + push eax .lookup_done_pop: - pop eax + pop eax .lookup_done: - pop si + pop si ; CF=1 <=> failed - jnc .found - pop ds - pop [bp + cur_obj - dat] - mov si, error_not_found - jmp find_error_si + jnc .found + pop ds + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si .found: - mov eax, [di+20-2] - mov edx, [di+28] - mov ax, [di+26] ; get cluster - test byte [di+11], 10h ; directory? - pop ds - pop [bp + cur_obj - dat] ; forget old [cur_obj] - jz .regular_file - cmp byte [si-1], 0 - jnz .parse_dir_loop + mov eax, [di+20-2] + mov edx, [di+28] + mov ax, [di+26] ; get cluster + test byte [di+11], 10h ; directory? + pop ds + pop [bp + cur_obj - dat] ; forget old [cur_obj] + jz .regular_file + cmp byte [si-1], 0 + jnz .parse_dir_loop ..directory_error: - mov si, directory_string - jmp find_error_si + mov si, directory_string + jmp find_error_si .regular_file: - cmp byte [si-1], 0 - jz @f + cmp byte [si-1], 0 + jz @f ..notdir_error: - mov si, notdir_string - jmp find_error_si + mov si, notdir_string + jmp find_error_si @@: ; ok, we have found a regular file and the caller requested it ; parse FAT chunk - push ss - pop es - push ss - pop ds - mov di, 0x4005 - mov byte [di-5], 1 ; non-resident attribute - mov dword [di-4], 1 - stosd - pop cx - push cx + push ss + pop es + push ss + pop ds + mov di, 0x4005 + mov byte [di-5], 1 ; non-resident attribute + mov dword [di-4], 1 + stosd + pop cx + push cx .parsefat: - call [bp + get_next_cluster_ptr - dat] - jnc .done - mov esi, [di-8] - add esi, [di-4] - cmp eax, esi - jz .contc - mov dword [di], 1 - scasd - stosd - jmp @f + call [bp + get_next_cluster_ptr - dat] + jnc .done + mov esi, [di-8] + add esi, [di-4] + cmp eax, esi + jz .contc + mov dword [di], 1 + scasd + stosd + jmp @f .contc: - inc dword [di-8] + inc dword [di-8] @@: - sub cl, [0x320D] - sbb ch, 0 - ja .parsefat + sub cl, [0x320D] + sbb ch, 0 + ja .parsefat .done: - xor eax, eax - stosd - mov si, 0x4000 + xor eax, eax + stosd + mov si, 0x4000 load_file_common_end: - xor ecx, ecx - pop cx - pop bx - pop es - mov [bp + filesize - dat], edx - mov [bp + sectors_read - dat], ecx - add edx, 0x1FF - shr edx, 9 - mov [bp + filesize_sectors - dat], edx - cmp edx, ecx - seta al - mov ah, 0 - push ax - call read_file_chunk + xor ecx, ecx + pop cx + pop bx + pop es + mov [bp + filesize - dat], edx + mov [bp + sectors_read - dat], ecx + add edx, 0x1FF + shr edx, 9 + mov [bp + filesize_sectors - dat], edx + cmp edx, ecx + seta al + mov ah, 0 + push ax + call read_file_chunk continue_load_common_end: - mov [bp + cur_chunk_ptr - dat], si - pop bx - mov ax, word [bp + filesize - dat] - mov dx, word [bp + filesize+2 - dat] - jnc @f - mov bl, 3 ; read error + mov [bp + cur_chunk_ptr - dat], si + pop bx + mov ax, word [bp + filesize - dat] + mov dx, word [bp + filesize+2 - dat] + jnc @f + mov bl, 3 ; read error @@: - ret + ret continue_load_file: ; es:bx -> buffer for output, ecx = cx = number of sectors - mov si, [bp + cur_chunk_ptr - dat] - push ecx - add ecx, [bp + sectors_read - dat] - mov [bp + sectors_read - dat], ecx - cmp [bp + filesize_sectors - dat], ecx - pop ecx - seta al - mov ah, 0 - push ax - push continue_load_common_end - push ss - pop ds - cmp [bp + cur_chunk_resident - dat], ah - jnz .nonresident + mov si, [bp + cur_chunk_ptr - dat] + push ecx + add ecx, [bp + sectors_read - dat] + mov [bp + sectors_read - dat], ecx + cmp [bp + filesize_sectors - dat], ecx + pop ecx + seta al + mov ah, 0 + push ax + push continue_load_common_end + push ss + pop ds + cmp [bp + cur_chunk_resident - dat], ah + jnz .nonresident .resident: - mov ax, word [bp + num_sectors - dat] - jmp read_file_chunk.resident.continue + mov ax, word [bp + num_sectors - dat] + jmp read_file_chunk.resident.continue .nonresident: - mov eax, [bp + cur_cluster - dat] - mov edx, [bp + num_sectors - dat] - add eax, [bp + cur_delta - dat] - jmp read_file_chunk.nonresident.continue + mov eax, [bp + cur_cluster - dat] + mov edx, [bp + num_sectors - dat] + add eax, [bp + cur_delta - dat] + jmp read_file_chunk.nonresident.continue fat_scan_for_filename: ; in: ss:si -> 11-bytes FAT name @@ -355,52 +355,52 @@ fat_scan_for_filename: ; out: if found: CF=0, ZF=1, es:di -> directory entry ; out: if not found, but continue required: CF=1 and ZF=0 ; out: if not found and zero item reached: CF=1 and ZF=1 - push ds - pop es - xor di, di - push cx - jcxz .noent + push ds + pop es + xor di, di + push cx + jcxz .noent .loop: - cmp byte [di], 0 - jz .notfound - test byte [di+11], 8 ; volume label? - jnz .cont ; ignore volume labels - pusha - mov cx, 11 - repz cmps byte [ss:si], byte [es:di] - popa - jz .done + cmp byte [di], 0 + jz .notfound + test byte [di+11], 8 ; volume label? + jnz .cont ; ignore volume labels + pusha + mov cx, 11 + repz cmps byte [ss:si], byte [es:di] + popa + jz .done .cont: - add di, 0x20 - loop .loop + add di, 0x20 + loop .loop .noent: - inc cx ; clear ZF flag + inc cx ; clear ZF flag .notfound: - stc + stc .done: - pop cx - ret + pop cx + ret fat12_get_next_cluster: ; in: ax = cluster (high word of eax is zero) ; out: if there is next cluster: CF=1, ax = next cluster ; out: if there is no next cluster: CF=0 - push si - push ds - push 0x6000 - pop ds - mov si, ax - shr si, 1 - add si, ax - test al, 1 - lodsw - jz @f - shr ax, 4 + push si + push ds + push 0x6000 + pop ds + mov si, ax + shr si, 1 + add si, ax + test al, 1 + lodsw + jz @f + shr ax, 4 @@: - and ax, 0xFFF - cmp ax, 0xFF7 - pop ds si - ret + and ax, 0xFFF + cmp ax, 0xFF7 + pop ds si + ret fat16_get_next_cluster: ; in: ax = cluster (high word of eax is zero) @@ -408,102 +408,102 @@ fat16_get_next_cluster: ; out: if there is no next cluster: CF=0 ; each sector contains 200h bytes = 100h FAT entries ; so ah = # of sector, al = offset in sector - push si - mov si, ax - shr si, 8 + push si + mov si, ax + shr si, 8 ; calculate segment for this sector of FAT table ; base for FAT table is 6000:0000, so the sector #si has to be loaded to (60000 + 200*si) ; segment = 6000 + 20*si, offset = 0 - push es - push si - shl si, 5 - add si, 0x6000 - mov es, si - pop si - cmp byte [ss:0x3400+si], 0 ; sector already loaded? - jnz .noread + push es + push si + shl si, 5 + add si, 0x6000 + mov es, si + pop si + cmp byte [ss:0x3400+si], 0 ; sector already loaded? + jnz .noread ; load corresponding sector, try all FATs if disk read error detected - pusha - movzx di, byte [ss:0x3210] ; BPB_NumFATs - xor bx, bx - mov ax, [ss:0x320E] ; BPB_RsvdSecCnt - xor dx, dx - add ax, si - adc dx, bx + pusha + movzx di, byte [ss:0x3210] ; BPB_NumFATs + xor bx, bx + mov ax, [ss:0x320E] ; BPB_RsvdSecCnt + xor dx, dx + add ax, si + adc dx, bx @@: - push es - push dx ax - pop eax - mov cx, 1 ; read 1 sector - call read - pop es - jnc @f - add ax, [ss:0x3216] ; BPB_FATSz16 - adc dx, bx - dec di - jnz @b + push es + push dx ax + pop eax + mov cx, 1 ; read 1 sector + call read + pop es + jnc @f + add ax, [ss:0x3216] ; BPB_FATSz16 + adc dx, bx + dec di + jnz @b ..found_disk_error: - mov si, disk_error_msg - jmp find_error_si + mov si, disk_error_msg + jmp find_error_si @@: - popa + popa .noread: - mov si, ax - and si, 0xFF - add si, si - mov ax, [es:si] - pop es - cmp ax, 0xFFF7 - pop si - ret + mov si, ax + and si, 0xFF + add si, si + mov ax, [es:si] + pop es + cmp ax, 0xFFF7 + pop si + ret fat32_get_next_cluster: ; in: eax = cluster ; out: if there is next cluster: CF=1, eax = next cluster ; out: if there is no next cluster: CF=0 - push di - push ax - shr eax, 7 + push di + push ax + shr eax, 7 ; eax = FAT sector number; look in cache - push si - mov si, cache1head - call cache_lookup - pop si - jnc .noread + push si + mov si, cache1head + call cache_lookup + pop si + jnc .noread ; read FAT, try all FATs if disk read error detected - push es - pushad - movzx edx, word [ss:0x320E] ; BPB_RsvdSecCnt - add eax, edx - movzx si, byte [ss:0x3210] ; BPB_NumFATs + push es + pushad + movzx edx, word [ss:0x320E] ; BPB_RsvdSecCnt + add eax, edx + movzx si, byte [ss:0x3210] ; BPB_NumFATs @@: - lea cx, [di - 0x3400 + (0x6000 shr (9-3))] - shl cx, 9-3 - mov es, cx - xor bx, bx - mov cx, 1 - call read - jnc @f - add eax, [ss:0x3224] ; BPB_FATSz32 - dec si - jnz @b - jmp ..found_disk_error + lea cx, [di - 0x3400 + (0x6000 shr (9-3))] + shl cx, 9-3 + mov es, cx + xor bx, bx + mov cx, 1 + call read + jnc @f + add eax, [ss:0x3224] ; BPB_FATSz32 + dec si + jnz @b + jmp ..found_disk_error @@: - popad - pop es + popad + pop es .noread: ; get requested item - lea ax, [di - 0x3400 + (0x6000 shr (9-3))] - pop di - and di, 0x7F - shl di, 2 - shl ax, 9-3 - push ds - mov ds, ax - and byte [di+3], 0x0F - mov eax, [di] - pop ds - pop di - ;and eax, 0x0FFFFFFF - cmp eax, 0x0FFFFFF7 - ret + lea ax, [di - 0x3400 + (0x6000 shr (9-3))] + pop di + and di, 0x7F + shl di, 2 + shl ax, 9-3 + push ds + mov ds, ax + and byte [di+3], 0x0F + mov eax, [di] + pop ds + pop di + ;and eax, 0x0FFFFFFF + cmp eax, 0x0FFFFFF7 + ret diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/kordldr.win.txt b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/kordldr.win.txt index 703c5a447..3affd3f39 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/kordldr.win.txt +++ b/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. Добавляет сектор в конец списка (самый новый вход). diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/ntfs.inc b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/ntfs.inc index f92f13caa..2d1353e8d 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/ntfs.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/ntfs.inc @@ -27,376 +27,376 @@ restore_usa: ; Update Sequence Array restore ; in: ds:bx -> USA-protected structure - push bx - lea di, [bx+1feh] - mov cx, [bx+6] - add bx, [bx+4] - dec cx + push bx + lea di, [bx+1feh] + mov cx, [bx+6] + add bx, [bx+4] + dec cx @@: - mov ax, [bx+2] - mov [di], ax - inc bx - inc bx - add di, 200h - loop @b - pop bx - ret + mov ax, [bx+2] + mov [di], ax + inc bx + inc bx + add di, 200h + loop @b + pop bx + ret find_attr: ; in: ds:di->file record, ax=attribute ; out: ds:di->attribute or di=0 if not found - add di, [di+14h] + add di, [di+14h] .1: ; attributes' codes are formally dwords, but all of them fit in word - cmp word [di], -1 - jz .notfound - cmp word [di], ax - jnz .continue + cmp word [di], -1 + jz .notfound + cmp word [di], ax + jnz .continue ; for $DATA attribute, scan only unnamed - cmp ax, 80h - jnz .found - cmp byte [di+9], 0 - jz .found + cmp ax, 80h + jnz .found + cmp byte [di+9], 0 + jz .found .continue: - add di, [di+4] - jmp .1 + add di, [di+4] + jmp .1 .notfound: - xor di, di + xor di, di .found: - ret + ret process_mcb_nonres: ; in: ds:si->attribute, es:di->buffer ; out: es:di->buffer end - pushad - pop di - add si, [si+20h] - xor ebx, ebx + pushad + pop di + add si, [si+20h] + xor ebx, ebx .loop: - lodsb - test al, al - jz .done - push invalid_read_request_string - movzx cx, al - shr cx, 4 - jz find_error_sp - xchg ax, dx - and dx, 0Fh - jz find_error_sp - add si, cx - add si, dx - pop ax - push si - dec si - movsx eax, byte [si] - dec cx - jz .l1e + lodsb + test al, al + jz .done + push invalid_read_request_string + movzx cx, al + shr cx, 4 + jz find_error_sp + xchg ax, dx + and dx, 0Fh + jz find_error_sp + add si, cx + add si, dx + pop ax + push si + dec si + movsx eax, byte [si] + dec cx + jz .l1e .l1: - dec si - shl eax, 8 - mov al, [si] - loop .l1 + dec si + shl eax, 8 + mov al, [si] + loop .l1 .l1e: - xchg ebp, eax - dec si - movsx eax, byte [si] - mov cx, dx - dec cx - jz .l2e + xchg ebp, eax + dec si + movsx eax, byte [si] + mov cx, dx + dec cx + jz .l2e .l2: - dec si - shl eax, 8 - mov al, byte [si] - loop .l2 + dec si + shl eax, 8 + mov al, byte [si] + loop .l2 .l2e: - pop si - add ebx, ebp + pop si + add ebx, ebp ; eax=length, ebx=disk block - stosd - mov eax, ebx - stosd - cmp di, 0x8000 - 12 - jbe .loop + stosd + mov eax, ebx + stosd + cmp di, 0x8000 - 12 + jbe .loop ..attr_overflow: - mov si, fragmented_string - jmp find_error_si + mov si, fragmented_string + jmp find_error_si .done: - xor ax, ax - stosw - stosw - push di - popad - ret + xor ax, ax + stosw + stosw + push di + popad + ret load_attr: ; in: ax=attribute, ds:bx->base record ; out: if found: CF=0, attribute loaded to [freeattr], [freeattr] updated, -; edx=size of attribute in bytes +; edx=size of attribute in bytes ; out: if not found: CF=1 - mov di, [bp + freeattr - dat] - push ss - pop es - mov byte [es:di], 1 - inc di - cmp di, 0x8000 - 12 - ja ..attr_overflow - or edx, -1 ; file size is not known yet + mov di, [bp + freeattr - dat] + push ss + pop es + mov byte [es:di], 1 + inc di + cmp di, 0x8000 - 12 + ja ..attr_overflow + or edx, -1 ; file size is not known yet ; scan for attribute - push di - mov di, bx - add di, [di+14h] + push di + mov di, bx + add di, [di+14h] @@: - call find_attr.1 - test di, di - jz .notfound1 - cmp byte [di+8], 0 - jnz .nonresident - mov si, di - pop di - push ds - jmp .resident + call find_attr.1 + test di, di + jz .notfound1 + cmp byte [di+8], 0 + jnz .nonresident + mov si, di + pop di + push ds + jmp .resident .aux_resident: - mov ax, ds - mov si, di - pop di ds bx ds edx - push ss - pop es - push ds - mov ds, ax + mov ax, ds + mov si, di + pop di ds bx ds edx + push ss + pop es + push ds + mov ds, ax ; resident attribute .resident: - dec di - mov al, 0 - stosb - mov ax, [si+10h] - stosw - push di - add di, ax - cmp di, 0x8000 - 12 - pop di - ja ..attr_overflow - movzx edx, ax ; length of attribute - xchg ax, cx - add si, [si+14h] - rep movsb - mov [bp + freeattr - dat], di - pop ds - ret + dec di + mov al, 0 + stosb + mov ax, [si+10h] + stosw + push di + add di, ax + cmp di, 0x8000 - 12 + pop di + ja ..attr_overflow + movzx edx, ax ; length of attribute + xchg ax, cx + add si, [si+14h] + rep movsb + mov [bp + freeattr - dat], di + pop ds + ret .nonresident: ; nonresident attribute - cmp dword [di+10h], 0 - jnz @b + cmp dword [di+10h], 0 + jnz @b ; read start of data - mov si, di - mov edx, [di+30h] ; size of attribute - pop di - call process_mcb_nonres - sub di, 4 - push di + mov si, di + mov edx, [di+30h] ; size of attribute + pop di + call process_mcb_nonres + sub di, 4 + push di .notfound1: - pop di - push edx + pop di + push edx ; $ATTRIBUTE_LIST is always in base file record - cmp ax, 20h - jz .nofragmented + cmp ax, 20h + jz .nofragmented ; try to load $ATTRIBUTE_LIST = 20h - push ax - mov ax, 20h - push [bp + freeattr - dat] - mov [bp + freeattr - dat], di - push di - call load_attr - pop di - pop [bp + freeattr - dat] - pop ax - jc .nofragmented - push ds bx - pusha - mov si, di - push ss - pop ds - push 0x8100 - pop es - xor ecx, ecx - mov cl, 0x78 - xor bx, bx - push es - call read_file_chunk - pop ds - jc ..found_disk_error - test cx, cx - jz ..attr_overflow - popa - push ss - pop es - xor bx, bx + push ax + mov ax, 20h + push [bp + freeattr - dat] + mov [bp + freeattr - dat], di + push di + call load_attr + pop di + pop [bp + freeattr - dat] + pop ax + jc .nofragmented + push ds bx + pusha + mov si, di + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + popa + push ss + pop es + xor bx, bx .1: - cmp [bx], ax - jnz .continue1 + cmp [bx], ax + jnz .continue1 ; only unnamed $DATA attributes! - cmp ax, 80h - jnz @f - cmp byte [bx+6], 0 - jnz .continue1 + cmp ax, 80h + jnz @f + cmp byte [bx+6], 0 + jnz .continue1 @@: - cmp dword [bx+10h], 0 - jz .continue1 - cmp dword [bx+8], 0 - jnz @f - dec di - cmp di, [bp + freeattr - dat] - lea di, [di+1] - jnz .continue1 + cmp dword [bx+10h], 0 + jz .continue1 + cmp dword [bx+8], 0 + jnz @f + dec di + cmp di, [bp + freeattr - dat] + lea di, [di+1] + jnz .continue1 @@: - push ds di - push ax - mov eax, [bx+10h] - mov ecx, [bx+8] - call read_file_record - pop ax - mov di, [14h] + push ds di + push ax + mov eax, [bx+10h] + mov ecx, [bx+8] + call read_file_record + pop ax + mov di, [14h] .2: - call find_attr.1 - cmp byte [di+8], 0 - jz .aux_resident - cmp dword [di+10h], ecx - jnz .2 - mov si, di - mov di, sp - cmp dword [ss:di+8], -1 - jnz @f - push dword [si+30h] ; size of attribute - pop dword [ss:di+8] + call find_attr.1 + cmp byte [di+8], 0 + jz .aux_resident + cmp dword [di+10h], ecx + jnz .2 + mov si, di + mov di, sp + cmp dword [ss:di+8], -1 + jnz @f + push dword [si+30h] ; size of attribute + pop dword [ss:di+8] @@: - pop di - call process_mcb_nonres - sub di, 4 - pop ds + pop di + call process_mcb_nonres + sub di, 4 + pop ds .continue1: - add bx, [bx+4] - cmp bx, dx - jb .1 - pop bx ds + add bx, [bx+4] + cmp bx, dx + jb .1 + pop bx ds .nofragmented: - pop edx - dec di - cmp di, [bp + freeattr - dat] - jnz @f - stc - ret + pop edx + dec di + cmp di, [bp + freeattr - dat] + jnz @f + stc + ret @@: - inc di - xor ax, ax - stosw - stosw - mov [bp + freeattr - dat], di - ret + inc di + xor ax, ax + stosw + stosw + mov [bp + freeattr - dat], di + ret read_file_record: ; in: eax = index of record ; out: ds:0 -> record ; find place in cache - push di - push si - mov si, cache1head - call cache_lookup - pop si - pushf - sub di, 3400h - shl di, 10-3 - add di, 0x6000 - mov ds, di - popf - pop di - jnc .noread + push di + push si + mov si, cache1head + call cache_lookup + pop si + pushf + sub di, 3400h + shl di, 10-3 + add di, 0x6000 + mov ds, di + popf + pop di + jnc .noread ; read file record to ds:0 - pushad - push ds - push es - movzx ecx, [bp + frs_size - dat] - shr cx, 9 - mul ecx - push ds - pop es - push ss - pop ds - mov si, 0x4000 - xor bx, bx - push [bp + cur_obj - dat] - mov [bp + cur_obj - dat], mft_string - push es - call read_attr + pushad + push ds + push es + movzx ecx, [bp + frs_size - dat] + shr cx, 9 + mul ecx + push ds + pop es + push ss + pop ds + mov si, 0x4000 + xor bx, bx + push [bp + cur_obj - dat] + mov [bp + cur_obj - dat], mft_string + push es + call read_attr ; initialize cache for $INDEX_ALLOCATION for this record - pop si - push si - sub si, 0x6000 - mov ax, si - shr si, 10-3 - shr ax, 2 - add si, 3480h - add ax, 3500h - mov [si], si - mov [si+2], si - mov [si+4], ax - pop ds - call restore_usa - pop [bp + cur_obj - dat] - pop es - pop ds - popad + pop si + push si + sub si, 0x6000 + mov ax, si + shr si, 10-3 + shr ax, 2 + add si, 3480h + add ax, 3500h + mov [si], si + mov [si+2], si + mov [si+4], ax + pop ds + call restore_usa + pop [bp + cur_obj - dat] + pop es + pop ds + popad .noread: - ret + ret read_attr: ; in: eax = offset in sectors, ecx = size in sectors (<10000h), es:bx -> buffer, ds:si -> attribute - push invalid_read_request_string - cmp byte [si], 0 - jnz .nonresident - cmp eax, 10000h shr 9 - jae find_error_sp - shl ax, 9 - shl cx, 9 - cmp ax, [si+2] - jae find_error_sp - cmp cx, [si+2] - ja find_error_sp - add si, 3 - add si, ax - mov di, bx - rep movsb - pop ax - ret + push invalid_read_request_string + cmp byte [si], 0 + jnz .nonresident + cmp eax, 10000h shr 9 + jae find_error_sp + shl ax, 9 + shl cx, 9 + cmp ax, [si+2] + jae find_error_sp + cmp cx, [si+2] + ja find_error_sp + add si, 3 + add si, ax + mov di, bx + rep movsb + pop ax + ret .nonresident: - inc si + inc si .loop: - mov edx, dword [si] - add si, 8 - test edx, edx - jz find_error_sp - imul edx, [bp + sect_per_clust - dat] - sub eax, edx - jnc .loop - add eax, edx - sub edx, eax - push cx - cmp ecx, edx - jb @f - mov cx, dx + mov edx, dword [si] + add si, 8 + test edx, edx + jz find_error_sp + imul edx, [bp + sect_per_clust - dat] + sub eax, edx + jnc .loop + add eax, edx + sub edx, eax + push cx + cmp ecx, edx + jb @f + mov cx, dx @@: - push bx - mov ebx, [si-4] - imul ebx, [bp + sect_per_clust - dat] - add eax, ebx - pop bx - call read - jc ..found_disk_error - mov dx, cx - pop cx - xor eax, eax - sub cx, dx - jnz .loop - pop ax - ret + push bx + mov ebx, [si-4] + imul ebx, [bp + sect_per_clust - dat] + add eax, ebx + pop bx + call read + jc ..found_disk_error + mov dx, cx + pop cx + xor eax, eax + sub cx, dx + jnz .loop + pop ax + ret load_file_ntfs: ; in: ss:bp = 0:dat @@ -405,183 +405,183 @@ load_file_ntfs: ; in: cx = limit in sectors ; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part has been loaded, bx=2 - file not found ; out: dx:ax = file size (0xFFFFFFFF if file not found) - push es bx cx - mov eax, 5 ; root cluster - mov [bp + cur_obj - dat], root_string + push es bx cx + mov eax, 5 ; root cluster + mov [bp + cur_obj - dat], root_string .parse_dir_loop: - push ds si - call read_file_record + push ds si + call read_file_record ; find attributes $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP - mov ax, [bp + freeattr - dat] - mov [bp + index_root - dat], ax - mov ax, 90h ; $INDEX_ROOT - xor bx, bx - call load_attr - mov si, noindex_string - jc find_error_si - mov ax, [bp + freeattr - dat] - mov [bp + index_alloc - dat], ax - mov ax, 0A0h ; $INDEX_ALLOCATION - call load_attr - jnc @f - mov [bp + index_alloc - dat], bx + mov ax, [bp + freeattr - dat] + mov [bp + index_root - dat], ax + mov ax, 90h ; $INDEX_ROOT + xor bx, bx + call load_attr + mov si, noindex_string + jc find_error_si + mov ax, [bp + freeattr - dat] + mov [bp + index_alloc - dat], ax + mov ax, 0A0h ; $INDEX_ALLOCATION + call load_attr + jnc @f + mov [bp + index_alloc - dat], bx @@: - push ds + push ds ; search for entry - mov si, [bp + index_root - dat] - push ss - pop ds - push 0x8100 - pop es - xor ecx, ecx - mov cl, 0x78 - xor bx, bx - push es - call read_file_chunk - pop ds - jc ..found_disk_error - test cx, cx - jz ..attr_overflow - mov si, invalid_read_request_string - cmp word [bx+10], 0 - jnz find_error_si + mov si, [bp + index_root - dat] + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + mov si, invalid_read_request_string + cmp word [bx+10], 0 + jnz find_error_si ; calculate number of items in cache - mov di, [bx+8] ; subnode_size - mov ax, 0x4000 - sub ax, word [bp + frs_size - dat] - cwd - div di - test ax, ax - jz find_error_si - mov si, invalid_volume_msg - test di, 0x1FF - jnz find_error_si - pop cx - mov [bp + cur_index_seg - dat], cx - shl ax, 3 - sub cx, 6000h - mov si, cx - shr cx, 2 - shr si, 10-3 - add cx, ax - add si, 3480h - mov [bp + cur_index_cache - dat], si - add cx, 3500h - mov [ss:si+6], cx - mov dx, di - add bx, 10h + mov di, [bx+8] ; subnode_size + mov ax, 0x4000 + sub ax, word [bp + frs_size - dat] + cwd + div di + test ax, ax + jz find_error_si + mov si, invalid_volume_msg + test di, 0x1FF + jnz find_error_si + pop cx + mov [bp + cur_index_seg - dat], cx + shl ax, 3 + sub cx, 6000h + mov si, cx + shr cx, 2 + shr si, 10-3 + add cx, ax + add si, 3480h + mov [bp + cur_index_cache - dat], si + add cx, 3500h + mov [ss:si+6], cx + mov dx, di + add bx, 10h .scan_record: - add bx, [bx] + add bx, [bx] .scan: - test byte [bx+0Ch], 2 - jnz .look_child - movzx cx, byte [bx+50h] ; namelen - lea di, [bx+52h] ; name - push ds - pop es - pop si ds - push ds si - xor ax, ax + test byte [bx+0Ch], 2 + jnz .look_child + movzx cx, byte [bx+50h] ; namelen + lea di, [bx+52h] ; name + push ds + pop es + pop si ds + push ds si + xor ax, ax .1: - lodsb - cmp al, '/' - jnz @f - mov al, 0 + lodsb + cmp al, '/' + jnz @f + mov al, 0 @@: - cmp al, 'A' - jb .nocapital - cmp al, 'Z' - ja .nocapital - or al, 20h + cmp al, 'A' + jb .nocapital + cmp al, 'Z' + ja .nocapital + or al, 20h .nocapital: - cmp al, 'a' - jb .notletter - cmp al, 'z' - ja .notletter - or byte [es:di], 20h + cmp al, 'a' + jb .notletter + cmp al, 'z' + ja .notletter + or byte [es:di], 20h .notletter: - scasw - loopz .1 - jb .look_child - ja @f - cmp byte [si], 0 - jz .file_found - cmp byte [si], '/' - jz .file_found + scasw + loopz .1 + jb .look_child + ja @f + cmp byte [si], 0 + jz .file_found + cmp byte [si], '/' + jz .file_found @@: - push es - pop ds - add bx, [bx+8] - jmp .scan + push es + pop ds + add bx, [bx+8] + jmp .scan .look_child: - push es - pop ds - test byte [bx+0Ch], 1 - jz .not_found - mov si, [bp + index_alloc - dat] - test si, si - jz .not_found - add bx, [bx+8] - mov eax, [bx-8] - mov es, [bp + cur_index_seg - dat] - push si - mov si, [bp + cur_index_cache - dat] - call cache_lookup - pop si - pushf - mov bx, di - mov bh, 0 - shr bx, 3 - imul bx, dx - add bx, [bp + frs_size - dat] - popf - jnc .noread - push es - push dx - push ss - pop ds - movzx ecx, dx - shr cx, 9 - mul [bp + sect_per_clust - dat] - call read_attr - pop dx - pop es - push es - pop ds - call restore_usa + push es + pop ds + test byte [bx+0Ch], 1 + jz .not_found + mov si, [bp + index_alloc - dat] + test si, si + jz .not_found + add bx, [bx+8] + mov eax, [bx-8] + mov es, [bp + cur_index_seg - dat] + push si + mov si, [bp + cur_index_cache - dat] + call cache_lookup + pop si + pushf + mov bx, di + mov bh, 0 + shr bx, 3 + imul bx, dx + add bx, [bp + frs_size - dat] + popf + jnc .noread + push es + push dx + push ss + pop ds + movzx ecx, dx + shr cx, 9 + mul [bp + sect_per_clust - dat] + call read_attr + pop dx + pop es + push es + pop ds + call restore_usa .noread: - push es - pop ds - add bx, 18h - jmp .scan_record + push es + pop ds + add bx, 18h + jmp .scan_record .not_found: - pop [bp + cur_obj - dat] - mov si, error_not_found - jmp find_error_si + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si .file_found: - pop [bp + cur_obj - dat] - pop cx - mov ax, [bp + index_root - dat] - mov [bp + freeattr - dat], ax - mov eax, [es:bx] - test byte [es:bx+48h+3], 10h - jz .regular_file - cmp byte [si], 0 - jz ..directory_error - inc si - jmp .parse_dir_loop + pop [bp + cur_obj - dat] + pop cx + mov ax, [bp + index_root - dat] + mov [bp + freeattr - dat], ax + mov eax, [es:bx] + test byte [es:bx+48h+3], 10h + jz .regular_file + cmp byte [si], 0 + jz ..directory_error + inc si + jmp .parse_dir_loop .regular_file: - cmp byte [si], 0 - jnz ..notdir_error + cmp byte [si], 0 + jnz ..notdir_error ; read entry - call read_file_record - xor bx, bx - mov ax, 80h - call load_attr - mov si, nodata_string - jc find_error_si - mov si, [bp + index_root - dat] - mov [bp + freeattr - dat], si - push ss - pop ds - jmp load_file_common_end + call read_file_record + xor bx, bx + mov ax, 80h + call load_attr + mov si, nodata_string + jc find_error_si + mov si, [bp + index_root - dat] + mov [bp + freeattr - dat], si + push ss + pop ds + jmp load_file_common_end diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.asm b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.asm index c89becbda..aa4b893bc 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.asm @@ -417,7 +417,7 @@ parse_dir: jbe @f push cx mov cx, 8000h - rep movsw + rep movsw mov cx, ds add cx, 1000h mov ds, cx @@ -429,7 +429,7 @@ parse_dir: @@: add cx, 1000h shl cx, 3 - rep movsw + rep movsw pop si pop ds ; correct positions in cache for existing items @@ -491,7 +491,7 @@ parse_dir: xor di, di mov cx, 0x1800 sub cx, si - rep movsb + rep movsb pop ax push di popa @@ -879,7 +879,7 @@ read_many_bytes.with_first: @@: pop di sub [cur_limit], ecx - rep movsb + rep movsb mov esi, ebx mov bx, di call normalize @@ -921,7 +921,7 @@ read_many_bytes.with_first: mov si, 1000h sub word [cur_limit], cx sbb word [cur_limit+2], 0 - rep movsb + rep movsb mov bx, di call normalize .nopost: diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.txt b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.txt index 969c52bba..7fedad177 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.txt +++ b/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. Если же нет, то считываются все сектора, кроме последнего, после чего + последний сектор считывается отдельно во временную область, и уже + оттуда нужная часть данных копируется в буфер. diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.asm b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.asm index 772829f06..033257c54 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.asm @@ -291,7 +291,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmpsb + repz cmpsb popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.txt b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.txt index 26a9621e1..c0449aa69 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.txt +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.asm b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.asm index 3dbee8865..eb0b7c936 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.asm @@ -284,7 +284,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmpsb + repz cmpsb popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.txt b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.txt index 858b52a8b..caabcb33f 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.txt +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/kordldr.f32.asm b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/kordldr.f32.asm index 61c865d82..b862419e1 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/kordldr.f32.asm +++ b/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/kordldr.f32.asm @@ -111,7 +111,7 @@ nomem: mov di, foldcache_clus mov cx, 8*4/2 + 1 xor ax, ax - rep stosw + rep stosw ; bootsector code caches one FAT sector, [bp-14], in 6000:0000 ; initialize our (more advanced) FAT caching from this mov di, 8400h @@ -167,7 +167,7 @@ kordldr_full: pop es xor si, si xor di, di - rep movsw + rep movsw pop ds ; ...continue loading... mov di, secondary_loader_info @@ -310,7 +310,7 @@ filename equ bp push di mov cx, 8+3 mov al, ' ' - rep stosb + rep stosb pop di mov cl, 8 ; 8 symbols per name mov bl, 1 @@ -434,7 +434,7 @@ folder_next_sector: pusha mov cx, 0x100 xor si, si - rep movsw + rep movsw mov di, es shr di, 8 add [ss:foldcache_size+di-0x90], 0x10 ; 0x10 new entries in the cache @@ -592,7 +592,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmps byte [ss:si], byte [es:di] + repz cmps byte [ss:si], byte [es:di] popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/bootloader/floppy1440.inc b/kernel/branches/Kolibri-acpi/bootloader/floppy1440.inc index 317ff5dac..118d087ec 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/floppy1440.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/floppy1440.inc @@ -1,26 +1,26 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - BS_OEMName db 'KOLIBRI ' ; db 8 - BPB_BytsPerSec dw 512 ; bytes per sector - BPB_SecPerClus db 1 ; sectors per cluster - BPB_RsvdSecCnt dw 1 ; number of reserver sectors - BPB_NumFATs db 2 ; count of FAT data structures - BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) - BPB_TotSec16 dw 2880 ; count of sectors on the volume (2880 for 1.44 mbytes disk) - BPB_Media db 0f0h ; f0 - used for removable media - BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT - BPB_SecPerTrk dw 18 ; sectors per track - BPB_NumHeads dw 2 ; number of heads - BPB_HiddSec dd 0 ; count of hidden sectors - BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) - BS_DrvNum db 0 ; int 13h drive number - BS_Reserved db 0 ; reserved - BS_BootSig db 29h ; Extended boot signature - BS_VolID dd 0 ; Volume serial number - BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) - BS_FilSysType db 'FAT12 ' ; file system type (db 8) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) + BPB_TotSec16 dw 2880 ; count of sectors on the volume (2880 for 1.44 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 18 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/Kolibri-acpi/bootloader/floppy1680.inc b/kernel/branches/Kolibri-acpi/bootloader/floppy1680.inc index da180fd89..8dfc57e10 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/floppy1680.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/floppy1680.inc @@ -1,26 +1,26 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - BS_OEMName db 'KOLIBRI ' ; db 8 - BPB_BytsPerSec dw 512 ; bytes per sector - BPB_SecPerClus db 1 ; sectors per cluster - BPB_RsvdSecCnt dw 1 ; number of reserver sectors - BPB_NumFATs db 2 ; count of FAT data structures - BPB_RootEntCnt dw 112 ; count of 32-byte dir. entries (112*32 = 7 sectors) - BPB_TotSec16 dw 3360 ; count of sectors on the volume (3360 for 1.68 mbytes disk) - BPB_Media db 0f0h ; f0 - used for removable media - BPB_FATSz16 dw 10 ; count of sectors by one copy of FAT - BPB_SecPerTrk dw 21 ; sectors per track - BPB_NumHeads dw 2 ; number of heads - BPB_HiddSec dd 0 ; count of hidden sectors - BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) - BS_DrvNum db 0 ; int 13h drive number - BS_Reserved db 0 ; reserved - BS_BootSig db 29h ; Extended boot signature - BS_VolID dd 0 ; Volume serial number - BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) - BS_FilSysType db 'FAT12 ' ; file system type (db 8) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 112 ; count of 32-byte dir. entries (112*32 = 7 sectors) + BPB_TotSec16 dw 3360 ; count of sectors on the volume (3360 for 1.68 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 10 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 21 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/Kolibri-acpi/bootloader/floppy1743.inc b/kernel/branches/Kolibri-acpi/bootloader/floppy1743.inc index 08707f4ab..c1682c987 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/floppy1743.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/floppy1743.inc @@ -1,26 +1,26 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - BS_OEMName db 'KOLIBRI ' ; db 8 - BPB_BytsPerSec dw 512 ; bytes per sector - BPB_SecPerClus db 1 ; sectors per cluster - BPB_RsvdSecCnt dw 1 ; number of reserver sectors - BPB_NumFATs db 2 ; count of FAT data structures - BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) - BPB_TotSec16 dw 3486 ; count of sectors on the volume (3486 for 1.74 mbytes disk) - BPB_Media db 0f0h ; f0 - used for removable media - BPB_FATSz16 dw 11 ; count of sectors by one copy of FAT - BPB_SecPerTrk dw 21 ; sectors per track - BPB_NumHeads dw 2 ; number of heads - BPB_HiddSec dd 0 ; count of hidden sectors - BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) - BS_DrvNum db 0 ; int 13h drive number - BS_Reserved db 0 ; reserved - BS_BootSig db 29h ; Extended boot signature - BS_VolID dd 0 ; Volume serial number - BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) - BS_FilSysType db 'FAT12 ' ; file system type (db 8) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) + BPB_TotSec16 dw 3486 ; count of sectors on the volume (3486 for 1.74 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 11 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 21 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/Kolibri-acpi/bootloader/floppy2880.inc b/kernel/branches/Kolibri-acpi/bootloader/floppy2880.inc index b8c4dc510..40d80b92f 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/floppy2880.inc +++ b/kernel/branches/Kolibri-acpi/bootloader/floppy2880.inc @@ -1,26 +1,26 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - BS_OEMName db 'KOLIBRI ' ; db 8 - BPB_BytsPerSec dw 512 ; bytes per sector - BPB_SecPerClus db 2 ; sectors per cluster - BPB_RsvdSecCnt dw 1 ; number of reserver sectors - BPB_NumFATs db 2 ; count of FAT data structures - BPB_RootEntCnt dw 240 ; count of 32-byte dir. entries (240*32 = 15 sectors) - BPB_TotSec16 dw 5760 ; count of sectors on the volume (5760 for 2.88 mbytes disk) - BPB_Media db 0f0h ; f0 - used for removable media - BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT - BPB_SecPerTrk dw 36 ; sectors per track - BPB_NumHeads dw 2 ; number of heads - BPB_HiddSec dd 0 ; count of hidden sectors - BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) - BS_DrvNum db 0 ; int 13h drive number - BS_Reserved db 0 ; reserved - BS_BootSig db 29h ; Extended boot signature - BS_VolID dd 0 ; Volume serial number - BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) - BS_FilSysType db 'FAT12 ' ; file system type (db 8) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 2 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 240 ; count of 32-byte dir. entries (240*32 = 15 sectors) + BPB_TotSec16 dw 5760 ; count of sectors on the volume (5760 for 2.88 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 36 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/Kolibri-acpi/bootloader/readme b/kernel/branches/Kolibri-acpi/bootloader/readme index 14d1c6f1d..b68c8bac0 100644 --- a/kernel/branches/Kolibri-acpi/bootloader/readme +++ b/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 --------------------------------------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/build.bat b/kernel/branches/Kolibri-acpi/build.bat index 54f98c2da..fef4f7f6d 100644 --- a/kernel/branches/Kolibri-acpi/build.bat +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc b/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc new file mode 100644 index 000000000..6f5800e52 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc b/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc new file mode 100644 index 000000000..d775ff998 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/hub.inc b/kernel/branches/Kolibri-acpi/bus/usb/hub.inc new file mode 100644 index 000000000..3387ddd02 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/init.inc b/kernel/branches/Kolibri-acpi/bus/usb/init.inc new file mode 100644 index 000000000..5a596346e --- /dev/null +++ b/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" diff --git a/kernel/branches/Kolibri-acpi/bus/usb/memory.inc b/kernel/branches/Kolibri-acpi/bus/usb/memory.inc new file mode 100644 index 000000000..5567354ae --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/ohci.inc b/kernel/branches/Kolibri-acpi/bus/usb/ohci.inc new file mode 100644 index 000000000..c99fddf19 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc b/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc new file mode 100644 index 000000000..f3bd72529 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/protocol.inc b/kernel/branches/Kolibri-acpi/bus/usb/protocol.inc new file mode 100644 index 000000000..9ee0995d9 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc b/kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc new file mode 100644 index 000000000..8248475e9 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc b/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc new file mode 100644 index 000000000..243972f2d --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/const.inc b/kernel/branches/Kolibri-acpi/const.inc index fa4a1882a..15e7dcfdb 100644 --- a/kernel/branches/Kolibri-acpi/const.inc +++ b/kernel/branches/Kolibri-acpi/const.inc @@ -181,131 +181,6 @@ ends 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) @@ -331,7 +206,7 @@ new_app_base equ 0; twdw equ 0x2000 ;(CURRENT_TASK-window_data) std_application_base_address equ new_app_base -RING0_STACK_SIZE equ (0x2000 - 512) ;512 FPU +RING0_STACK_SIZE equ (0x2000 - 512) ;512 байт для контекста FPU REG_SS equ (RING0_STACK_SIZE-4) REG_APP_ESP equ (RING0_STACK_SIZE-8) @@ -368,7 +243,6 @@ BOOT_PITCH equ 0x9001 ;word scanline length 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_MOUSE equ 0x00000020 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 @@ struct SRV 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 @@ struct IRQH list LHEAD handler dd ? ;handler roututine data dd ? ;user-specific data + num_ints dd ? ;how many times handled ends diff --git a/kernel/branches/Kolibri-acpi/core/conf_lib-sp.inc b/kernel/branches/Kolibri-acpi/core/conf_lib-sp.inc index 65acc92fd..c400336a0 100644 --- a/kernel/branches/Kolibri-acpi/core/conf_lib-sp.inc +++ b/kernel/branches/Kolibri-acpi/core/conf_lib-sp.inc @@ -1,11 +1,11 @@ -; ste archivo debe ser editado con codificacin CP866 +; Éste archivo debe ser editado con codificación CP866 -ugui_mouse_speed db 'velocidad del ratn',0 -ugui_mouse_delay db 'demora del ratn',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 'msc',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 diff --git a/kernel/branches/Kolibri-acpi/core/conf_lib.inc b/kernel/branches/Kolibri-acpi/core/conf_lib.inc index 1b235d590..b74cd2abf 100644 --- a/kernel/branches/Kolibri-acpi/core/conf_lib.inc +++ b/kernel/branches/Kolibri-acpi/core/conf_lib.inc @@ -72,54 +72,7 @@ ugui_mouse_delay_def db '0x00A',0 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 @@ endp proc strtoint_dec stdcall,strs pushad xor edx, edx - ; + ; поиск конца mov esi, [strs] @@: lodsb @@ -180,7 +133,7 @@ proc strtoint_dec stdcall,strs dec ebx or ebx, ebx jz @f - imul ecx, ecx, 10; 冷 + imul ecx, ecx, 10; порядок jmp @b @@: diff --git a/kernel/branches/Kolibri-acpi/core/dll.inc b/kernel/branches/Kolibri-acpi/core/dll.inc index 55ba1741b..fe307a75a 100644 --- a/kernel/branches/Kolibri-acpi/core/dll.inc +++ b/kernel/branches/Kolibri-acpi/core/dll.inc @@ -142,7 +142,11 @@ proc srv_handler stdcall, ioctl:dword 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 @@ srv_handlerEx: 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,8 +221,30 @@ proc get_service stdcall, sz_name:dword 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 @@ -223,10 +253,10 @@ proc reg_service stdcall, name:dword, handler:dword 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 diff --git a/kernel/branches/Kolibri-acpi/core/exports.inc b/kernel/branches/Kolibri-acpi/core/exports.inc index fc5af7e80..26617948b 100644 --- a/kernel/branches/Kolibri-acpi/core/exports.inc +++ b/kernel/branches/Kolibri-acpi/core/exports.inc @@ -95,6 +95,17 @@ iglobal 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: @@ -180,6 +191,18 @@ kernel_export: 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 diff --git a/kernel/branches/Kolibri-acpi/core/fpu.inc b/kernel/branches/Kolibri-acpi/core/fpu.inc index 3a0961af5..dd38a7347 100644 --- a/kernel/branches/Kolibri-acpi/core/fpu.inc +++ b/kernel/branches/Kolibri-acpi/core/fpu.inc @@ -179,5 +179,5 @@ except_7: ;#NM exception handler iret iglobal - fpu_owner dd 0 + fpu_owner dd 2 endg diff --git a/kernel/branches/Kolibri-acpi/core/irq.inc b/kernel/branches/Kolibri-acpi/core/irq.inc index 3ab2bc7c9..482de4217 100644 --- a/kernel/branches/Kolibri-acpi/core/irq.inc +++ b/kernel/branches/Kolibri-acpi/core/irq.inc @@ -66,8 +66,7 @@ proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword test edx, edx jz .err - pushfd - cli + spin_lock_irqsave IrqsList ;allocate handler @@ -84,13 +83,14 @@ proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword 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 stdcall enable_irq, ebx .fail: - popfd + spin_unlock_irqrestore IrqsList .err: pop ebx mov eax, [.irqh] @@ -188,7 +188,7 @@ align 16 push [ebx+IRQH.data] call [ebx+IRQH.handler] - add esp, 4 + pop ecx pop esi pop edi @@ -197,6 +197,7 @@ align 16 test eax, eax jz .next + inc [ebx+IRQH.num_ints] btr [irq_active_set], ebp jmp .next @@ -204,9 +205,70 @@ align 16 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 diff --git a/kernel/branches/Kolibri-acpi/core/memory.inc b/kernel/branches/Kolibri-acpi/core/memory.inc index 3b8197d67..3ad095f0b 100644 --- a/kernel/branches/Kolibri-acpi/core/memory.inc +++ b/kernel/branches/Kolibri-acpi/core/memory.inc @@ -631,21 +631,21 @@ proc page_fault_handler mov eax, [pf_err_code] cmp ebx, OS_BASE ;ebx == .err_addr - jb .user_space ; ; + jb .user_space ;страница в памяти приложения ; cmp ebx, page_tabs - jb .kernel_space ; + jb .kernel_space ;страница в памяти ядра cmp ebx, kernel_tabs - jb .alloc;.app_tabs ; ; - ; -if 0 ; + jb .alloc;.app_tabs ;таблицы страниц приложения ; + ;просто создадим одну +if 0 ;пока это просто лишнее cmp ebx, LFB_BASE - jb .core_tabs ; - ; + jb .core_tabs ;таблицы страниц ядра + ;Ошибка .lfb: - ; LFB - ; + ;область LFB + ;Ошибка jmp .fail end if .core_tabs: @@ -661,21 +661,21 @@ end if .user_space: test eax, PG_MAP - jnz .err_access ; - ; ? + jnz .err_access ;Страница присутствует + ;Ошибка доступа ? shr ebx, 12 mov ecx, ebx shr ecx, 10 mov edx, [master_tab+ecx*4] test edx, PG_MAP - jz .fail ; - ; + jz .fail ;таблица страниц не создана + ;неверный адрес в программе mov eax, [page_tabs+ebx*4] test eax, 2 - jz .fail ; ; - ;. + jz .fail ;адрес не зарезервирован для ; + ;использования. Ошибка .alloc: call alloc_page test eax, eax @@ -731,16 +731,16 @@ end if .kernel_space: test eax, PG_MAP - jz .fail ; + jz .fail ;страница не присутствует test eax, 12 ;U/S (+below) - jnz .fail ; - ; + jnz .fail ;приложение обратилось к памяти + ;ядра ;test eax, 8 - ;jnz .fail ; - ; . P4/Xeon + ;jnz .fail ;установлен зарезервированный бит + ;в таблицах страниц. добавлено в P4/Xeon -; +;попытка записи в защищённую страницу ядра cmp ebx, tss._io_map_0 jb .fail @@ -1136,11 +1136,6 @@ proc sys_ipc_send stdcall, PID:dword, msg_addr:dword, msg_size:dword 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: diff --git a/kernel/branches/Kolibri-acpi/core/sched.inc b/kernel/branches/Kolibri-acpi/core/sched.inc index 72ad56d6e..64cb3a75c 100644 --- a/kernel/branches/Kolibri-acpi/core/sched.inc +++ b/kernel/branches/Kolibri-acpi/core/sched.inc @@ -29,8 +29,8 @@ irq0: .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 @@ end if 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 @@ update_counters: 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 @@ updatecputimes: 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 @@ mutex_unlock: 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 @@ struc TIMER } -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 diff --git a/kernel/branches/Kolibri-acpi/core/sys32-sp.inc b/kernel/branches/Kolibri-acpi/core/sys32-sp.inc index 20e759d98..2bc437aa3 100644 --- a/kernel/branches/Kolibri-acpi/core/sys32-sp.inc +++ b/kernel/branches/Kolibri-acpi/core/sys32-sp.inc @@ -1,4 +1,4 @@ -; ste archivo debe ser editado con codificacin CP866 +; Éste archivo debe ser editado con codificación CP866 - msg_sel_ker db "ncleo", 0 - msg_sel_app db "aplicacin", 0 + msg_sel_ker: cp850 "núcleo", 0 + msg_sel_app: cp850 "aplicación", 0 diff --git a/kernel/branches/Kolibri-acpi/core/sys32.inc b/kernel/branches/Kolibri-acpi/core/sys32.inc index bb7ba9502..45ec1ac88 100644 --- a/kernel/branches/Kolibri-acpi/core/sys32.inc +++ b/kernel/branches/Kolibri-acpi/core/sys32.inc @@ -61,7 +61,7 @@ iglobal idtreg: ; data for LIDT instruction (!!! must be immediately below sys_int data) dw 2*($-sys_int-4)-1 dd idts ;0x8000B100 - dw 0 ; + dw 0 ;просто выравнивание msg_fault_sel dd msg_exc_8,msg_exc_u,msg_exc_a,msg_exc_b dd msg_exc_c,msg_exc_d,msg_exc_e @@ -109,19 +109,19 @@ uglobal pf_err_code dd ? endg -page_fault_exc: ; : ... - pop [ss:pf_err_code]; #PF +page_fault_exc: ; дуракоусточивость: селекторы испорчены... + pop [ss:pf_err_code]; действительно до следующего #PF save_ring3_context mov bl, 14 -exc_c: ; (, 7- - #NM) -; / 3- + pushad (.., ) +exc_c: ; исключения (все, кроме 7-го - #NM) +; Фрэйм стека при исключении/прерывании из 3-го кольца + pushad (т.е., именно здесь) reg_ss equ esp+0x30 reg_esp3 equ esp+0x2C reg_eflags equ esp+0x28 reg_cs3 equ esp+0x24 reg_eip equ esp+0x20 - ; pushad + ; это фрэйм от pushad reg_eax equ esp+0x1C reg_ecx equ esp+0x18 reg_edx equ esp+0x14 @@ -131,10 +131,10 @@ exc_c: ; reg_esi equ esp+0x04 reg_edi equ esp+0x00 - mov ax, app_data ; - mov ds, ax ; - mov es, ax ; - cld ; DF + mov ax, app_data ;исключение + mov ds, ax ;загрузим правильные значения + mov es, ax ;в регистры + cld ; и приводим DF к стандарту movzx ebx, bl ; redirect to V86 manager? (EFLAGS & 0x20000) != 0? test byte[reg_eflags+2], 2 @@ -159,6 +159,7 @@ exc_c: ; 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,45 +262,40 @@ show_error_parameters: 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 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 eax + pop edx ecx 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 @@ terminate: ; terminate application 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 @@ term9: 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,27 +675,13 @@ term9: 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 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 diff --git a/kernel/branches/Kolibri-acpi/core/syscall.inc b/kernel/branches/Kolibri-acpi/core/syscall.inc index c2b022caa..206207955 100644 --- a/kernel/branches/Kolibri-acpi/core/syscall.inc +++ b/kernel/branches/Kolibri-acpi/core/syscall.inc @@ -29,7 +29,7 @@ cross_order: align 32 sysenter_entry: - ; + ; Настраиваем стек mov esp, [ss:tss._esp0] sti push ebp ; save app esp + 4 @@ -47,7 +47,7 @@ sysenter_entry: call unprotect_from_terminate popad ;------------------ - xchg ecx, [ss:esp] ; - app ecx, ecx - app esp + 4 + xchg ecx, [ss:esp] ; в вершин стека - app ecx, ecx - app esp + 4 sub ecx, 4 xchg edx, [ecx] ; edx - return point, & save original edx push edx @@ -112,7 +112,7 @@ iglobal align 4 servetable: - dd socket ; 53-Socket interface + dd 0 dd 0 dd 0 dd 0 @@ -182,8 +182,8 @@ iglobal 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 @@ iglobal 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 diff --git a/kernel/branches/Kolibri-acpi/core/taskman.inc b/kernel/branches/Kolibri-acpi/core/taskman.inc index f2a2e235c..ee93091a7 100644 --- a/kernel/branches/Kolibri-acpi/core/taskman.inc +++ b/kernel/branches/Kolibri-acpi/core/taskman.inc @@ -90,7 +90,7 @@ proc fs_execute 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,19 +152,7 @@ proc fs_execute test eax, eax jz .err_hdr -.wait_lock: - cmp [application_table_status], 0 - je .get_lock - call change_task - jmp .wait_lock - -.get_lock: - mov eax, 1 - xchg eax, [application_table_status] - test eax, eax - jnz .wait_lock - - call set_application_table_status + call lock_application_table call get_new_process_place test eax, eax @@ -246,9 +234,8 @@ end if 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 @@ end if .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 @@ end if pushad stdcall set_cursor, [handle] mov [redrawmouse_unconditional], 1 - call __sys_draw_pointer + call wakeup_osloop popad @@: ret @@ -550,7 +536,7 @@ proc destroy_app_space stdcall, pg_dir:dword, dlls_list:dword xor edx, edx push edx - mov eax, 0x2 + mov eax, 0x1 mov ebx, [pg_dir] .loop: ;eax = current slot of process @@ -898,19 +884,7 @@ proc new_sys_threads 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 - -.get_lock: - mov eax, 1 - xchg eax, [application_table_status] - test eax, eax - jnz .wait_lock - - call set_application_table_status + call lock_application_table call get_new_process_place test eax, eax @@ -967,14 +941,13 @@ proc new_sys_threads ;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 @@ proc set_app_params stdcall,slot:dword, params:dword,\ 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 @@ proc set_app_params stdcall,slot:dword, params:dword,\ 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 @@ proc set_app_params stdcall,slot:dword, params:dword,\ 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 diff --git a/kernel/branches/Kolibri-acpi/core/timers.inc b/kernel/branches/Kolibri-acpi/core/timers.inc index f9030de24..a9293b081 100644 --- a/kernel/branches/Kolibri-acpi/core/timers.inc +++ b/kernel/branches/Kolibri-acpi/core/timers.inc @@ -203,3 +203,28 @@ check_timers: 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 diff --git a/kernel/branches/Kolibri-acpi/core/v86.inc b/kernel/branches/Kolibri-acpi/core/v86.inc index 23e001af6..6d58f5bef 100644 --- a/kernel/branches/Kolibri-acpi/core/v86.inc +++ b/kernel/branches/Kolibri-acpi/core/v86.inc @@ -178,7 +178,7 @@ v86_set_page: ; esi=handle ; out: eax=V86 address, para-aligned (0x10 multiple) ; destroys: nothing -; ᠭ!!! +; недописана!!! ;v86_alloc: ; push ebx ecx edx edi ; lea ebx, [esi+V86_machine.mutex] @@ -377,8 +377,8 @@ v86_exc_c: jnz .nogp ; Otherwise we can safely access byte at CS:IP ; (because it is #GP, not #PF handler) -; ᫨ 嫮 ᪫祭 ⮫쪮 - ⥭ ⮢ , -; 㦥 嫮⠫ 뫮 #GP +; Если бы мы могли схлопотать исключение только из-за чтения байтов кода, +; мы бы его уже схлопотали и это было бы не #GP movzx esi, word [esp+v86_regs.cs] shl esi, 4 add esi, [esp+v86_regs.eip] diff --git a/kernel/branches/Kolibri-acpi/data16.inc b/kernel/branches/Kolibri-acpi/data16.inc index 7e178888c..e0311b05b 100644 --- a/kernel/branches/Kolibri-acpi/data16.inc +++ b/kernel/branches/Kolibri-acpi/data16.inc @@ -13,9 +13,9 @@ preboot_lfb db 0 preboot_bootlog db 0 boot_drive db 0 bx_from_load: - dw 'r1' ; - , bx ; {SPraid}[13.03.2007] - ; a,b,c,d - , r - - ; # ... , . '1', 1 + dw 'r1' ; структура для хранения параметров- откуда гашрузились, берется ниже из bx ; {SPraid}[13.03.2007] + ; a,b,c,d - винчестеры, r - рам диск + ; # диска... символ, а не байт. '1', а не 1 align 4 old_ints_h: diff --git a/kernel/branches/Kolibri-acpi/data32.inc b/kernel/branches/Kolibri-acpi/data32.inc index 4fb16f97f..4f4ec0cdd 100644 --- a/kernel/branches/Kolibri-acpi/data32.inc +++ b/kernel/branches/Kolibri-acpi/data32.inc @@ -49,43 +49,43 @@ keymap_alt: if lang eq ru - boot_initirq db '樠 IRQ',0 - boot_picinit db '樠 PIC',0 - boot_v86machine db '樠 ⥬ V86 設',0 - boot_inittimer db '樠 ⥬ ⠩ (IRQ0)',0 - boot_initapic db '⪠ 樠樨 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_APIC_found db 'APIC 祭', 0 - boot_APIC_nfound db 'APIC ', 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: 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' @@ -119,7 +119,7 @@ else boot_pal_vga db 'Setting VGA 640x480 palette',0 boot_failed db 'Failed to start first app',0 boot_mtrr db 'Setting MTRR',0 - + boot_APIC_found db 'APIC enabled', 0 boot_APIC_nfound db 'APIC not found', 0 if preboot_blogesc @@ -145,6 +145,7 @@ AMD_str db "AuthenticAMD",0 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 @@ read_firstapp db '/sys/' firstapp db 'LAUNCHER',0 notifyapp db '@notify',0 if lang eq ru -ud_user_message db '訡: ন ',0 +ud_user_message: cp866 'Ошибка: неподдерживаемая инструкция процессора',0 else if ~ lang eq sp ud_user_message db 'Error: unsupported processor instruction',0 end if @@ -332,8 +333,9 @@ mem_block_list rd 64*2 mem_used_list rd 64*2 mem_hash_cnt rd 64 -cpu_freq rq 1 +MEM_AMOUNT rd 1 +cpu_freq rq 1 heap_mutex MUTEX heap_size rd 1 heap_free rd 1 @@ -363,6 +365,35 @@ _display display_t _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 @@ LFBSize rd 1 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 @@ BiosDisksData rb 200h 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 diff --git a/kernel/branches/Kolibri-acpi/data32sp.inc b/kernel/branches/Kolibri-acpi/data32sp.inc index 1634abe75..6fb005f8a 100644 --- a/kernel/branches/Kolibri-acpi/data32sp.inc +++ b/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 libreras (.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 ratn',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 aplicacin',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 'versin 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: instruccin no soportada por el procesador',0 +ud_user_message:cp850 'Error: instrucción no soportada por el procesador',0 diff --git a/kernel/branches/Kolibri-acpi/detect/biosdisk.inc b/kernel/branches/Kolibri-acpi/detect/biosdisk.inc index acabba277..24bbd433a 100644 --- a/kernel/branches/Kolibri-acpi/detect/biosdisk.inc +++ b/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 @@ bdds: 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 @@ bdds: 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 diff --git a/kernel/branches/Kolibri-acpi/detect/dev_fd.inc b/kernel/branches/Kolibri-acpi/detect/dev_fd.inc index a4a058f78..82ab0c54e 100644 --- a/kernel/branches/Kolibri-acpi/detect/dev_fd.inc +++ b/kernel/branches/Kolibri-acpi/detect/dev_fd.inc @@ -9,9 +9,9 @@ $Revision$ ;*************************************************** -; -; FDD -; Mario79 +; предварительная очистка области таблицы +; поиск и занесение в таблицу приводов FDD +; автор Mario79 ;*************************************************** xor eax, eax mov edi, DRIVE_DATA diff --git a/kernel/branches/Kolibri-acpi/detect/dev_hdcd.inc b/kernel/branches/Kolibri-acpi/detect/dev_hdcd.inc index c92472de5..30f95b87c 100644 --- a/kernel/branches/Kolibri-acpi/detect/dev_hdcd.inc +++ b/kernel/branches/Kolibri-acpi/detect/dev_hdcd.inc @@ -9,13 +9,13 @@ $Revision$ ;****************************************************** -; HDD CD -; . -; Mario79 +; поиск приводов HDD и CD +; автор исходного текста Кулаков Владимир Геннадьевич. +; адаптация и доработка Mario79 ;****************************************************** ;**************************************************** -;* HDD CD * +;* ПОИСК HDD и CD * ;**************************************************** FindHDD: mov [ChannelNumber], 1 @@ -71,54 +71,54 @@ FindHDD_3: ret -; LBA +; Адрес считываемого сектора в режиме LBA uglobal SectorAddress DD ? endg ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - (1 2); * -;* DiskNumber - (0 1). * -;* * -;* Sector512. * +;* ЧТЕНИЕ ИДЕНТИФИКАТОРА ЖЕСТКОГО ДИСКА * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска на канале (0 или 1). * +;* Идентификационный блок данных считывается * +;* в массив Sector512. * ;************************************************* ReadHDD_ID: -; CHS +; Задать режим CHS mov [ATAAddressMode], 0 -; +; Послать команду идентификации устройства mov [ATAFeatures], 0 mov [ATAHead], 0 mov [ATACommand], 0ECh call SendCommandToHDD - cmp [DevErrorCode], 0; - jne @@End ;, + cmp [DevErrorCode], 0;проверить код ошибки + jne @@End ;закончить, сохранив код ошибки mov DX, [ATABasePortAddr] - add DX, 7 ; + add DX, 7 ;адрес регистра состояни mov ecx, 0xffff @@WaitCompleet: - ; + ; Проверить время выполнения команды dec ecx ; cmp ecx,0 - jz @@Error1 ; - - ; + jz @@Error1 ;ошибка тайм-аута + ; Проверить готовность in AL, DX - test AL, 80h ; BSY + test AL, 80h ;состояние сигнала BSY jnz @@WaitCompleet - test AL, 1 ; ERR + test AL, 1 ;состояние сигнала ERR jnz @@Error6 - test AL, 08h ; DRQ + test AL, 08h ;состояние сигнала DRQ jz @@WaitCompleet -; +; Принять блок данных от контроллера ; mov AX,DS ; mov ES,AX mov EDI, Sector512 ;offset Sector512 - mov DX, [ATABasePortAddr]; - mov CX, 256 ; - rep insw ; + mov DX, [ATABasePortAddr];регистр данных + mov CX, 256 ;число считываемых слов + rep insw ;принять блок данных ret -; +; Записать код ошибки @@Error1: mov [DevErrorCode], 1 ret @@ -129,120 +129,120 @@ ReadHDD_ID: iglobal -; 1 2 +; Стандартные базовые адреса каналов 1 и 2 StandardATABases DW 1F0h, 170h endg uglobal -; +; Номер канала ChannelNumber DW ? -; +; Номер диска DiskNumber DB ? -; ATA +; Базовый адрес группы портов контроллера ATA ATABasePortAddr DW ? -; ATA- -ATAFeatures DB ? ; -ATASectorCount DB ? ; -ATASectorNumber DB ? ; -ATACylinder DW ? ; -ATAHead DB ? ; -ATAAddressMode DB ? ; (0 - CHS, 1 - LBA) -ATACommand DB ? ; , -; (0 - , 1 - -; , 2 - , -; 3 - , 4 - , -; 5 - , 6 - -; ) +; Параметры ATA-команды +ATAFeatures DB ? ;особенности +ATASectorCount DB ? ;количество обрабатываемых секторов +ATASectorNumber DB ? ;номер начального сектора +ATACylinder DW ? ;номер начального цилиндра +ATAHead DB ? ;номер начальной головки +ATAAddressMode DB ? ;режим адресации (0 - CHS, 1 - LBA) +ATACommand DB ? ;код команды, подлежащей выполнению +; Код ошибки (0 - нет ошибок, 1 - превышен допустимый +; интервал ожидания, 2 - неверный код режима адресации, +; 3 - неверный номер канала, 4 - неверный номер диска, +; 5 - неверный номер головки, 6 - ошибка при выполнении +; команды) DevErrorCode dd ? endg ;**************************************************** -;* * -;* * -;* : * -;* ChannelNumber - (1 2); * -;* DiskNumber - (0 1); * -;* ATAFeatures - ""; * -;* ATASectorCount - ; * -;* ATASectorNumber - ; * -;* ATACylinder - ; * -;* ATAHead - ; * -;* ATAAddressMode - (0-CHS, 1-LBA); * -;* ATACommand - . * -;* : * -;* ATABasePortAddr - HDD; * -;* DevErrorCode - . * -;* DevErrorCode * -;* . * +;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска (0 или 1); * +;* ATAFeatures - "особенности"; * +;* ATASectorCount - количество секторов; * +;* ATASectorNumber - номер начального сектора; * +;* ATACylinder - номер начального цилиндра; * +;* ATAHead - номер начальной головки; * +;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); * +;* ATACommand - код команды. * +;* После успешного выполнения функции: * +;* в ATABasePortAddr - базовый адрес HDD; * +;* в DevErrorCode - ноль. * +;* При возникновении ошибки в DevErrorCode будет * +;* возвращен код ошибки. * ;**************************************************** SendCommandToHDD: -; +; Проверить значение кода режима cmp [ATAAddressMode], 1 ja @@Err2 -; +; Проверить корректность номера канала mov BX, [ChannelNumber] cmp BX, 1 jb @@Err3 cmp BX, 2 ja @@Err3 -; +; Установить базовый адрес dec BX shl BX, 1 movzx ebx, bx mov AX, [ebx+StandardATABases] mov [ATABasePortAddr], AX -; HDD - ; +; Ожидание готовности HDD к приему команды + ; Выбрать нужный диск mov DX, [ATABasePortAddr] - add DX, 6 ; + add DX, 6 ;адрес регистра головок mov AL, [DiskNumber] - cmp AL, 1 ; + cmp AL, 1 ;проверить номера диска ja @@Err4 shl AL, 4 or AL, 10100000b out DX, AL - ; , + ; Ожидать, пока диск не будет готов inc DX mov ecx, 0xfff ; mov eax,[timer_ticks] ; mov [TickCounter_1],eax @@WaitHDReady: - ; + ; Проверить время ожидани dec ecx ; cmp ecx,0 jz @@Err1 ; mov eax,[timer_ticks] ; sub eax,[TickCounter_1] -; cmp eax,300 ; 300 -; ja @@Err1 ; - - ; +; cmp eax,300 ;ожидать 300 тиков +; ja @@Err1 ;ошибка тайм-аута + ; Прочитать регистр состояни in AL, DX - ; BSY + ; Проверить состояние сигнала BSY test AL, 80h jnz @@WaitHDReady - ; DRQ + ; Проверить состояние сигнала DRQ test AL, 08h jnz @@WaitHDReady -; +; Загрузить команду в регистры контроллера cli mov DX, [ATABasePortAddr] - inc DX ; "" + inc DX ;регистр "особенностей" mov AL, [ATAFeatures] out DX, AL - inc DX ; + inc DX ;счетчик секторов mov AL, [ATASectorCount] out DX, AL - inc DX ; + inc DX ;регистр номера сектора mov AL, [ATASectorNumber] out DX, AL - inc DX ; ( ) + inc DX ;номер цилиндра (младший байт) mov AX, [ATACylinder] out DX, AL - inc DX ; ( ) + inc DX ;номер цилиндра (старший байт) mov AL, AH out DX, AL - inc DX ; / + inc DX ;номер головки/номер диска mov AL, [DiskNumber] shl AL, 4 - cmp [ATAHead], 0Fh; + cmp [ATAHead], 0Fh;проверить номер головки ja @@Err5 or AL, [ATAHead] or AL, 10100000b @@ -250,15 +250,15 @@ SendCommandToHDD: shl AH, 6 or AL, AH out DX, AL -; +; Послать команду mov AL, [ATACommand] - inc DX ; + inc DX ;регистр команд out DX, AL sti -; +; Сбросить признак ошибки mov [DevErrorCode], 0 ret -; +; Записать код ошибки @@Err1: mov [DevErrorCode], 1 ret @@ -273,22 +273,22 @@ SendCommandToHDD: ret @@Err5: mov [DevErrorCode], 5 -; +; Завершение работы программы ret ;************************************************* -;* ATAPI * -;* * -;* : * -;* ChannelNumber - ; * -;* DiskNumber - . * -;* * -;* Sector512. * +;* ЧТЕНИЕ ИДЕНТИФИКАТОРА УСТРОЙСТВА ATAPI * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;* Идентификационный блок данных считывается * +;* в массив Sector512. * ;************************************************* ReadCD_ID: -; CHS +; Задать режим CHS mov [ATAAddressMode], 0 -; +; Послать команду идентификации устройства mov [ATAFeatures], 0 mov [ATASectorCount], 0 mov [ATASectorNumber], 0 @@ -296,34 +296,34 @@ ReadCD_ID: mov [ATAHead], 0 mov [ATACommand], 0A1h call SendCommandToHDD - cmp [DevErrorCode], 0; - jne @@End_1 ;, -; HDD + cmp [DevErrorCode], 0;проверить код ошибки + jne @@End_1 ;закончить, сохранив код ошибки +; Ожидать готовность данных HDD mov DX, [ATABasePortAddr] - add DX, 7 ; 17h + add DX, 7 ;порт 1х7h mov ecx, 0xffff @@WaitCompleet_1: - ; + ; Проверить врем dec ecx ; cmp ecx,0 - jz @@Error1_1 ; - - ; + jz @@Error1_1 ;ошибка тайм-аута + ; Проверить готовность in AL, DX - test AL, 80h ; BSY + test AL, 80h ;состояние сигнала BSY jnz @@WaitCompleet_1 - test AL, 1 ; ERR + test AL, 1 ;состояние сигнала ERR jnz @@Error6_1 - test AL, 08h ; DRQ + test AL, 08h ;состояние сигнала DRQ jz @@WaitCompleet_1 -; +; Принять блок данных от контроллера ; mov AX,DS ; mov ES,AX mov EDI, Sector512 ;offset Sector512 - mov DX, [ATABasePortAddr]; 1x0h - mov CX, 256; + mov DX, [ATABasePortAddr];порт 1x0h + mov CX, 256;число считываемых слов rep insw ret -; +; Записать код ошибки @@Error1_1: mov [DevErrorCode], 1 ret @@ -333,52 +333,52 @@ ReadCD_ID: ret ;************************************************* -;* * -;* * -;* : * -;* ChannelNumber - (1 2); * -;* DiskNumber - (0 1). * +;* СБРОС УСТРОЙСТВА * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска (0 или 1). * ;************************************************* DeviceReset: -; +; Проверить корректность номера канала mov BX, [ChannelNumber] cmp BX, 1 jb @@Err3_2 cmp BX, 2 ja @@Err3_2 -; +; Установить базовый адрес dec BX shl BX, 1 movzx ebx, bx mov DX, [ebx+StandardATABases] mov [ATABasePortAddr], DX -; - add DX, 6 ; +; Выбрать нужный диск + add DX, 6 ;адрес регистра головок mov AL, [DiskNumber] - cmp AL, 1 ; + cmp AL, 1 ;проверить номера диска ja @@Err4_2 shl AL, 4 or AL, 10100000b out DX, AL -; "" +; Послать команду "Сброс" mov AL, 08h - inc DX ; + inc DX ;регистр команд out DX, AL mov ecx, 0x80000 @@WaitHDReady_1: - ; + ; Проверить время ожидани dec ecx ; cmp ecx,0 - je @@Err1_2 ; - - ; + je @@Err1_2 ;ошибка тайм-аута + ; Прочитать регистр состояни in AL, DX - ; BSY + ; Проверить состояние сигнала BSY test AL, 80h jnz @@WaitHDReady_1 -; +; Сбросить признак ошибки mov [DevErrorCode], 0 ret -; +; Обработка ошибок @@Err1_2: mov [DevErrorCode], 1 ret @@ -387,7 +387,7 @@ DeviceReset: ret @@Err4_2: mov [DevErrorCode], 4 -; +; Записать код ошибки ret EndFindHDD: diff --git a/kernel/branches/Kolibri-acpi/detect/sear_par.inc b/kernel/branches/Kolibri-acpi/detect/sear_par.inc index e21a9fb41..9c55b9299 100644 --- a/kernel/branches/Kolibri-acpi/detect/sear_par.inc +++ b/kernel/branches/Kolibri-acpi/detect/sear_par.inc @@ -9,9 +9,9 @@ $Revision$ ;**************************************************** -; HDD -; -; Mario79 +; поиск логических дисков на обнаруженных HDD +; и занесение данных в область таблицы +; автор Mario79 ;**************************************************** mov [transfer_adress], DRIVE_DATA+0xa search_partitions_ide0: diff --git a/kernel/branches/Kolibri-acpi/docs/drivers_api.txt b/kernel/branches/Kolibri-acpi/docs/drivers_api.txt index ca55bbff6..3df3ac97d 100644 --- a/kernel/branches/Kolibri-acpi/docs/drivers_api.txt +++ b/kernel/branches/Kolibri-acpi/docs/drivers_api.txt @@ -13,12 +13,14 @@ drivers. They must be called in the following order: DiskAdd, then zero or 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 @@ struc DISKFUNC { .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 @@ struc DISKFUNC ; 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* numsectors); +; 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* numsectors); +; 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. } diff --git a/kernel/branches/Kolibri-acpi/docs/loader_doc.txt b/kernel/branches/Kolibri-acpi/docs/loader_doc.txt index 3134c98ec..ac46890d6 100644 --- a/kernel/branches/Kolibri-acpi/docs/loader_doc.txt +++ b/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/ diff --git a/kernel/branches/Kolibri-acpi/docs/sysfuncr.txt b/kernel/branches/Kolibri-acpi/docs/sysfuncr.txt index 2fbf0fcd8..ac50580ef 100644 --- a/kernel/branches/Kolibri-acpi/docs/sysfuncr.txt +++ b/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 @@ dword- * 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 @@ Architecture Software Developer's Manual, Volume 3, Appendix B); * +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 = слишком много процессов diff --git a/kernel/branches/Kolibri-acpi/docs/sysfuncs.txt b/kernel/branches/Kolibri-acpi/docs/sysfuncs.txt index 20671c334..5cf7c72db 100644 --- a/kernel/branches/Kolibri-acpi/docs/sysfuncs.txt +++ b/kernel/branches/Kolibri-acpi/docs/sysfuncs.txt @@ -2923,9 +2923,9 @@ Remarks: * if one want to read 0 blocks, function considers, that he requested 1; * if one requests more than 14 blocks or starting block is - not less than 14, function returns eax=5 (not found) ebx=-1; + not less than 14, function returns eax=5 (not found) and ebx=-1; * size of ramdisk root folder is 14 blocks, - 0x1C00=7168 ; but function returns ebx=0 + 0x1C00=7168 bytes; but function returns ebx=0 (except of the case of previous item); * strangely enough, it is possible to read 14th block (which generally contains a garbage - I remind, the indexing begins @@ -2994,7 +2994,7 @@ Remarks: data of the concrete partition are required, application must define starting sector of this partition (either directly through MBR, or from the full structure returned by - 11 18). + subfunction 11 of function 18). * Function does not check error code of hard disk, so request of nonexisting sector reads something (most probably it will be zeroes, but this is defined by device) and this is considered @@ -3868,7 +3868,7 @@ Message codes: * in addition dword-image of the register DR6 is given: * bits 0-3: condition of the corresponding breakpoint (set by subfunction 9) is satisfied - * 14: exception has occured because of the trace mode + * bit 14: exception has occured because of the trace mode (flag TF is set TF) * process is suspended When debugger terminates, all debugged processes are killed. @@ -4440,7 +4440,7 @@ Parameters: Format of the information structure: * +0: dword: 7 = subfunction number * +4: dword: flags field: - * 0: start process as debugged + * bit 0: start process as debugged * other bits are reserved and must be set to 0 * +8: dword: 0 or pointer to ASCIIZ-string with parameters * +12 = +0xC: dword: 0 (reserved) diff --git a/kernel/branches/Kolibri-acpi/docs/usbapi.txt b/kernel/branches/Kolibri-acpi/docs/usbapi.txt new file mode 100644 index 000000000..4ae11f140 --- /dev/null +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm b/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm index cd8015078..1a8070e15 100644 --- a/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm +++ b/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm @@ -174,68 +174,68 @@ endp align 4 MSMouseSearch: - ; COM- + ; ПОИСК МЫШИ ЧЕРЕЗ COM-ПОРТЫ MouseSearch: - ; - ; / 1200 + ; Устанавливаем скорость + ; приема/передачи 1200 бод ; in bx COM Port Base Address mov DX, bx add DX, 3 in AL, DX - or AL, 80h ; DLAB + or AL, 80h ;установить бит DLAB out DX, AL mov DX, bx - mov AL, 60h ;1200 + mov AL, 60h ;1200 бод out DX, AL inc DX mov AL, 0 out DX, AL - ; 7 , 1 , - ; + ; Установить длину слова 7 бит, 1 стоповый бит, + ; четность не контролировать mov DX, bx add DX, 3 mov AL, 00000010b out DX, AL - ; + ; Запретить все прерывани mov dx, bx inc dx mov AL, 0 out DX, AL -; , -; MSMouse - ; +; Проверить, что устройство подключено и являетс +; мышью типа MSMouse + ; Отключить питание мыши и прерывани mov DX, bx - add EDX, 4 ; - mov AL, 0 ; DTR, RTS OUT2 + add EDX, 4 ;регистр управления модемом + mov AL, 0 ;сбросить DTR, RTS и OUT2 out DX, AL - ; 5 "" (0,2 ) + ; Ожидать 5 "тиков" (0,2 с) mov ecx, 0xFFFF loop $ - ; + ; Включить питание мыши mov al, 1 out dx, al mov ecx, 0xFFFF loop $ - ; + ; Очистить регистр данных mov dx, bx in AL, DX add edx, 4 - mov AL, 1011b ; DTR RTS OUT2 + mov AL, 1011b ;установить DTR и RTS и OUT2 out DX, AL mov ecx, 0x1FFFF -; +; Цикл опроса порта WaitData: - ; 10 "" + ; Ожидать еще 10 "тиков" dec ecx ; cmp ecx,0 jz NoMouse - ; + ; Проверить наличие идентификационного байта mov DX, bx add DX, 5 in AL, DX - test AL, 1 ; ? + test AL, 1 ;Данные готовы? jz WaitData - ; + ; Ввести данные mov DX, bx in AL, DX NoMouse: @@ -257,41 +257,41 @@ irq_handler: ; in: esi -> COM_MOUSE_DATA struc, dx = base port (xF8h) add edx, 5 ; xFDh in al, dx - test al, 1 ; ? + test al, 1 ; Данные готовы? jz .Error -; +; Ввести данные sub edx, 5 in al, dx -; +; Сбросить старший незначащий бит and al, 01111111b -; +; Определить порядковый номер принимаемого байта cmp [esi+COM_MOUSE_DATA.MouseByteNumber], 2 ja .Error jz .ThirdByte jp .SecondByte -; +; Сохранить первый байт данных .FirstByte: - test al, 1000000b ; ? + test al, 1000000b ; Первый байт посылки? jz .Error mov [esi+COM_MOUSE_DATA.FirstByte], al inc [esi+COM_MOUSE_DATA.MouseByteNumber] jmp .EndMouseInterrupt -; +; Сохранить второй байт данных .SecondByte: test al, 1000000b jnz .Error mov [esi+COM_MOUSE_DATA.SecondByte], al inc [esi+COM_MOUSE_DATA.MouseByteNumber] jmp .EndMouseInterrupt -; +; Сохранить третий байт данных .ThirdByte: test al, 1000000b jnz .Error mov [esi+COM_MOUSE_DATA.ThirdByte], al mov [esi+COM_MOUSE_DATA.MouseByteNumber], 0 -; ( ). -; +; (Пакет данных от мыши принят полностью). +; Записать новое значение состояния кнопок мыши mov al, [esi+COM_MOUSE_DATA.FirstByte] mov ah, al shr al, 3 @@ -302,7 +302,7 @@ irq_handler: movzx eax, al mov [BTN_DOWN], eax -; X X +; Прибавить перемещение по X к координате X mov al, [esi+COM_MOUSE_DATA.FirstByte] shl al, 6 or al, [esi+COM_MOUSE_DATA.SecondByte] @@ -311,7 +311,7 @@ irq_handler: movzx eax, ax mov [MOUSE_X], eax -; Y Y +; Прибавить перемещение по Y к координате Y mov al, [esi+COM_MOUSE_DATA.FirstByte] and al, 00001100b shl al, 4 @@ -327,8 +327,8 @@ irq_handler: jmp .EndMouseInterrupt .Error: -; -; , +; Произошел сбой в порядке передачи информации от +; мыши, обнулить счетчик байтов пакета данных mov [esi+COM_MOUSE_DATA.MouseByteNumber], 0 .EndMouseInterrupt: @@ -340,9 +340,9 @@ irq_handler: align 4 struc COM_MOUSE_DATA { -; +; Номер принимаемого от мыши байта .MouseByteNumber db ? -; , +; Трехбайтовая структура данных, передаваемая мышью .FirstByte db ? .SecondByte db ? .ThirdByte db ? diff --git a/kernel/branches/Kolibri-acpi/drivers/emu10k1x.asm b/kernel/branches/Kolibri-acpi/drivers/emu10k1x.asm index 9b2e52b4c..8d934067f 100644 --- a/kernel/branches/Kolibri-acpi/drivers/emu10k1x.asm +++ b/kernel/branches/Kolibri-acpi/drivers/emu10k1x.asm @@ -19,7 +19,7 @@ IRQ_REMAP equ 0 IRQ_LINE equ 0 -;irq 0,1,2,8,12,13 㯭 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010100000b diff --git a/kernel/branches/Kolibri-acpi/drivers/ensoniq.asm b/kernel/branches/Kolibri-acpi/drivers/ensoniq.asm index abeb7bb57..08dc47ef4 100644 --- a/kernel/branches/Kolibri-acpi/drivers/ensoniq.asm +++ b/kernel/branches/Kolibri-acpi/drivers/ensoniq.asm @@ -17,7 +17,7 @@ include 'imports.inc' REMAP_IRQ equ 0 -;irq 0,1,2,8,12,13 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010101000b diff --git a/kernel/branches/Kolibri-acpi/drivers/fm801.asm b/kernel/branches/Kolibri-acpi/drivers/fm801.asm index 1de7366a3..4316425bf 100644 --- a/kernel/branches/Kolibri-acpi/drivers/fm801.asm +++ b/kernel/branches/Kolibri-acpi/drivers/fm801.asm @@ -17,7 +17,7 @@ API_VERSION equ 0x01000100 USE_COM_IRQ equ 0 ;make irq 3 and irq 4 available for PCI devices -;irq 0,1,2,8,12,13 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010100000b diff --git a/kernel/branches/Kolibri-acpi/drivers/imports.inc b/kernel/branches/Kolibri-acpi/drivers/imports.inc index 90d3ca51b..6ba5fd7de 100644 --- a/kernel/branches/Kolibri-acpi/drivers/imports.inc +++ b/kernel/branches/Kolibri-acpi/drivers/imports.inc @@ -96,7 +96,15 @@ kernel_export \ LFBAddress,\ GetDisplay,\ SetScreen,\ +\ + RegUSBDriver,\ + USBOpenPipe,\ + USBNormalTransferAsync,\ + USBControlTransferAsync,\ \ DiskAdd,\ DiskMediaChanged,\ - DiskDel + DiskDel,\ +\ + TimerHS,\ + CancelTimerHS diff --git a/kernel/branches/Kolibri-acpi/drivers/intelac97.asm b/kernel/branches/Kolibri-acpi/drivers/intelac97.asm index 548835d6e..a73b707be 100644 --- a/kernel/branches/Kolibri-acpi/drivers/intelac97.asm +++ b/kernel/branches/Kolibri-acpi/drivers/intelac97.asm @@ -21,7 +21,7 @@ IRQ_REMAP equ 0 IRQ_LINE equ 0 -;irq 0,1,2,8,12,13 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010100000b diff --git a/kernel/branches/Kolibri-acpi/drivers/sis.asm b/kernel/branches/Kolibri-acpi/drivers/sis.asm index c5bf58307..1c7fc87a0 100644 --- a/kernel/branches/Kolibri-acpi/drivers/sis.asm +++ b/kernel/branches/Kolibri-acpi/drivers/sis.asm @@ -21,7 +21,7 @@ IRQ_REMAP equ 0 IRQ_LINE equ 0 -;irq 0,1,2,8,12,13 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010100000b diff --git a/kernel/branches/Kolibri-acpi/drivers/vt823x.asm b/kernel/branches/Kolibri-acpi/drivers/vt823x.asm index 51f4e3dc3..c6df0d0fd 100644 --- a/kernel/branches/Kolibri-acpi/drivers/vt823x.asm +++ b/kernel/branches/Kolibri-acpi/drivers/vt823x.asm @@ -19,7 +19,7 @@ IRQ_REMAP equ 0 IRQ_LINE equ 0 -;irq 0,1,2,8,12,13 㯭 +;irq 0,1,2,8,12,13 недоступны ; FEDCBA9876543210 VALID_IRQ equ 1100111011111000b ATTCH_IRQ equ 0000111010100000b diff --git a/kernel/branches/Kolibri-acpi/encoding.inc b/kernel/branches/Kolibri-acpi/encoding.inc new file mode 100644 index 000000000..cdf1e13f2 --- /dev/null +++ b/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 +} diff --git a/kernel/branches/Kolibri-acpi/fs/fat12.inc b/kernel/branches/Kolibri-acpi/fs/fat12.inc index c6ca94fee..8f2685146 100644 --- a/kernel/branches/Kolibri-acpi/fs/fat12.inc +++ b/kernel/branches/Kolibri-acpi/fs/fat12.inc @@ -119,9 +119,9 @@ frfl6_1: call read_flp_fat cmp [FDC_Status], 0 jne fdc_status_error_3_1 - mov [FDD_Track], 0; - mov [FDD_Head], 1; - mov [FDD_Sector], 2; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 1; Сторона + mov [FDD_Sector], 2; Сектор call SeekTrack mov dh, 14 l.20_1: @@ -253,9 +253,9 @@ read_flp_root: jne unnecessary_root_read cmp [root_read], 1 je unnecessary_root_read - mov [FDD_Track], 0; - mov [FDD_Head], 1; - mov [FDD_Sector], 2; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 1; Сторона + mov [FDD_Sector], 2; Сектор mov edi, FLOPPY_BUFF call SeekTrack read_flp_root_1: @@ -282,9 +282,9 @@ read_flp_fat: jne unnecessary_flp_fat cmp [flp_fat], 1 je unnecessary_flp_fat - mov [FDD_Track], 0; - mov [FDD_Head], 0; - mov [FDD_Sector], 2; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 2; Сектор mov edi, FLOPPY_BUFF call SeekTrack read_flp_fat_1: @@ -351,9 +351,9 @@ calculatefatchain_flp: check_label: pushad - mov [FDD_Track], 0; - mov [FDD_Head], 0; - mov [FDD_Sector], 1; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 1; Сектор call SetUserInterrupts call FDDMotorON call RecalibrateFDD @@ -392,9 +392,9 @@ save_flp_root: jne unnecessary_root_save cmp [root_read], 0 je unnecessary_root_save - mov [FDD_Track], 0; - mov [FDD_Head], 1; - mov [FDD_Sector], 2; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 1; Сторона + mov [FDD_Sector], 2; Сектор mov esi, FLOPPY_BUFF call SeekTrack save_flp_root_1: @@ -421,9 +421,9 @@ save_flp_fat: cmp [flp_fat], 0 je unnecessary_flp_fat_save call restorefatchain_flp - mov [FDD_Track], 0; - mov [FDD_Head], 0; - mov [FDD_Sector], 2; + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 2; Сектор mov esi, FLOPPY_BUFF call SeekTrack save_flp_fat_1: diff --git a/kernel/branches/Kolibri-acpi/fs/fs.inc b/kernel/branches/Kolibri-acpi/fs/fs.inc index d5f043e8d..afaf2c47c 100644 --- a/kernel/branches/Kolibri-acpi/fs/fs.inc +++ b/kernel/branches/Kolibri-acpi/fs/fs.inc @@ -441,7 +441,7 @@ hd_err_return: fs_noharddisk: ; \begin{diamond}[18.03.2006] mov eax, 5 ; file not found -; , ? +; а может быть, возвращать другой код ошибки? mov ebx, [esp+24+24]; do not change ebx in application ; \end{diamond}[18.03.2006] @@ -713,14 +713,14 @@ expand_pathz: ;* output eax - number ;******************************************* StringToNumber: -; -; : -; EDI - . 0Dh -; : -; CF - : -; 0 - ; -; 1 - -; CF=0, AX - . +; ПЕРЕВОД СТРОКОВОГО ЧИСЛА В ЧИСЛОВОЙ ВИД +; Вход: +; EDI - адрес строки с числом. Конец числа отмечен кодом 0Dh +; Выход: +; CF - индикатор ошибок: +; 0 - ошибок нет; +; 1 - ошибка +; Если CF=0, то AX - число. push bx push cx diff --git a/kernel/branches/Kolibri-acpi/fs/iso9660.inc b/kernel/branches/Kolibri-acpi/fs/iso9660.inc index 08462771b..6d9dc4c27 100644 --- a/kernel/branches/Kolibri-acpi/fs/iso9660.inc +++ b/kernel/branches/Kolibri-acpi/fs/iso9660.inc @@ -137,7 +137,7 @@ fs_CdRead: .l1: push ecx edx push 0 - mov eax, [edi+10] ; + mov eax, [edi+10] ; реальный размер файловой секции sub eax, ebx jb .eof cmp eax, ecx @@ -159,7 +159,7 @@ fs_CdRead: jb .incomplete_sector ; we may read and memmove complete sector mov [CDDataBuf_pointer], edx - call ReadCDWRetr; + call ReadCDWRetr; читаем сектор файла cmp [DevErrorCode], 0 jne .noaccess_3 add edx, 2048 @@ -170,7 +170,7 @@ fs_CdRead: .incomplete_sector: ; we must read and memmove incomplete sector mov [CDDataBuf_pointer], CDDataBuf - call ReadCDWRetr; + call ReadCDWRetr; читаем сектор файла cmp [DevErrorCode], 0 jne .noaccess_3 push ecx @@ -240,7 +240,7 @@ fs_CdReadFolder: .found_dir: mov eax, [edi+2] ; eax=cluster mov [CDSectorAddress], eax - mov eax, [edi+10] ; + mov eax, [edi+10] ; размер директрории .doit: ; init header push eax ecx @@ -252,7 +252,7 @@ fs_CdReadFolder: mov byte [edx], 1 ; version mov [cd_mem_location], edx add [cd_mem_location], 32 -; +; начинаем переброску БДВК в УСВК ;.mainloop: mov [cd_counter_block], dword 0 dec dword [CDSectorAddress] @@ -260,12 +260,12 @@ fs_CdReadFolder: .read_to_buffer: inc dword [CDSectorAddress] mov [CDDataBuf_pointer], CDDataBuf - call ReadCDWRetr ; + call ReadCDWRetr ; читаем сектор директории cmp [DevErrorCode], 0 jne .noaccess_1 call .get_names_from_buffer sub eax, 2048 -; ? +; директория закончилась? ja .read_to_buffer mov edi, [cd_counter_block] mov [edx+8], edi @@ -310,17 +310,17 @@ fs_CdReadFolder: call uni2ansi_char cld stosb -; +; проверка конца файла mov ax, [esi] - cmp ax, word 3B00h; ';' + cmp ax, word 3B00h; сепаратор конца файла ';' je .cd_get_parameters_of_file_1 -; +; проверка для файлов не заканчивающихся сепаратором movzx eax, byte [ebp-33] add eax, ebp sub eax, 34 cmp esi, eax je .cd_get_parameters_of_file_1 -; +; проверка конца папки movzx eax, byte [ebp-1] add eax, ebp cmp esi, eax @@ -347,17 +347,17 @@ fs_CdReadFolder: jbe .unicode_parent_directory cld movsw -; +; проверка конца файла mov ax, [esi] - cmp ax, word 3B00h; ';' + cmp ax, word 3B00h; сепаратор конца файла ';' je .cd_get_parameters_of_file_2 -; +; проверка для файлов не заканчивающихся сепаратором movzx eax, byte [ebp-33] add eax, ebp sub eax, 34 cmp esi, eax je .cd_get_parameters_of_file_2 -; +; проверка конца папки movzx eax, byte [ebp-1] add eax, ebp cmp esi, eax @@ -386,60 +386,60 @@ fs_CdReadFolder: cd_get_parameters_of_file: mov edi, [cd_mem_location] cd_get_parameters_of_file_1: -; +; получаем атрибуты файла xor eax, eax -; +; файл не архивировался inc eax shl eax, 1 -; ? +; это каталог? test [ebp-8], byte 2 jz .file inc eax .file: -; FAT, -; +; метка тома не как в FAT, в этом виде отсутсвует +; файл не является системным shl eax, 3 -; ? ( ) +; файл является скрытым? (атрибут существование) test [ebp-8], byte 1 jz .hidden inc eax .hidden: shl eax, 1 -; , CD +; файл всегда только для чтения, так как это CD inc eax mov [edi], eax -; -; +; получаем время для файла +;час movzx eax, byte [ebp-12] shl eax, 8 -; +;минута mov al, [ebp-11] shl eax, 8 -; +;секунда mov al, [ebp-10] -; +;время создания файла mov [edi+8], eax -; +;время последнего доступа mov [edi+16], eax -; +;время последней записи mov [edi+24], eax -; -; +; получаем дату для файла +;год movzx eax, byte [ebp-15] add eax, 1900 shl eax, 8 -; +;месяц mov al, [ebp-14] shl eax, 8 -; +;день mov al, [ebp-13] -; +;дата создания файла mov [edi+12], eax -; +;время последнего доступа mov [edi+20], eax -; +;время последней записи mov [edi+28], eax -; +; получаем тип данных имени xor eax, eax test dword [ebx+4], 1; 0=ANSI, 1=UNICODE jnz .unicode_1 @@ -449,7 +449,7 @@ cd_get_parameters_of_file_1: inc eax mov [edi+4], eax @@: -; +; получаем размер файла в байтах xor eax, eax mov [edi+32+4], eax mov eax, [ebp-23] @@ -503,21 +503,21 @@ cd_find_lfn: ; out: CF=1 - file not found ; else CF=0 and [cd_current_pointer_of_input] direntry push eax esi -; 16 +; 16 сектор начало набора дескрипторов томов call WaitUnitReady cmp [DevErrorCode], 0 jne .access_denied call prevent_medium_removal -; +; тестовое чтение mov [CDSectorAddress], dword 16 mov [CDDataBuf_pointer], CDDataBuf call ReadCDWRetr;_1 cmp [DevErrorCode], 0 jne .access_denied -; +; вычисление последней сессии call WaitUnitReady cmp [DevErrorCode], 0 jne .access_denied @@ -539,37 +539,37 @@ cd_find_lfn: jne .access_denied .start_check: -; +; проверка на вшивость cmp [CDDataBuf+1], dword 'CD00' jne .access_denied cmp [CDDataBuf+5], byte '1' jne .access_denied -; ? +; сектор является терминатором набор дескрипторов томов? cmp [CDDataBuf], byte 0xff je .access_denied -; ? +; сектор является дополнительным и улучшенным дескриптором тома? cmp [CDDataBuf], byte 0x2 jne .start -; ? +; сектор является дополнительным дескриптором тома? cmp [CDDataBuf+6], byte 0x1 jne .start -; root - mov eax, [CDDataBuf+0x9c+2]; root +; параметры root директрории + mov eax, [CDDataBuf+0x9c+2]; начало root директрории mov [CDSectorAddress], eax - mov eax, [CDDataBuf+0x9c+10]; root + mov eax, [CDDataBuf+0x9c+10]; размер root директрории cmp byte [esi], 0 jnz @f mov [cd_current_pointer_of_input], CDDataBuf+0x9c jmp .done @@: -; +; начинаем поиск .mainloop: dec dword [CDSectorAddress] .read_to_buffer: inc dword [CDSectorAddress] mov [CDDataBuf_pointer], CDDataBuf - call ReadCDWRetr ; + call ReadCDWRetr ; читаем сектор директории cmp [DevErrorCode], 0 jne .access_denied push ebp @@ -577,27 +577,27 @@ cd_find_lfn: pop ebp jnc .found sub eax, 2048 -; ? +; директория закончилась? cmp eax, 0 ja .read_to_buffer -; +; нет искомого элемента цепочки .access_denied: pop esi eax mov [cd_appl_data], 1 stc ret -; +; искомый элемент цепочки найден .found: -; +; конец пути файла cmp byte [esi-1], 0 jz .done .nested: mov eax, [cd_current_pointer_of_input] push dword [eax+2] - pop dword [CDSectorAddress] ; - mov eax, [eax+2+8]; + pop dword [CDSectorAddress] ; начало директории + mov eax, [eax+2+8]; размер директории jmp .mainloop -; +; указатель файла найден .done: test ebp, ebp jz @f @@ -629,13 +629,13 @@ cd_get_name: mov ebp, [cd_current_pointer_of_input_2] mov [cd_current_pointer_of_input], ebp mov eax, [ebp] - test eax, eax ; ? + test eax, eax ; входы закончились? jz .next_sector - cmp ebp, CDDataBuf+2048 ; ? + cmp ebp, CDDataBuf+2048 ; буфер закончился? jae .next_sector movzx eax, byte [ebp] - add [cd_current_pointer_of_input_2], eax; - add ebp, 33; + add [cd_current_pointer_of_input_2], eax; следующий вход каталога + add ebp, 33; указатель установлен на начало имени pop eax clc ret @@ -669,9 +669,9 @@ cd_compare_name: scasw jne .name_not_coincide .coincides: - cmp [esi], byte '/'; , + cmp [esi], byte '/'; разделитель пути, конец имени текущего элемента je .done - cmp [esi], byte 0; , + cmp [esi], byte 0; разделитель пути, конец имени текущего элемента je .done jmp .loop .name_not_coincide: @@ -679,16 +679,16 @@ cd_compare_name: stc ret .done: -; - cmp [edi], word 3B00h; ';' +; проверка конца файла + cmp [edi], word 3B00h; сепаратор конца файла ';' je .done_1 -; +; проверка для файлов не заканчивающихся сепаратором movzx eax, byte [ebp-33] add eax, ebp sub eax, 34 cmp edi, eax je .done_1 -; +; проверка конца папки movzx eax, byte [ebp-1] add eax, ebp cmp edi, eax @@ -708,14 +708,14 @@ char_todown: jb .ret cmp al, 'Z' jbe .az - cmp al, '' + cmp al, 0x80 ; 'А' jb .ret - cmp al, '' + cmp al, 0x90 ; 'Р' jb .rus1 - cmp al, '' + cmp al, 0x9F ; 'Я' ja .ret ; 0x90-0x9F -> 0xE0-0xEF - add al, ''-'' + add al, 0xE0-0x90 .ret: ret .rus1: @@ -744,10 +744,10 @@ uni2ansi_char: mov al, '_' jmp .doit .yo1: - mov al, '' + mov al, 0xF0 ; 'Ё' in cp866 jmp .doit .yo2: - mov al, '' + mov al, 0xF1 ; 'ё' in cp866 jmp .doit .rus1: ; 0x410-0x43F -> 0x80-0xAF diff --git a/kernel/branches/Kolibri-acpi/fs/part_set.inc b/kernel/branches/Kolibri-acpi/fs/part_set.inc index 955ba5beb..5abce1ed3 100644 --- a/kernel/branches/Kolibri-acpi/fs/part_set.inc +++ b/kernel/branches/Kolibri-acpi/fs/part_set.inc @@ -77,10 +77,10 @@ ext2_data: .inode_size dd ? .count_pointer_in_block dd ? ; block_size / 4 .count_pointer_in_block_square dd ? ; (block_size / 4)**2 - .ext2_save_block dd ? ; 1 楤 - .ext2_temp_block dd ? ; 楤 - .ext2_save_inode dd ? ; inode 楤 - .ext2_temp_inode dd ? ; inode 楤 + .ext2_save_block dd ? ; блок на глобальную 1 процедуру + .ext2_temp_block dd ? ; блок для мелких процедур + .ext2_save_inode dd ? ; inode на глобальную процедуру + .ext2_temp_inode dd ? ; inode для мелких процедур .sb dd ? ; superblock .groups_count dd ? if $ > fs_dependent_data_end @@ -278,7 +278,7 @@ test_primary_partition_3: ;pop edx test_ext_partition_0: - pop eax ; 모뢠 ⥪ + pop eax ; просто выкидываем из стека mov al, [ebx+0x1be+4]; get extended partition type call scan_extended_types jnz test_ext_partition_1 @@ -368,7 +368,7 @@ hd_and_partition_ok: ; mov edx, [PARTITION_END] ; sub edx, eax - ; inc edx ; edx = length of partition 祬 ?? + ; inc edx ; edx = length of partition зачем оно нам?? ; mov [hd_setup],1 mov ebx, buffer diff --git a/kernel/branches/Kolibri-acpi/gui/event.inc b/kernel/branches/Kolibri-acpi/gui/event.inc index e336544ab..70d433461 100644 --- a/kernel/branches/Kolibri-acpi/gui/event.inc +++ b/kernel/branches/Kolibri-acpi/gui/event.inc @@ -21,8 +21,8 @@ align 4 event_uid dd 0 endg EV_SPACE = 512 -FreeEvents = event_start-EVENT.fd ; "" event, : - ; FreeEvents.fd=event_start FreeEvents.bk=event_end +FreeEvents = event_start-EVENT.fd ; "виртуальный" event, используются только поля: + ; FreeEvents.fd=event_start и FreeEvents.bk=event_end ;----------------------------------------------------------------------------- align 4 init_events: ;; used from kernel.asm @@ -31,8 +31,8 @@ init_events: ;; used from kernel.asm jz .fail ; eax - current event, ebx - previos event below mov ecx, EV_SPACE ; current - in allocated space - mov ebx, FreeEvents ; previos - - push ebx ; + mov ebx, FreeEvents ; previos - начало списка + push ebx ; оно же и конец потом будет ;-------------------------------------- align 4 @@: @@ -41,7 +41,7 @@ align 4 mov ebx, eax ; previos <- current add eax, sizeof.EVENT ; new current loop @b - pop eax ; + pop eax ; вот оно концом и стало mov [ebx+EVENT.fd], eax mov [eax+EVENT.bk], ebx ;-------------------------------------- @@ -49,16 +49,16 @@ align 4 .fail: ret ;----------------------------------------------------------------------------- -EVENT_WATCHED equ 0x10000000 ; 28 -EVENT_SIGNALED equ 0x20000000 ; 29 -MANUAL_RESET equ 0x40000000 ; 30 -MANUAL_DESTROY equ 0x80000000 ; 31 +EVENT_WATCHED equ 0x10000000 ;бит 28 +EVENT_SIGNALED equ 0x20000000 ;бит 29 +MANUAL_RESET equ 0x40000000 ;бит 30 +MANUAL_DESTROY equ 0x80000000 ;бит 31 ;----------------------------------------------------------------------------- align 4 create_event: ;; EXPORT use ;info: -; EVENT FreeEvents ObjList -; EVENT.state ecx, EVENT.code esi ( esi<>0) +; Переносим EVENT из списка FreeEvents в список ObjList текущего слота +; EVENT.state устанавливаем из ecx, EVENT.code косвенно из esi (если esi<>0) ;param: ; esi - event data ; ecx - flags @@ -76,9 +76,9 @@ create_event: ;; EXPORT use align 4 set_event: ;; INTERNAL use !!! don't use for Call ;info: -; event FreeEvents, , ecx,edx,esi -; , ebx. -; event ( eax), uid ( edx) +; Берем новый event из FreeEvents, заполняем его поля, как указано в ecx,edx,esi +; и устанавливаем в список, указанный в ebx. +; Возвращаем сам event (в eax), и его uid (в edx) ;param: ; ebx - start-chain "virtual" event for entry new event Right of him ; ecx - flags (copied to EVENT.state) @@ -115,13 +115,13 @@ align 4 align 4 RemoveEventTo: ;; INTERNAL use !!! don't use for Call ;param: -; eax - event, -; ebx - event, +; eax - указатель на event, КОТОРЫЙ вставляем +; ebx - указатель на event, ПОСЛЕ которого вставляем ;scratched: ebx,ecx mov ecx, eax ; ecx=eax=Self, ebx=NewLeft xchg ecx, [ebx+EVENT.fd] ; NewLeft.fd=Self, ecx=NewRight - cmp eax, ecx ; , ... - je .break ; - ? + cmp eax, ecx ; стоп, себе думаю... + je .break ; - а не дурак ли я? mov [ecx+EVENT.bk], eax ; NewRight.bk=Self xchg ebx, [eax+EVENT.bk] ; Self.bk=NewLeft, ebx=OldLeft xchg ecx, [eax+EVENT.fd] ; Self.fd=NewRight, ecx=OldRight @@ -142,22 +142,22 @@ NotDummyTest: ;; INTERNAL use (not returned push edi ;-------------------------------------- align 4 -.small: ; -... +.small: ; криво как-то... pop edi pushfd cli call pid_to_slot ; saved all registers (eax - retval) shl eax, 8 jz RemoveEventTo.break ; POPF+RET - jmp edi ; + jmp edi ; штатный возврат ;----------------------------------------------------------------------------- align 4 raise_event: ;; EXPORT use ;info: -; EVENT.code -; EVENT_SIGNALED - -; : , EVENT_WATCHED edx -; EVENT_SIGNALED EVENT_WATCHED +; Устанавливаем данные EVENT.code +; Если там флаг EVENT_SIGNALED уже активен - больше ничего +; Иначе: этот флаг взводится, за исключением случая наличия флага EVENT_WATCHED в edx +; В этом случае EVENT_SIGNALED взводится лишь при наличие EVENT_WATCHED в самом событии ;param: ; eax - event ; ebx - uid (for Dummy testing) @@ -165,7 +165,6 @@ raise_event: ;; EXPORT use ; 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 @@ clear_event: ;; EXPORT use align 4 send_event: ;; EXPORT use ;info: -; EVENT ( FreeEvents) EventList -; (eax=pid), esi , state=EVENT_SIGNALED +; Создает новый EVENT (вытаскивает из списка FreeEvents) в списке EventList +; целевого слота (eax=pid), с данными из esi косвенно, и state=EVENT_SIGNALED ;param: ; eax - slots pid, to sending new event ; esi - pointer to sending data (in code field of new event) @@ -252,24 +251,24 @@ Wait_events: align 4 Wait_events_ex: ;info: -; "" 5- . -; , APPDATA.wait_test, -; . -; shed- , "" , -; "/" . +; Ожидание "абстрактного" события через перевод слота в 5-ю позицию. +; Абстрактность заключена в том, что факт события определяется функцией APPDATA.wait_test, +; которая задается клиентом и может быть фактически любой. +; Это позволяет shed-у надежно определить факт события, и не совершать "холостых" переключений, +; предназначенных для разборок типа "свой/чужой" внутри задачи. ;param: -; edx - wait_test, - ( ) -; ecx - wait_param, , [wait_test] +; edx - wait_test, клиентская ф-я тестирования (адрес кода) +; ecx - wait_param, дополнительный параметр, возможно необходимый для [wait_test] ; ebx - wait_timeout ;retval: -; eax - [wait_test] (=0 => timeout) +; eax - результат вызова [wait_test] (=0 => timeout) ;scratched: esi mov esi, [current_slot] mov [esi+APPDATA.wait_param], ecx pushad - mov ebx, esi; , .......... - pushfd ; : - - cli ; , shed + mov ebx, esi;пока это вопрос, чего куды сувать.......... + pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет + cli ; право рассчитывать на закрытые прерывания, как при вызове из shed call edx popfd mov [esp+28], eax @@ -291,12 +290,12 @@ align 4 align 4 wait_event: ;; EXPORT use ;info: -; EVENT_SIGNALED Event -; (, , raise_event) -; MANUAL_RESET - -; : EVENT_SIGNALED EVENT_WATCHED , -; , MANUAL_DESTROY - ObjList , -; - (destroy_event.internal) +; Ожидание флага EVENT_SIGNALED в совершенно конкретном Event +; (устанавливаемого, надо полагать, через raise_event) +; При активном флаге MANUAL_RESET - больше ничего +; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются, +; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота, +; а при не активном - уничтожается штатно (destroy_event.internal) ;param: ; eax - event ; ebx - uid (for Dummy testing) @@ -327,16 +326,16 @@ wait_event_timeout: align 4 get_event_ex: ;; f68:14 ;info: -; EventList -; code - ( edi) -; MANUAL_RESET - -; : EVENT_SIGNALED EVENT_WATCHED , -; , MANUAL_DESTROY - ObjList , -; - (destroy_event.internal) +; Ожидание любого события в очереди EventList текущего слота +; Данные события code - копируются в память приложения (косвенно по edi) +; При активном флаге MANUAL_RESET - больше ничего +; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются, +; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота, +; а при не активном - уничтожается штатно (destroy_event.internal) ;param: -; edi - EVENT.code +; edi - адрес в коде приложения для копирования данных из EVENT.code ;retval: -; eax - EVENT ( ) +; eax - собственно EVENT (будем называть это его хэндлом) ;scratched: ebx,ecx,edx,esi,edi mov edx, get_event_queue ; wait_test call Wait_events ; timeout ignored @@ -362,12 +361,12 @@ wait_finish: align 4 destroy_event: ;; EXPORT use ;info: -; EVENT FreeEvents, magic,destroy,pid,id +; Переносим EVENT в список FreeEvents, чистим поля magic,destroy,pid,id ;param: ; eax - event ; ebx - uid (for Dummy testing) ;retval: -; eax - EVENT (=0 => fail) +; eax - адрес объекта EVENT (=0 => fail) ;scratched: ebx,ecx call DummyTest ; not returned for fail !!! ;-------------------------------------- @@ -386,17 +385,17 @@ align 4 align 4 get_event_queue: ;info: -; - get_event_ex +; клиентская ф-я тестирования для get_event_ex ;warning: ; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot ; -may be assumed, that interrupt are disabled ; -it is not restriction for scratched registers ;param: -; ebx - APPDATA +; ebx - адрес APPDATA слота тестирования ;retval: -; eax - EVENT (=0 => fail) +; eax - адрес объекта EVENT (=0 => fail) add ebx, APP_EV_OFFSET - mov eax, [ebx+APPOBJ.bk] ; , FIFO + mov eax, [ebx+APPOBJ.bk] ; выбираем с конца, по принципу FIFO cmp eax, ebx ; empty ??? je get_event_alone.ret0 ;-------------------------------------- @@ -407,15 +406,15 @@ align 4 align 4 get_event_alone: ;info: -; - wait_event +; клиентская ф-я тестирования для wait_event ;warning: ; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot ; -may be assumed, that interrupt are disabled ; -it is not restriction for scratched registers ;param: -; ebx - APPDATA +; ebx - адрес APPDATA слота тестирования ;retval: -; eax - EVENT (=0 => fail) +; eax - адрес объекта EVENT (=0 => fail) mov eax, [ebx+APPDATA.wait_param] test byte[eax+EVENT.state+3], EVENT_SIGNALED shr 24 jnz .ret @@ -459,7 +458,7 @@ align 4 ;-------------------------------------- align 4 .result: - setae byte[esp+32+4] ;, : dword[esp+32+4]==72 + setae byte[esp+32+4] ;считаем, что исходно: dword[esp+32+4]==72 ;-------------------------------------- align 4 .retf: @@ -471,9 +470,9 @@ align 4 ;----------------------------------------------------------------------------- align 4 sys_getevent: ;; f11 - mov ebx, [current_slot]; , .......... - pushfd ; : - - cli ; , shed + mov ebx, [current_slot];пока это вопрос, чего куды сувать.......... + pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет + cli ; право рассчитывать на закрытые прерывания, как при вызове из shed call get_event_for_app popfd mov [esp+32], eax @@ -495,15 +494,15 @@ sys_wait_event_timeout: ;; f23 align 4 get_event_for_app: ;; used from f10,f11,f23 ;info: -; - (f10,f23) +; клиентская ф-я тестирования для приложений (f10,f23) ;warning: ; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot ; -may be assumed, that interrupt are disabled ; -it is not restriction for scratched registers ;param: -; ebx - APPDATA +; ebx - адрес APPDATA слота тестирования ;retval: -; eax - (=0 => no events) +; eax - номер события (=0 => no events) movzx edi, bh ; bh is assumed as [CURRENT_TASK] shl edi, 5 add edi, CURRENT_TASK ; edi is assumed as [TASK_BASE] @@ -511,13 +510,13 @@ get_event_for_app: ;; used from f10,f11,f23 and ecx, 0x7FFFFFFF ;-------------------------------------- align 4 -.loop: ; - bsr eax, ecx ; (31 -> 0) - jz .no_events ; , ??? - btr ecx, eax ; - ; (eax) - 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 @@ align 4 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 @@ align 4 cmp edx, 0xFFFF ;-ID for Minimize-Button of Form jne .result mov [window_minimize], 1 + call wakeup_osloop dec byte[BTN_COUNT] jmp .loop ;-------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/gui/mouse.inc b/kernel/branches/Kolibri-acpi/gui/mouse.inc index 7977b4a80..e5ff99318 100644 --- a/kernel/branches/Kolibri-acpi/gui/mouse.inc +++ b/kernel/branches/Kolibri-acpi/gui/mouse.inc @@ -66,13 +66,9 @@ mouse_check_events: ;////////////////////////////////////////////////////////// ; NOTE: this code wouldn't be necessary if we knew window did ; already redraw itself after call above or eax, eax - jz @f - - and [mouse.state.buttons], 0 - jmp .exit + jnz .exit ; is there any system button under cursor? - @@: call mouse._.find_sys_button_under_cursor or eax, eax jz .check_buttons_released diff --git a/kernel/branches/Kolibri-acpi/gui/window.inc b/kernel/branches/Kolibri-acpi/gui/window.inc index 307a9a77b..d4ee215a1 100644 --- a/kernel/branches/Kolibri-acpi/gui/window.inc +++ b/kernel/branches/Kolibri-acpi/gui/window.inc @@ -992,7 +992,6 @@ waredraw: ;//////////////////////////////////////////////////////////////////// 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 @@ align 4 ; 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 @@ align 4 add edx, ebx call ebp inc [_display.mask_seqno] - mov byte[MOUSE_BACKGROUND], 0 ;-------------------------------------- align 4 .exit: @@ -1629,6 +1624,7 @@ window._.sys_set_window: ;///////////////////////////////////////////////////// ; 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] diff --git a/kernel/branches/Kolibri-acpi/hid/keyboard.inc b/kernel/branches/Kolibri-acpi/hid/keyboard.inc index 1ec9016e8..1543e5065 100644 --- a/kernel/branches/Kolibri-acpi/hid/keyboard.inc +++ b/kernel/branches/Kolibri-acpi/hid/keyboard.inc @@ -339,6 +339,7 @@ send_scancode: jne .noctrlaltdel mov [ctrl_alt_del], 1 + call wakeup_osloop .noctrlaltdel: test dl, VKEY_CONTROL ; ctrl on ? jz @f @@ -490,7 +491,6 @@ send_scancode: mov [KEY_COUNT], al mov [KEY_COUNT+eax], bl .exit.irq1: - mov [check_idle_semaphore], 5 ret ;--------------------------------------------------------------------- set_lights: @@ -526,9 +526,14 @@ ps2_set_lights: 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 diff --git a/kernel/branches/Kolibri-acpi/hid/mousedrv.inc b/kernel/branches/Kolibri-acpi/hid/mousedrv.inc index 981b4bff2..25e17c1d4 100644 --- a/kernel/branches/Kolibri-acpi/hid/mousedrv.inc +++ b/kernel/branches/Kolibri-acpi/hid/mousedrv.inc @@ -494,9 +494,9 @@ proc set_mouse_data stdcall, BtnState:dword, XMoving:dword, YMoving:dword, VScro ;-------------------------------------- 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 ;-------------------------------------- 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 @@ align 4 mov [mouse_active], 1 mov eax, [timer_ticks] mov [mouse_timer_ticks], eax + call wakeup_osloop ret endp ;----------------------------------------------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/init.inc b/kernel/branches/Kolibri-acpi/init.inc index d05476317..4aca7924c 100644 --- a/kernel/branches/Kolibri-acpi/init.inc +++ b/kernel/branches/Kolibri-acpi/init.inc @@ -310,7 +310,7 @@ init_BIOS32: test al, al jnz .PCI_BIOS32_not_found - ; PCI BIOS + ; здесь создаются дискрипторы для PCI BIOS add ebx, OS_BASE dec ecx @@ -334,7 +334,7 @@ init_BIOS32: mov [(pci_bios_entry-OS_BASE)], edx ; jmp .end .PCI_BIOS32_not_found: - ; pci_emu_dat + ; здесь должна заполнятся pci_emu_dat .BIOS32_not_found: .end: ret diff --git a/kernel/branches/Kolibri-acpi/kernel.asm b/kernel/branches/Kolibri-acpi/kernel.asm index 59c569129..54e35010f 100644 --- a/kernel/branches/Kolibri-acpi/kernel.asm +++ b/kernel/branches/Kolibri-acpi/kernel.asm @@ -77,11 +77,14 @@ $Revision$ 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 "boot/bootcode.inc" ; 16 bit system boot code 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 @@ high_code: 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 @@ high_code: 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 @@ high_code: 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 + movzx eax, word[BOOT_VAR+BOOT_PITCH] ; for other modes + mov [BytesPerScanLine], eax mov [_display.pitch], eax @@: mov eax, [_display.width] @@ -444,7 +475,7 @@ high_code: 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 @@ no_mode_0x12: ; !!!! 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 @@ no_mode_0x12: 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 @@ no_mode_0x12: ;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 @@ no_mode_0x12: 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,55 +642,29 @@ no_mode_0x12: 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 @@ -801,6 +798,10 @@ end if 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,39 +823,40 @@ end if 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 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 @@ end if 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 @@ first_app_found: 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 @@ end if @@: 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 @@ end if mov [timer_ticks_enable], 1 ; for cd driver sti - call change_task +; call change_task jmp osloop @@ -1092,6 +1082,52 @@ boot_log: 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,14 +1135,21 @@ boot_log: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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 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,37 +1160,45 @@ osloop: ; 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 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1195,7 +1246,7 @@ reserve_irqs_ports: iglobal - process_number dd 0x1 + process_number dd 0x2 endg set_variables: @@ -1212,17 +1263,17 @@ set_variables: 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 @@ -1991,6 +2042,13 @@ sys_end: call restore_default_cursor_before_killing 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] @@ -2002,6 +2060,7 @@ sys_end: 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 @@ restore_default_cursor_before_killing: mov [current_cursor], esi @@: mov [redrawmouse_unconditional], 1 + call wakeup_osloop popfd ret ;------------------------------------------------------------------------------ @@ -2087,6 +2147,7 @@ sysfn_shutdown: ; 18.9 = system shutdown mov eax, [TASK_COUNT] mov [SYS_SHUTDOWN], al mov [shutdown_processes], eax + call wakeup_osloop and dword [esp+32], 0 exit_for_anyone: ret @@ -2114,6 +2175,12 @@ sysfn_terminate: ; 18.2 = TERMINATE pop edx ecx 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 @@ -2132,11 +2199,12 @@ sysfn_terminate: ; 18.2 = TERMINATE ;-------------------------------------- ;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 @@ noprocessterminate: 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 @@ sysfn_terminate2: 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 @@ sysfn_deactivate: ; 18.1 = DEACTIVATE WINDOW 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 @@ sysfn_activate: ; 18.3 = ACTIVATE WINDOW @@: ;------------------------------------- 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 @@ sysfn_activate: ; 18.3 = ACTIVATE WINDOW 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_sound_flag: ; 18.8 = get/set sound_flag ;------------------------------------------------------------------------------ sysfn_minimize: ; 18.10 = minimize window mov [window_minimize], 1 + call wakeup_osloop ret ;------------------------------------------------------------------------------ align 4 @@ -2333,6 +2391,7 @@ sysfn_centermouse: ; 18.15 = mouse centered 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 @@ sysfn_mouse_acceleration: ; 18.19 = set/get mouse features 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 @@ sysfn_mouse_acceleration: ; 18.19 = set/get mouse features jnz .end mov [BTN_DOWN], dl mov [mouse_active], 1 + call wakeup_osloop .end: ret ;------------------------------------------------------------------------------ @@ -2799,9 +2860,12 @@ nosb7: 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 @@ nosb8: mov [draw_data+32 + RECT.bottom], edx inc byte[REDRAW_BACKGROUND] + call wakeup_osloop ;-------------------------------------- align 4 .exit: @@ -2869,6 +2934,7 @@ force_redraw_background: mov [draw_data+32 + RECT.bottom], ebx pop ebx eax inc byte[REDRAW_BACKGROUND] + call wakeup_osloop ret ;------------------------------------------------------------------------------ align 4 @@ -2878,7 +2944,7 @@ sys_getbackground: jnz nogb1 mov eax, [BgrDataWidth] shl eax, 16 - mov ax, [BgrDataHeight] + mov ax, word [BgrDataHeight] mov [esp+32], eax ret ;------------------------------------------------------------------------------ @@ -3294,8 +3360,8 @@ sys_sheduler: ;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 @@ mouse_not_active: 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 @@ align 4 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 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 @@ markz: @@: add edx, 0x20 loop markz + call wakeup_osloop ;-------------------------------------- align 4 @@: @@ -3678,6 +3769,7 @@ align 4 align 4 @@: add byte[REDRAW_BACKGROUND], dl + call wakeup_osloop jmp newdw8 ;-------------------------------------- align 4 @@ -3699,6 +3791,7 @@ align 4 cmp dword [esp], 1 jne nobgrd inc byte[REDRAW_BACKGROUND] + call wakeup_osloop ;-------------------------------------- align 4 newdw8: @@ -4670,10 +4763,39 @@ if defined debug_com_base 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 @@ sys_gs: ; direct screen access .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 @@ syscall_drawrect: ; DrawRect 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 @@ syscall_threads: ; CreateThreads 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 ;------------------------------------------------------------------------------ diff --git a/kernel/branches/Kolibri-acpi/kernel32.inc b/kernel/branches/Kolibri-acpi/kernel32.inc index 9eb0b1e23..6f1dfa696 100644 --- a/kernel/branches/Kolibri-acpi/kernel32.inc +++ b/kernel/branches/Kolibri-acpi/kernel32.inc @@ -126,25 +126,29 @@ struct APPDATA 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 "gui/skincode.inc" include "bus/pci/pci32.inc" +; USB functions +include "bus/usb/init.inc" + ; Floppy drive controller include "blkdev/fdc.inc" diff --git a/kernel/branches/Kolibri-acpi/kernelsp.inc b/kernel/branches/Kolibri-acpi/kernelsp.inc index 48ebca1a2..97beb2423 100644 --- a/kernel/branches/Kolibri-acpi/kernelsp.inc +++ b/kernel/branches/Kolibri-acpi/kernelsp.inc @@ -1,4 +1,4 @@ -; ste archivo debe ser editado con codificacin CP866 +; Éste archivo debe ser editado con codificación CP866 -version db 'Kolibri OS versin 0.7.7.0+ ',13,10,13,10,0 -diff16 "fin del cdigo 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,$ diff --git a/kernel/branches/Kolibri-acpi/makefile b/kernel/branches/Kolibri-acpi/makefile index 29cb9cb5e..2e25eb2f7 100644 --- a/kernel/branches/Kolibri-acpi/makefile +++ b/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,15 +22,15 @@ drivers: 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 \ diff --git a/kernel/branches/Kolibri-acpi/network/ARP.inc b/kernel/branches/Kolibri-acpi/network/ARP.inc new file mode 100644 index 000000000..961b9e5a8 --- /dev/null +++ b/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 + diff --git a/kernel/branches/Kolibri-acpi/network/IPv4.inc b/kernel/branches/Kolibri-acpi/network/IPv4.inc new file mode 100644 index 000000000..6e1c98580 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/IPv6.inc b/kernel/branches/Kolibri-acpi/network/IPv6.inc new file mode 100644 index 000000000..42d67ab97 --- /dev/null +++ b/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 + diff --git a/kernel/branches/Kolibri-acpi/network/PPPoE.inc b/kernel/branches/Kolibri-acpi/network/PPPoE.inc new file mode 100644 index 000000000..2f5e6c7a7 --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/network/ethernet.inc b/kernel/branches/Kolibri-acpi/network/ethernet.inc new file mode 100644 index 000000000..518c4f2d5 --- /dev/null +++ b/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 + diff --git a/kernel/branches/Kolibri-acpi/network/icmp.inc b/kernel/branches/Kolibri-acpi/network/icmp.inc index 7a646dd88..45ea69c90 100644 --- a/kernel/branches/Kolibri-acpi/network/icmp.inc +++ b/kernel/branches/Kolibri-acpi/network/icmp.inc @@ -1,193 +1,431 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; ICMP.INC ;; -;; ;; -;; Internet Control Message Protocol ( RFC 792 ) ;; -;; ;; -;; Last revision: 11.11.2006 ;; -;; ;; -;; This file contains the following: ;; -;; icmp_rx - processes ICMP-packets received by the IP layer ;; -;; ;; -;; Changes history: ;; -;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;; -;; 11.11.2006 - [Johnny_B] and [smb] ;; -;; ;; -;; Current status: ;; -;; This implemetation of ICMP proto supports message of ECHO type. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ICMP.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$ +; ICMP types & codes + +ICMP_ECHOREPLY = 0 ; echo reply message + +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 + +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 +; +;----------------------------------------------------------------- + +macro ICMP_init { + + xor eax, eax + mov edi, ICMP_PACKETS_TX + mov ecx, 2*MAX_NET_DEVICES + rep stosd -struc ICMP_PACKET -{ .Type db ? ;+00 - .Code db ? ;+01 - .Checksum dw ? ;+02 - .Identifier dw ? ;+04 - .SequenceNumber dw ? ;+06 - .Data db ? ;+08 } -virtual at 0 - ICMP_PACKET ICMP_PACKET -end virtual - -; Example: -; ECHO message format +;----------------------------------------------------------------- ; +; ICMP_input: ; -; 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 ... -; +-+-+-+-+- +; 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 ; -; ICMP types & codes, RFC 792 and FreeBSD's ICMP sources +; 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] -; -; Description -; ICMP protocol handler -; This is a kernel function, called by ip_rx -; -; 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 -; -; OUT: -; EAX=not defined -; -; All used registers will be saved -; -;*************************************************************************** -proc icmp_rx stdcall uses ebx esi edi,\ - buffer_number:DWORD,IPPacketBase:DWORD,IPHeaderLength:DWORD + .loopback: + add esi, 2 + push [esi + IPv4_header.SourceAddress] + push [esi + IPv4_header.DestinationAddress] + pop [esi + IPv4_header.SourceAddress] + pop [esi + IPv4_header.DestinationAddress] - mov esi, [IPPacketBase];esi=IP_PACKET base address - mov edi, esi - add edi, [IPHeaderLength];edi=ICMP_PACKET base address +; 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 - cmp byte[edi + ICMP_PACKET.Type], ICMP_ECHO; Is this an echo request? discard if not - jz .icmp_echo + push esi ; Calculate the IP checksum + xor edx, edx ; + call checksum_1 ; + call checksum_2 ; + pop esi ; + mov [esi + IPv4_header.HeaderChecksum], dx ; - mov eax, [buffer_number] - call freeBuff - jmp .exit +; 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 - .icmp_echo: + mov esi, eax ; Calculate ICMP checksum + xor edx, edx ; + call checksum_1 ; + call checksum_2 ; + mov [eax + ICMP_header.Checksum], dx ; - ; 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 - - ; recalculate the IP header checksum - mov eax, [IPHeaderLength] - stdcall checksum_jb, esi, eax;buf_ptr,buf_size - - mov byte[esi + IP_PACKET.HeaderChecksum], ah - mov byte[esi + IP_PACKET.HeaderChecksum + 1], al ; ?? correct byte order? - - 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 - - ; 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 - - .exit: +; 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 + + +;----------------------------------------------------------------- +; +; ICMP_output +; +; IN: eax = dest ip +; ebx = source ip +; ecx = data length +; dh = type +; dl = code +; esi = data offset +; edi = identifier shl 16 + sequence number +; +;----------------------------------------------------------------- +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 + + + + +;----------------------------------------------------------------- +; +; ICMP_output +; +; IN: eax = socket ptr +; ecx = data length +; esi = data offset +; +;----------------------------------------------------------------- +align 4 +ICMP_output_raw: + + DEBUGF 1,"Creating ICMP Packet for socket %x, data ptr=%x\n", eax, edx + + push edx + + 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 + + pop esi + push edx + push eax + + push edi ecx + DEBUGF 1,"copying %u bytes from %x to %x\n", ecx, esi, edi + rep movsb + pop ecx edi + + mov [edi + ICMP_header.Checksum], 0 + + mov esi, edi + xor edx, edx + call checksum_1 + call checksum_2 + mov [edi + ICMP_header.Checksum], dx + + DEBUGF 1,"Sending ICMP Packet\n" + call [ebx + NET_DEVICE.transmit] + ret + .exit: + DEBUGF 1,"Creating ICMP Packet failed\n" + add esp, 4 + ret + + + + +;----------------------------------------------------------------- +; +; 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: + + 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, [ICMP_PACKETS_TX + eax] + ret + + .packets_rx: + mov eax, [ICMP_PACKETS_RX + eax] ret -endp diff --git a/kernel/branches/Kolibri-acpi/network/loopback.inc b/kernel/branches/Kolibri-acpi/network/loopback.inc new file mode 100644 index 000000000..2e6e9d7d4 --- /dev/null +++ b/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 + + diff --git a/kernel/branches/Kolibri-acpi/network/queue.inc b/kernel/branches/Kolibri-acpi/network/queue.inc index 9d5379e29..86ab74cd8 100644 --- a/kernel/branches/Kolibri-acpi/network/queue.inc +++ b/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 ;; -;; ;; -;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; -;; ;; -;; See file COPYING for details ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $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 +} \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/socket.inc b/kernel/branches/Kolibri-acpi/network/socket.inc index cc6d1844b..5bd45cc6c 100644 --- a/kernel/branches/Kolibri-acpi/network/socket.inc +++ b/kernel/branches/Kolibri-acpi/network/socket.inc @@ -1,1129 +1,2284 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; SOCKET.INC ;; -;; ;; -;; Sockets constants, structures and functions ;; -;; ;; -;; 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 ;; -;; ;; -;; ;; -;; Changes history: ;; -;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;; -;; 11.11.2006 - [Johnny_B] and [smb] ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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, ;; +;; and Clevermouse. ;; +;; ;; +;; Based on code by mike.dld ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ -; socket data structure + struct SOCKET - 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 - SND_WND dd ? ; send 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 + + NextPtr dd ? ; pointer to next socket in list + PrevPtr dd ? ; pointer to previous socket in list + 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 -; TCP opening modes -SOCKET_PASSIVE = 0 -SOCKET_ACTIVE = 1 +struct IP_SOCKET SOCKET -; socket types -SOCK_STREAM = 1 -SOCK_DGRAM = 2 + 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_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 + +struct UDP_SOCKET IP_SOCKET + + LocalPort dw ? ; network byte order + RemotePort dw ? ; network byte order + firstpacket db ? + +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) -; pointer to bitmap of free ports (1=free, 0=used) 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 -align 4 -network_free_hint dd 1024/8 -endg -;; 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). +;----------------------------------------------------------------- ; -; @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_init +; +;----------------------------------------------------------------- +macro SOCKET_init { - ; zero-initialize allocated memory - push eax - mov edi, eax - mov ecx, SOCKETBUFFSIZE / 4 - cld xor eax, eax - rep stosd + 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 +sys_socket: + + 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 + +;----------------------------------------------------------------- +; +; SOCKET_open +; +; IN: domain in ecx +; type in edx +; protocol in esi +; OUT: eax is socket num, -1 on error +; +;----------------------------------------------------------------- +align 4 +SOCKET_open: + + 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 + 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: - ret -endp - -;; Free socket data memory and pop socket off the list -; -; @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 - - ; 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 - - ; okay, we found the correct one - ; mark local port as unused - movzx ebx, [eax + SOCKET.LocalPort] - push eax - mov eax, [network_free_ports] - xchg bl, bh - lock bts [eax], ebx - 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] - ret - - .error: - DEBUGF 1, "K : failed\n" - 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. -; -; @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 - - ; 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 - - ; okay, we found the correct one - mov eax, ebx - ret - - .error: - xor eax, eax - 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. -; -; @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 - - ; 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, eax - jne .next_socket - ;cmp [ebx + SOCKET.PID], ecx - ;jne .next_socket - - ; okay, we found the correct one - mov eax, [ebx + SOCKET.Number] - ret - - .error: - xor eax, eax - ret -endp - -;; [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 - 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. - 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 - 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 - - DEBUGF 1, "K : socket_open (0x%x)\n", eax - - push eax - - 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 - - ;pop eax ; Get the socket number back, so we can return it - stdcall net_socket_addr_to_num - ret - - .error.free: - stdcall net_socket_free;, eax - - .error: - DEBUGF 1, "K : socket_open (fail)\n" - or eax, -1 - ret -endp - -;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way) -; -; @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 ? - - cmp esi, SOCKET_PASSIVE - jne .skip_port_check - - push ebx - mov eax, ebx - xchg al, ah - mov ebx, net_sockets - - .next_socket: - mov ebx, [ebx + SOCKET.NextPtr] - or ebx, ebx - jz .last_socket - cmp [ebx + SOCKET.TCBState], TCB_LISTEN - jne .next_socket - cmp [ebx + SOCKET.LocalPort], ax - jne .next_socket - - xchg al, ah - DEBUGF 1, "K : error: port %u is listened by 0x%x\n", ax, ebx - pop ebx - jmp .error - - .last_socket: - pop ebx - - .skip_port_check: - call net_socket_alloc - or eax, eax - jz .error - - 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. - - 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 - - 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 - - cmp ebx, TCB_LISTEN - je .exit - - ; 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 - - push eax - - mov bl, TH_SYN - xor ecx, ecx - 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 - - mov esi, [sockAddr] - - ; 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 - 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 - - stdcall net_socket_free, eax - - xor eax, eax - ret - - .error: - DEBUGF 1, "K : socket_close (fail)\n" - or eax, -1 - 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 ? - - DEBUGF 1, "K : socket_close_tcp (0x%x)\n", ebx - ; first, remove any resend entries - pusha - - mov esi, resendQ - mov ecx, 0 - - .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 - - @@: - mov dword[esi + 4], 0 - inc ecx - add esi, 8 - jmp .next_resendq - - .last_resendq: - popa - - stdcall net_socket_num_to_addr, ebx - or eax, eax - jz .error - - mov ebx, eax - mov [sockAddr], eax - - 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 - - push eax - - mov bl, TH_FIN+TH_ACK - xor ecx, ecx - xor esi, esi - stdcall build_tcp_packet, [sockAddr] - - 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 - - ; assume CLOSE WAIT - ; Send a fin, then enter last-ack state - mov [ebx + SOCKET.TCBState], TCB_LAST_ACK - jmp .send - - .fin_wait_1: - ; Send a fin, then enter finwait2 state - mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1 - - .send: - 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 - jmp .exit - - .destroy_tcb: - - ; Clear the socket variables - stdcall net_socket_free, ebx - - .exit: - xor eax, eax - ret - - .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 - jz .error - - mov eax, [eax + SOCKET.rxDataCount] - ret - - .error: - xor eax, eax - ret -endp - -;; [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 - jz .error - - mov eax, [eax + SOCKET.TCBState] - ret - - .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 - or eax, eax - jz .error - - mov ebx, eax - lea ecx, [eax + SOCKET.mutex] - call mutex_lock - - mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes - test eax, eax - jz .error_release - - 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 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 - - lea ecx, [ebx + SOCKET.mutex] - mov ebx, eax - call mutex_unlock - mov eax, ebx - ret - - .error_release: - lea ecx, [ebx + SOCKET.mutex] - call mutex_unlock - .error: - xor ebx, ebx - xor eax, eax - 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 - jz .error - - mov ebx, eax - - push ecx edx - lea ecx, [eax + SOCKET.mutex] - call mutex_lock - pop edx ecx - - 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) - - 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 - - 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 - mov eax, ebx - ret ; at last, exit - - .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 - - .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 - 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 -; -; @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 - or eax, eax - jz .error - - mov ebx, eax - - mov eax, EMPTY_QUEUE - call dequeue - cmp ax, NO_BUFFER - je .error - - ; Save the queue entry number - push eax - - ; 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 - - mov edx, eax - - ; 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 UDP data length - push eax - - 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 - - ; Fill in the UDP header (some data is in the socket descriptor) - mov ax, [ebx + SOCKET.LocalPort] - mov [edx + 20 + UDP_PACKET.SourcePort], ax - - mov ax, [ebx + SOCKET.RemotePort] - mov [edx + 20 + UDP_PACKET.DestinationPort], ax - - pop eax - push eax - - add eax, 8 - xchg al, ah - mov [edx + 20 + UDP_PACKET.Length], ax - - ; Checksum left unfilled - xor eax, eax - mov [edx + 20 + UDP_PACKET.Checksum], ax - - 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 + DEBUGF 1,"SOCKET_receive: STREAM\n" + mov ebx, edi + mov ecx, esi mov edi, edx - add edi, 28 - cld - rep movsb ; copy the data across + xor edx, edx - ; 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 + 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 - 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 + add eax, STREAM_SOCKET.rcv + call SOCKET_ring_read + call SOCKET_ring_free - call checksum - - ; 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 - - ; Check destination IP address. - ; If it is the local host IP, route it back to IP_RX - - pop ebx - - mov eax, NET1OUT_QUEUE - mov ecx, [edx + SOCKET.RemoteIP] - mov edx, [stack_ip] - cmp edx, ecx - jne .not_local - mov eax, IPIN_QUEUE - - .not_local: - ; Send it. - call queue - - xor eax, eax + mov [esp+32], ecx ; return number of bytes copied ret - .error: - or eax, -1 + .peek: + mov ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size] + mov [esp+32], ecx ; return number of bytes available ret -endp -;; [53.7] Send data through STREAM socket + .block: + test [eax + SOCKET.options], SO_NONBLOCK + jnz .return0 + + call SOCKET_block + jmp .loop + + .return0: + xor ecx, ecx + mov [esp+32], ecx + 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_tcp stdcall -local sockAddr dd ? +; 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: -; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx - stdcall net_socket_num_to_addr, ebx - or eax, eax - jz .error + DEBUGF 2,"SOCKET_send: socknum=%u data ptr=%x length=%u flags=%x\n", ecx, edx, esi, edi - mov ebx, eax - mov [sockAddr], ebx + call SOCKET_num_to_ptr + jz s_error - ; If the sockets window timer is nonzero, do not queue packet - cmp [ebx + SOCKET.wndsizeTimer], 0 - jne .error - - mov eax, EMPTY_QUEUE - call dequeue - cmp ax, NO_BUFFER - je .error - - push eax - - ; Get the address of the callers data - mov edi, [TASK_BASE] - add edi, TASKDATA.mem_start - add edx, [edi] + mov ecx, esi mov esi, edx - pop eax + 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 - - push ecx - mov bl, TH_ACK - stdcall build_tcp_packet, [sockAddr] - pop ecx - - ; Check destination IP address. - ; If it is the local host IP, route it back to IP_RX - - pop ebx - push ecx - - 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: - 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 - - pop ebx - - ; 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 - - .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 - - @@: - push ebx - - ; 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 - - @@: - add edi, IPBUFFSIZE - loop @b - - ; we have dest buffer location in edi + add eax, STREAM_SOCKET.snd + call SOCKET_ring_write pop eax - ; convert source buffer pointer eax to the absolute address - mov ecx, IPBUFFSIZE - mul ecx - add eax, IPbuffs - mov esi, eax - ; do copy - mov ecx, IPBUFFSIZE - cld - rep movsb + mov [esp+32], ecx + + call TCP_output + ret + + +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 + + +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 + + +;----------------------------------------------------------------- +; +; 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: + + DEBUGF 2,"SOCKET_get_opt\n" + + 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 + + + +;----------------------------------------------------------------- +; +; 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 + + .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 + + + +;----------------------------------------------------------------- +; +; 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: + + 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 + mov [esp+24], edi ; application's ebx + + 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 - .exit: - xor eax, eax ret .error: - or eax, -1 + mov eax, ebx + call SOCKET_free + jmp s_error + + + +;----------------------------------------------------------------- +; +; 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 + + .returnall: + mov ebx, net_sockets + .next_socket: + mov ebx, [ebx + SOCKET.NextPtr] + test ebx, ebx + jz .done + mov eax, [ebx + SOCKET.Number] + stosd + jmp .next_socket + .done: + xor eax, eax + stosd + + mov dword [esp+32], 0 + ret + + +;----------------------------------------------------------------- +; +; 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 2,"SOCKET_find_port\n" + + push ebx esi ecx + + cmp [eax + SOCKET.Protocol], IP_PROTO_UDP + je .udp + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + je .tcp + + pop ecx esi ebx + ret + + .udp: + mov bx, [last_UDP_port] + call .findit + mov [last_UDP_port], bx + + pop ecx esi ebx + ret + + .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 + + + +;----------------------------------------------------------------- +; +; 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: + + DEBUGF 2,"SOCKET_check_port: " + + mov ecx, [eax + SOCKET.Protocol] + mov edx, [eax + IP_SOCKET.LocalIP] + mov esi, net_sockets + + .next_socket: + mov esi, [esi + SOCKET.NextPtr] + or esi, esi + jz .port_ok + + cmp [esi + SOCKET.Protocol], ecx + jne .next_socket + + cmp [esi + IP_SOCKET.LocalIP], edx + jne .next_socket + + cmp [esi + UDP_SOCKET.LocalPort], bx + jne .next_socket + + DEBUGF 2,"local port %x already in use\n", bx ; FIXME: find a way to print big endian values with debugf + ret + + .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 + + + +;----------------------------------------------------------------- +; +; 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: + + DEBUGF 2,"SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx + + mov [esp+4], ecx + push esi + mov esi, esp + + add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, SOCKET_input.full + + DEBUGF 1,"SOCKET_input: success\n" + add esp, sizeof.socket_queue_entry + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + popa + + jmp SOCKET_notify + + .full: + DEBUGF 2,"SOCKET_input: socket %x is full!\n", eax + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + popa + + call kernel_free + add esp, 8 + + ret + + +;-------------------------- +; +; 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 + +;----------------------------------------------------------------- +; +; 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: + + DEBUGF 1,"SOCKET_ring_write: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx + +; lock mutex + pusha + lea ecx, [eax + RING_BUFFER.mutex] + call mutex_lock ; TODO: check what registers this function actually destroys + popa + +; 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 + +;----------------------------------------------------------------- +; +; 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,"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, [eax + RING_BUFFER.read_ptr] + add esi, edx ; esi = start_ptr + offset + + neg edx + add edx, [eax + RING_BUFFER.size] ; edx = snd.size - offset + jle .no_data_at_all + + pusha + lea ecx, [eax + RING_BUFFER.mutex] + call mutex_unlock ; TODO: check what registers this function actually destroys + popa + + 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 + + DEBUGF 1,"SOCKET_ring_read: no data at all!\n" + xor ecx, ecx + ret + + .less_data: + mov ecx, edx + jmp .copy + + +;----------------------------------------------------------------- +; +; 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 + + xor ecx, ecx + ret + + +;----------------------------------------------------------------- +; +; SOCKET_block +; +; Suspends the thread attached to a socket +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- +align 4 +SOCKET_block: + + DEBUGF 1,"SOCKET_block: %x\n", eax + + pushf + cli + + ; Set the 'socket is blocked' flag + or [eax + SOCKET.state], SS_BLOCKED + + ; Suspend the thread + push edx + mov edx, [TASK_BASE] + mov [edx + TASKDATA.state], 1 ; Suspended + + ; 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 + + call change_task + popf + + DEBUGF 1,"SOCKET_block: continueing\n" + + ret + + +;----------------------------------------------------------------- +; +; 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 + + test [eax + SOCKET.state], SS_BLOCKED + jnz .unblock + + test [eax + SOCKET.options], SO_NONBLOCK + jz .error + + 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: + + ; Run the thread + mov [esi + TASKDATA.state], 0 ; Running + DEBUGF 1,"SOCKET_notify: Unblocked socket!\n" + + .done: + pop esi ecx eax + + .error: + ret + + +;-------------------------------------------------------------------- +; +; 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 .exit + +; 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_init + popa + +; add socket to the list by re-arranging some pointers + mov ebx, [net_sockets + SOCKET.NextPtr] + + mov [eax + SOCKET.PrevPtr], net_sockets + mov [eax + SOCKET.NextPtr], ebx + + test ebx, ebx + jz @f + + pusha + lea ecx, [ebx + SOCKET.mutex] + call mutex_lock + popa + + mov [ebx + SOCKET.PrevPtr], eax + + pusha + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + popa + @@: + + mov [net_sockets + SOCKET.NextPtr], eax + or eax, eax ; used to clear zero flag + .exit: + pop ebx + + ret + + +;---------------------------------------------------- +; +; 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 + + push ebx + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + popa + + cmp [eax + SOCKET.Domain], AF_INET4 + jnz .no_tcp + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + jnz .no_tcp + + mov ebx, eax + stdcall kernel_free, [ebx + STREAM_SOCKET.rcv.start_ptr] + stdcall kernel_free, [ebx + STREAM_SOCKET.snd.start_ptr] + mov eax, ebx + .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: + ret + +;------------------------------------ +; +; SOCKET_fork +; +; Create a child socket +; +; IN: socket nr in ebx +; OUT: child socket nr in eax +; +;----------------------------------- +align 4 +SOCKET_fork: + + 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 + + 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 + + +;--------------------------------------------------- +; +; 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 + + test eax, eax + + DEBUGF 1,"ptr=%x\n", eax + ret + + .error: + DEBUGF 1,"not found\n", eax + ret + + +;--------------------------------------------------- +; +; 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: + + DEBUGF 1,"SOCKET_ptr_to_num: ptr=%x ", eax + + call SOCKET_check + jz .error + + mov eax, [eax + SOCKET.Number] + + DEBUGF 1,"num=%u\n", eax + ret + + .error: + DEBUGF 1,"not found\n", eax + ret + + +;--------------------------------------------------- +; +; 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: + + DEBUGF 1,"SOCKET_check: %x\n", eax + + push ebx + mov ebx, net_sockets + + .next_socket: + mov ebx, [ebx + SOCKET.NextPtr] + or ebx, ebx + jz .done + cmp ebx, eax + jnz .next_socket + + .done: + mov eax, ebx + test eax, eax + pop ebx + + ret + + + +;--------------------------------------------------- +; +; SOCKET_check_owner +; +; checks if the caller application owns the socket +; +; IN: eax = socket ptr +; OUT: ZF = true/false +; +;--------------------------------------------------- +align 4 +SOCKET_check_owner: + + DEBUGF 1,"SOCKET_check_owner: %x\n", eax + + push ebx + mov ebx, [TASK_BASE] + mov ebx, [ebx + TASKDATA.pid] + cmp [eax + SOCKET.PID], ebx + pop ebx + + ret + + + + +;------------------------------------------------------ +; +; 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: + + DEBUGF 1, "SOCKET_process_end: %x\n", edx + + push ebx + mov ebx, net_sockets + + .next_socket: + mov ebx, [ebx + SOCKET.NextPtr] + .next_socket_test: + test ebx, ebx + jz .done + + cmp [ebx + SOCKET.PID], edx + jne .next_socket + + DEBUGF 1, "SOCKET_process_end: killing socket %x\n", ebx + + 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 + + + + +;----------------------------------------------------------------- +; +; SOCKET_is_connecting +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_connecting: + + DEBUGF 1,"SOCKET_is_connecting: %x\n", eax + + and [eax + SOCKET.options], not (SS_ISCONNECTED + SS_ISDISCONNECTING + SS_ISCONFIRMING) + or [eax + SOCKET.options], SS_ISCONNECTING + + jmp SOCKET_notify + + + +;----------------------------------------------------------------- +; +; SOCKET_is_connected +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_connected: + + DEBUGF 1,"SOCKET_is_connected: %x\n", eax + + and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISDISCONNECTING + SS_ISCONFIRMING) + or [eax + SOCKET.options], SS_ISCONNECTED + + jmp SOCKET_notify + + + + +;----------------------------------------------------------------- +; +; SOCKET_is_disconnecting +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_disconnecting: + + DEBUGF 1,"SOCKET_is_disconnecting: %x\n", eax + + and [eax + SOCKET.options], not (SS_ISCONNECTING) + or [eax + SOCKET.options], SS_ISDISCONNECTING + SS_CANTRCVMORE + SS_CANTSENDMORE + + jmp SOCKET_notify + + + +;----------------------------------------------------------------- +; +; SOCKET_is_disconnected +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_disconnected: + + DEBUGF 1,"SOCKET_is_disconnected: %x\n", eax + + and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING) + or [eax + SOCKET.options], SS_CANTRCVMORE + SS_CANTSENDMORE + + 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 + + + +;----------------------------------------------------------------- +; +; 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 \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/stack.inc b/kernel/branches/Kolibri-acpi/network/stack.inc index 3b286a357..561b29d94 100644 --- a/kernel/branches/Kolibri-acpi/network/stack.inc +++ b/kernel/branches/Kolibri-acpi/network/stack.inc @@ -1,912 +1,789 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; STACK.INC ;; -;; ;; -;; TCP/IP stack for Menuet OS ;; -;; ;; -;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; -;; ;; -;; See file COPYING for details ;; -;; ;; -;; Version 0.7 ;; -;; Added a timer per socket to allow delays when rx window ;; -;; gets below 1KB ;; -;; ;; -;;10.01.2007 Bugfix for checksum function from Paolo Franchetti ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; STACK.INC ;; +;; ;; +;; TCP/IP stack for KolibriOS ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; 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) ;; +;; ;; +;; TCP part is based on 4.4BSD ;; +;; ;; +;; 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 + +SO_BLOCK = 1 shl 10 ; TO BE REMOVED +SO_NONBLOCK = 1 shl 31 + +; Socket flags for user calls +MSG_PEEK = 0x02 +MSG_DONTWAIT = 0x40 + +; Socket level +SOL_SOCKET = 0 -; 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 +; 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 -; 32 bit word -stack_config equ stack_data +SS_ASYNC = 0x0100 ; async i/o notify +SS_ISCONFIRMING = 0x0200 ; deciding to accept connection req +SS_MORETOCOME = 0x0400 -; 32 bit word - IP Address in network format -stack_ip equ stack_data + 4 - -; 1 byte. 0 == inactive, 1 = active -ethernet_active equ stack_data + 9 +SS_BLOCKED = 0x8000 -; TODO :: empty memory area +SOCKET_MAXDATA = 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8 -; 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 +; Network driver types +NET_TYPE_LOOPBACK = 0 +NET_TYPE_ETH = 1 +NET_TYPE_SLIP = 2 -; holds the TCP/UDP pseudo header. SA|DA|0|prot|UDP len| -pseudoHeader equ stack_data + 50 +MAX_backlog = 20 ; maximum backlog for stream sockets -; 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 ) +; Error Codes +ENOBUFS = 55 +ECONNREFUSED = 61 +ECONNRESET = 52 +ETIMEDOUT = 60 +ECONNABORTED = 53 -;resendQ equ queueList + ( 2 * NUMQUEUEENTRIES ) -;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP -; equ resendBuffer + ( IPBUFFSIZE * NUMRESENDENTRIES ) +; 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 + +struct NET_DEVICE + + 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 +} -;resendQ equ 0x770000 -;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP ; XTODO: validate size -resendBuffer equ resendQ + ( 8 * NUMRESENDENTRIES ) ; for TCP +; Network to Hardware byte order (dword) +macro ntohd reg { + rol word reg, 8 + rol dword reg, 16 + rol word reg , 8 -uglobal -net_sockets rd 2 -endg +} + +; Network to Hardware byte order (word) +macro ntohw reg { + + rol word reg, 8 -; 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 } -; Below, the main network layer source code is included -; 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 - - ; Queries initialization - call queueInit - - ; The following block sets up the 1s timer - mov al, 0x0 - out 0x70, al - in al, 0x71 - mov [last_1sTick], al - ret - -;*************************************************************************** -; Function -; stack_handler -; -; Description -; The kernel loop routine for the stack -; This is a kernel function, called in the main loop -; -;*************************************************************************** align 4 -stack_handler: +uglobal - call ethernet_driver - call ip_rx + NET_RUNNING dd ? + NET_DEFAULT dd ? + NET_DRV_LIST rd MAX_NET_DEVICES + +endg - ; Test for 10ms tick, call tcp timer - mov eax, [timer_ticks];[0xfdf0] - cmp eax, [last_1hsTick] - je sh_001 +;----------------------------------------------------------------- +; +; stack_init +; +; This function calls all network init procedures +; +; IN: / +; OUT: / +; +;----------------------------------------------------------------- +align 4 +stack_init: - mov [last_1hsTick], eax - call tcp_tx_handler +; Init the network drivers list + xor eax, eax + mov edi, NET_RUNNING + mov ecx, (MAX_NET_DEVICES + 2) + rep stosd -sh_001: + PPPoE_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 + IPv4_init +; IPv6_init + ICMP_init - mov [last_1sTick], al + ARP_init + UDP_init + TCP_init - stdcall arp_table_manager, ARP_TABLE_TIMER, 0, 0 - call tcp_tcb_handler + SOCKET_init + + mov [net_tmr_count], 0 -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 ; -;*************************************************************************** - - -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 - -cs1: - mov bh, [eax] - mov bl, [eax + 1] - - add eax, 2 - add edx, ebx - - loopw cs1 - -cs1_1: - and word [checkSize1], 0x01 - jz cs_test2 - - mov bh, [eax] - xor bl, bl - - add edx, ebx - -cs_test2: - mov cx, [checkSize2] - cmp cx, 0 - jz cs_exit ; Finished if no 2nd buffer - - mov eax, [checkAdd2] - - shr cx, 1 - jz cs2_1 - -cs2: - mov bh, [eax] - mov bl, [eax + 1] - - add eax, 2 - add edx, ebx - - loopw cs2 - -cs2_1: - and word [checkSize2], 0x01 - jz cs_exit - - mov bh, [eax] - xor bl, bl - - add edx, ebx - -cs_exit: - mov ebx, edx - - shr ebx, 16 - and edx, 0xffff - add edx, ebx - mov eax, edx - shr eax, 16 - add edx, eax - not dx - - mov [checkResult], dx - popa - ret - - - - -;*************************************************************************** -; Function -; app_stack_handler +; This function is called in kernel loop ; -; Description -; This is an application service, called by int 0x40, function 52 -; It provides application access to the network interface layer +; IN: / +; OUT: / ; -;*************************************************************************** -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 +stack_handler: - jmp dword [f52call+ebx*4] + ; Test for 10ms tick + mov eax, [timer_ticks] + cmp eax, [net_10ms] + je .exit + mov [net_10ms], eax + cmp [NET_RUNNING], 0 + je .exit -.00: -; Read the configuration word - mov eax, [stack_config] + test [net_10ms], 0x0f ; 160ms + jnz .exit + + TCP_timer_160ms + + test [net_10ms], 0x3f ; 640ms + jnz .exit + + TCP_timer_640ms + ARP_decrease_entry_ttls + IPv4_decrease_fragment_ttls + + .exit: ret -.01: -; read the IP address - mov eax, [stack_ip] - ret -.02: -; write the configuration word - mov [stack_config], ecx -; -; 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. - -; 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 - ret - -.03: -; write the IP Address - mov [stack_ip], ecx - 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 - -.10: -; read the subnet mask - mov eax, [subnet_mask] - ret -.11: -; write the gateway IP Address - mov [gateway_ip], ecx - ret - -.12: -; write the subnet mask - mov [subnet_mask], ecx - ret - -.13: -; read the dns - mov eax, [dns_ip] - ret - -.14: -; write the dns IP Address - mov [dns_ip], ecx - ret - -.15: -; -; 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 - -.param_error: - or eax, -1 ; params not accepted - ret - -.16: -; 0 -> arp_probe -; 1 -> arp_announce -; 2 -> arp_responce (not supported yet) - test ecx, ecx - je a_probe - - dec ebx - jz a_ann ; arp announce -.fail: - or eax, -1 - ret - -; cmp ebx,2 -; jne a_resp ; arp response - -; 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] - - mov edx, [stack_ip] - and [stack_ip], dword 0 - mov esi, ecx ; pointer to target MAC address - call arp_request - - pop dword [stack_ip] - 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 - ret - -.17: -; -; modified by [smb] - -; -; ARPTable manager interface - ;see "proc arp_table_manager" for more details - stdcall arp_table_manager, ecx, edx, esi;Opcode,Index,Extra - ret -; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;; -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 -; -;*************************************************************************** -iglobal 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_link_changed: -app_socket_handler: -;in ebx,ecx,edx,wsi -;out eax - cmp eax, 255 - je stack_internal_status + DEBUGF 1,"NET_link_changed device=0x%x status=0x%x\n", ebx, [ebx + NET_DEVICE.state] - cmp eax, 11 - ja .fail ;if more than 15 then exit +align 4 +NET_send_event: - jmp dword [f53call+eax*4] + DEBUGF 1,"NET_send_event\n" + +; 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 -.10: - mov eax, dword[drvr_cable] - test eax, eax - jnz @f ; if function is not implented, return -1 - or al, -1 ret -@@: - jmp dword[drvr_cable] -.fail: - or eax, -1 - 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 +; NET_add_device: ; -;*************************************************************************** -; 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 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: + DEBUGF 1,"NET_Add_Device: %x\n", ebx ;;; TODO: use mutex to lock net device list -; 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 ) + 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 -stack_internal_status: - cmp ebx, 100 - jnz notsis100 + repne scasd ; See if device is already in the list + jz .error - ; 100 : return length of EMPTY QUEUE - mov ebx, EMPTY_QUEUE - call queueSize - ret - -notsis100: - cmp ebx, 101 - jnz notsis101 - - ; 101 : return length of IPOUT QUEUE - mov ebx, IPOUT_QUEUE - call queueSize - ret - -notsis101: - cmp ebx, 102 - jnz notsis102 - - ; 102 : return length of IPIN QUEUE - mov ebx, IPIN_QUEUE - call queueSize - ret - -notsis102: - cmp ebx, 103 - jnz notsis103 - - ; 103 : return length of NET1OUT QUEUE - mov ebx, NET1OUT_QUEUE - call queueSize - ret - -notsis103: - cmp ebx, 200 - jnz notsis200 - - ; 200 : return num entries in arp table - movzx eax, byte [NumARP] - ret - -notsis200: - cmp ebx, 201 - jnz notsis201 - - ; 201 : return arp table size - mov eax, 20; ARP_TABLE_SIZE - ret - -notsis201: - cmp ebx, 202 - jnz notsis202 - - ; 202 - read the requested table entry - ; into a temporary buffer - ; ecx holds the entry number - - mov eax, ecx - mov ecx, 14; ARP_ENTRY_SIZE - mul ecx - - 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 - ret - -notsis202: - cmp ebx, 203 - jnz notsis203 - - ; 203 - return IP address - mov eax, [ARPTmp] - ret - -notsis203: - cmp ebx, 204 - jnz notsis204 - - ; 204 - return MAC high dword - mov eax, [ARPTmp+4] - ret - -notsis204: - cmp ebx, 205 - jnz notsis205 - - ; 205 - return MAC ls word - movzx eax, word [ARPTmp+8] - ret - -notsis205: - cmp ebx, 206 - jnz notsis206 - - ; 206 - return status word - movzx eax, word [ARPTmp+10] - ret - -notsis206: - cmp ebx, 207 - jnz notsis207 - - ; 207 - return ttl word - movzx eax, word [ARPTmp+12] - ret - -notsis207: - cmp ebx, 2 - jnz notsis2 - - ; 2 : return number of IP packets received - mov eax, [ip_rx_count] - ret - -notsis2: - cmp ebx, 3 - jnz notsis3 - - ; 3 : return number of packets transmitted - mov eax, [ip_tx_count] - ret - -notsis3: - cmp ebx, 4 - jnz notsis4 - - ; 4 : return number of received packets dumped - mov eax, [dumped_rx_count] - ret - -notsis4: - cmp ebx, 5 - jnz notsis5 - - ; 5 : return number of arp packets received - mov eax, [arp_rx_count] - ret - -notsis5: - cmp ebx, 6 - jnz notsis6 - - ; 6 : return status of packet driver - ; ( 0 == not active, FFFFFFFF = successful ) - mov eax, [eth_status] - ret - -notsis6: +;---------------------------- +; Find empty slot in the list xor eax, eax - ret + mov ecx, MAX_NET_DEVICES + mov edi, NET_DRV_LIST + repne scasd + jnz .error + sub edi, 4 -;*************************************************************************** -; 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 +;----------------------------- +; Add device to the found slot + mov [edi], ebx ; add device to list - push eax ; Save buffer number for freeing at end + mov eax, edi ; Calculate device number in eax + sub eax, NET_DRV_LIST + shr eax, 2 - 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 - - mov ecx, 1500 ; should get the actual number of bytes to write - mov esi, eax - cld - rep movsb ; copy the data across - - ; And finally, return the buffer to the free queue - pop eax - call freeBuff - - mov eax, 1500 - ret - -sgp_non_exit: - xor eax, 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 -; -;*************************************************************************** -stack_insert_packet: - - mov eax, EMPTY_QUEUE - call dequeue - cmp ax, NO_BUFFER - je sip_err_exit + inc [NET_RUNNING] ; Indicate that one more network device is up and running + 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 + @@: - ; save the pointers to the data buffer & size - push edx + call NET_send_event + + DEBUGF 1,"Device number: %u\n", eax + ret + + .error: + or eax, -1 + DEBUGF 2,"Adding network device failed\n" + 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: + + DEBUGF 1,"NET_set_default: device=%x\n", eax + + cmp eax, MAX_NET_DEVICES + jae .error + + cmp [NET_DRV_LIST+eax*4], 0 + je .error + + mov [NET_DEFAULT], eax + + DEBUGF 1,"NET_set_default: succes\n" + ret + + .error: + or eax, -1 + DEBUGF 1,"NET_set_default: failed\n" + 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: + + cmp [NET_RUNNING], 0 + je .error + + 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 + @@: + +;---------------------------- +; Find the driver in the list + + mov eax, ebx + mov ecx, MAX_NET_DEVICES + mov edi, NET_DRV_LIST+4 + + repne scasd + jnz .error + +;------------------------ +; Remove it from the list + + xor eax, eax + mov dword [edi-4], eax + + call NET_send_event + + dec [NET_RUNNING] + ret + + .error: + or eax, -1 + ret + + + +;----------------------------------------------------------------- +; +; NET_ptr_to_num +; +; IN: ebx = ptr to device struct +; OUT: edi = -1 on error, device number otherwise +; +;----------------------------------------------------------------- +align 4 +NET_ptr_to_num: push ecx - ; convert buffer pointer eax to the absolute address - mov ecx, IPBUFFSIZE - mul ecx - add eax, IPbuffs + mov ecx, MAX_NET_DEVICES + mov edi, NET_DRV_LIST - mov edx, eax + .loop: + cmp ebx, [edi] + jz .found + add edi, 4 + dec ecx + jnz .loop - ; So, edx holds the IPbuffer ptr + ; repnz scasd could work too if eax is used instead of ebx! - pop ecx ; count of bytes to send - mov ebx, ecx ; need the length later - pop eax ; get callers ptr to data to send + or edi, -1 - ; 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 - cld - rep movsb ; copy the data across - - pop ebx - - mov eax, IPIN_QUEUE - call queue - - inc dword [ip_rx_count] - - mov eax, 0 + pop ecx ret -sip_err_exit: - mov eax, 0xFFFFFFFF + .found: + sub edi, NET_DRV_LIST + shr edi, 2 + + pop ecx ret +;----------------------------------------------------------------- +; +; checksum_1 +; +; 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 + + shr ecx, 1 + pushf + jz .no_4 + + shr ecx, 1 + pushf + jz .no_8 + + .loop: + add dl, [esi+1] + adc dh, [esi+0] + + adc dl, [esi+3] + adc dh, [esi+2] + + adc dl, [esi+5] + adc dh, [esi+4] + + adc dl, [esi+7] + adc dh, [esi+6] + + adc edx, 0 + add esi, 8 + + dec ecx + jnz .loop + + adc edx, 0 + + .no_8: + popf + jnc .no_4 + + add dl, [esi+1] + adc dh, [esi+0] + + adc dl, [esi+3] + adc dh, [esi+2] + + adc edx, 0 + add esi, 4 + + .no_4: + popf + jnc .no_2 + + add dl, [esi+1] + adc dh, [esi+0] + + adc edx, 0 + inc esi + inc esi + + .no_2: + popf + jnc .end + + add dh, [esi+0] + adc edx, 0 + .end: + ret + +;----------------------------------------------------------------- +; +; 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: + + mov ecx, edx + shr ecx, 16 + and edx, 0xffff + add edx, ecx + + 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 + + DEBUGF 1,"Checksum: %x\n", dx + + ret + + + +;---------------------------------------------------------------- +; +; System function to work with network devices (75) +; +;---------------------------------------------------------------- +align 4 +sys_network: ; FIXME: make default device easily accessible + + cmp ebx, -1 + jne @f + + mov eax, [NET_RUNNING] + jmp .return + + @@: + cmp bh, MAX_NET_DEVICES ; Check if device number exists + jae .doesnt_exist + + mov esi, ebx + and esi, 0x0000ff00 + shr esi, 6 + + cmp dword [esi + NET_DRV_LIST], 0 ; check if driver is running + je .doesnt_exist + + mov eax, [esi + NET_DRV_LIST] + + and ebx, 0x000000ff + cmp ebx, .number + ja .doesnt_exist + jmp dword [.table + 4*ebx] + + .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 + + .get_type: ; 0 = Get device type (ethernet/token ring/...) + + mov eax, [eax + NET_DEVICE.type] + jmp .return + + + .get_dev_name: ; 1 = Get device name + + mov esi, [eax + NET_DEVICE.name] + mov edi, ecx + + mov ecx, 64/4 ; max length + rep movsd + + xor eax, eax + jmp .return + + .reset: ; 2 = Reset the device + + call [eax + NET_DEVICE.reset] + jmp .return + + .stop: ; 3 = Stop driver for this device + + call [eax + NET_DEVICE.unload] + jmp .return + + + .get_ptr: ; 4 = Get driver pointer + + jmp .return + + + .get_drv_name: ; 5 = Get driver name + + 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 + + +;---------------------------------------------------------------- +; +; System function to work with protocols (76) +; +;---------------------------------------------------------------- +align 4 +sys_protocols: + cmp bh, MAX_NET_DEVICES ; Check if device number exists + jae .doesnt_exist + + 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 .return ; return address (we will be using jumps instead of calls) + + mov eax, ebx ; set ax to protocol number + shr eax, 16 ; + + cmp ax, API_ETH + je ETH_api + + cmp ax, API_IPv4 + je IPv4_api + + cmp ax, API_ICMP + je ICMP_api + + cmp ax, API_UDP + je UDP_api + + cmp ax, API_TCP + je TCP_api + + cmp ax, API_ARP + je ARP_api + + cmp ax, API_PPPOE + je PPPoE_api + + cmp ax, API_IPv6 + je IPv6_api + + add esp, 4 ; if we reached here, no function was called, so we need to balance stack + + .doesnt_exist: + mov eax, -1 + + .return: + mov [esp+28+4], eax ; return eax value to the program + ret diff --git a/kernel/branches/Kolibri-acpi/network/tcp.inc b/kernel/branches/Kolibri-acpi/network/tcp.inc index dc763ecb8..640a190bf 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp.inc @@ -1,1176 +1,224 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; TCP.INC ;; -;; ;; -;; TCP Processes for Menuet OS TCP/IP stack ;; -;; ;; -;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; -;; ;; -;; 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 ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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$ +; 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 + +; timer constants +TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK +TCP_max_keepcnt = 8 ; max keepalive probes -;******************************************************************* -; 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 -; -;******************************************************************* +TCP_max_winshift = 14 +TCP_max_win = 65535 +TCP_re_xmit_thresh = 3 -; 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_mss_default = 1480 ; default max segment size +; 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 -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 -} +; 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 -virtual at 0 - TCP_PACKET TCP_PACKET -end virtual +TCP_BIT_SENDALOT = 1 shl 0 +TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds +TCP_QUEUE_SIZE = 50 -;*************************************************************************** -; Function -; tcp_tcb_handler -; -; Description -; Handles sockets in the timewait state, closing them -; when the TCB timer expires -; -;*************************************************************************** +struct TCP_header -proc tcp_tcb_handler stdcall uses ebx - ; scan through all the sockets, decrementing active timers + 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 ? - mov ebx, net_sockets +ends - cmp [ebx + SOCKET.NextPtr], 0 - je .exit - ;DEBUGF 1, "K : sockets:\n" +struct TCP_queue_entry - .next_socket: - mov ebx, [ebx + SOCKET.NextPtr] - or ebx, ebx - jz .exit + ip_ptr dd ? + segment_ptr dd ? + segment_size dd ? + device_ptr dd ? - ;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] + buffer_ptr dd ? + timestamp dd ? - cmp [ebx + SOCKET.TCBTimer], 0 - jne .decrement_tcb - cmp [ebx + SOCKET.wndsizeTimer], 0 - jne .decrement_wnd - jmp .next_socket +ends - .decrement_tcb: - ; decrement it, delete socket if TCB timer = 0 & socket in timewait state - dec [ebx + SOCKET.TCBTimer] - jnz .next_socket - - cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT - jne .next_socket - - push [ebx + SOCKET.PrevPtr] - stdcall net_socket_free, ebx - pop ebx - jmp .next_socket - - .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 -; -;*************************************************************************** - -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 - - mov esi, resendQ - mov ecx, 0 - - .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 - - @@: ; 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: - pop ebx - call queue - - .tth005: - popad - - inc ecx - add esi, 8 - jmp .next_resendq - - .exit: - ret -endp - - -;*************************************************************************** -; 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 -; -;*************************************************************************** - -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 -; -; 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 -; -;*************************************************************************** - -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 +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 -;*************************************************************************** -; Function -; tcpStateMachine +;----------------------------------------------------------------- ; -; Description -; TCP state machine -; This is a kernel function, called by tcp_rx +; TCP_init ; -; IP buffer address given in edx -; Socket/TCB address in ebx +; This function resets all TCP variables ; -; The IP buffer will be released by the caller -;*************************************************************************** +;----------------------------------------------------------------- +macro TCP_init { -proc tcpStateMachine stdcall, sockAddr:DWORD - ; as a packet has been received, update the TCB timer - mov [ebx + SOCKET.TCBTimer], TWOMSL + xor eax, eax + mov edi, TCP_segments_tx + mov ecx, (6*MAX_NET_DEVICES) + rep stosd - ; 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 + pseudo_random eax + mov [TCP_sequence_num], eax - ; get skt number in eax - stdcall net_socket_addr_to_num, ebx + init_queue TCP_queue - ; The ack number is in [edx + 28], inet format - ; skt in eax + push 1 + pop ebx + mov ecx, TCP_process_input + call new_sys_threads - 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? +include 'tcp_timer.inc' +include 'tcp_subr.inc' +include 'tcp_usreq.inc' +include 'tcp_input.inc' +include 'tcp_output.inc' - ; 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: - ret -endp - -;*************************************************************************** -; Function -; signal_network_event +;--------------------------------------------------------------------------- ; -; Description -; Signals about network event to socket owner -; This is a kernel function, called from TCP handler +; TCP_API ; -; 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 +; This function is called by system function 76 +; +; IN: subfunction number in bl +; device number in bh +; ecx, edx, .. depends on subfunction +; +; OUT: +; +;--------------------------------------------------------------------------- +align 4 +TCP_api: - .next_pid: - cmp [esi], eax - je .found_pid - inc ecx - add esi, 0x20 - cmp ecx, [TASK_COUNT] - jbe .next_pid + movzx eax, bh + shl eax, 2 - .found_pid: - shl ecx, 8 - or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event - pop eax esi ecx + 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 + + .error: + mov eax, -1 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_tx: + mov eax, [TCP_segments_tx + 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_rx: + mov eax, [TCP_segments_rx + 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_missed: + mov eax, [TCP_segments_missed + 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: + .packets_dumped: + mov eax, [TCP_segments_dumped + eax] 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 diff --git a/kernel/branches/Kolibri-acpi/network/tcp_input.inc b/kernel/branches/Kolibri-acpi/network/tcp_input.inc new file mode 100644 index 000000000..d634ed0bd --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/network/tcp_output.inc b/kernel/branches/Kolibri-acpi/network/tcp_output.inc new file mode 100644 index 000000000..839ba44ad --- /dev/null +++ b/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: + + ; + + .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 + + + + + + diff --git a/kernel/branches/Kolibri-acpi/network/tcp_subr.inc b/kernel/branches/Kolibri-acpi/network/tcp_subr.inc new file mode 100644 index 000000000..32eaf0116 --- /dev/null +++ b/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 + diff --git a/kernel/branches/Kolibri-acpi/network/tcp_timer.inc b/kernel/branches/Kolibri-acpi/network/tcp_timer.inc new file mode 100644 index 000000000..64adea1ee --- /dev/null +++ b/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 \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc b/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc new file mode 100644 index 000000000..737dda6e3 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/network/udp.inc b/kernel/branches/Kolibri-acpi/network/udp.inc index 102ec0b2f..2b9a5ad0d 100644 --- a/kernel/branches/Kolibri-acpi/network/udp.inc +++ b/kernel/branches/Kolibri-acpi/network/udp.inc @@ -1,155 +1,325 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; ;; -;; UDP.INC ;; -;; ;; -;; UDP Processes for Menuet OS TCP/IP stack ;; -;; ;; -;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; -;; ;; -;; See file COPYING for details ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; UDP.INC ;; +;; ;; +;; Part of the tcp/ip network stack for KolibriOS ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; 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 -; -; UDP 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 -; -; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -; | Source Port | Destination Port | -; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -; | Length ( UDP Header + Data ) | Checksum | -; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -; | UDP Data | -; +-+-+-.......... -+ -; +align 4 +uglobal + UDP_PACKETS_TX rd MAX_NET_DEVICES + UDP_PACKETS_RX rd MAX_NET_DEVICES +endg -struc UDP_PACKET -{ .SourcePort dw ? ;+00 - .DestinationPort dw ? ;+02 - .Length dw ? ;+04 - Length of (UDP Header + Data) - .Checksum dw ? ;+06 - .Data db ? ;+08 + +;----------------------------------------------------------------- +; +; UDP_init +; +; This function resets all UDP variables +; +;----------------------------------------------------------------- +macro UDP_init { + + 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 + +; 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 :) + +} -;*************************************************************************** -; Function -; udp_rx [by Johnny_B] +;----------------------------------------------------------------- ; -; 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 - ; Look for a socket where - ; IP Packet UDP Destination Port = local Port - ; IP Packet SA = Remote IP + neg [esi + UDP_header.Checksum] ; substract checksum from 0 + jz .no_checksum ; if checksum is zero, it is considered valid - mov ax, [edx + 20 + UDP_PACKET.DestinationPort] ; get the local port from - ; the IP packet's UDP header + ; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct - mov ebx, net_sockets + 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 cx, [esi + UDP_header.SourcePort] + mov dx, [esi + UDP_header.DestinationPort] + mov edi, [edi + 4] ; ipv4 source address + mov eax, 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 - ret -endp + 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 + + + + +;----------------------------------------------------------------- +; +; 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 diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/PrimaryLoader.txt b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/PrimaryLoader.txt index 510f24309..eddd3f163 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/PrimaryLoader.txt +++ b/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 не будут модифицированы ядром. diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/fat.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/fat.inc index 66130eabd..2399f48d3 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/fat.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/fat.inc @@ -31,322 +31,322 @@ ; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file has been loaded, bx=2 - file not found ; out: dx:ax = file size (0xFFFFFFFF if file not found) load_file_fat: - mov eax, [bp + root_clus - dat] - mov [bp + cur_obj - dat], root_string - push es - push bx - push cx + mov eax, [bp + root_clus - dat] + mov [bp + cur_obj - dat], root_string + push es + push bx + push cx .parse_dir_loop: ; convert name to FAT name - push [bp + cur_obj - dat] - push ax - mov [bp + cur_obj - dat], si - push ss - pop es + push [bp + cur_obj - dat] + push ax + mov [bp + cur_obj - dat], si + push ss + pop es ; convert ASCIIZ filename to FAT name - mov di, fat_filename - push di - mov cx, 8+3 - mov al, ' ' - rep stosb - pop di - mov cl, 8 ; 8 symbols per name - mov bl, 1 + mov di, fat_filename + push di + mov cx, 8+3 + mov al, ' ' + rep stosb + pop di + mov cl, 8 ; 8 symbols per name + mov bl, 1 .nameloop: - lodsb - test al, al - jz .namedone - cmp al, '/' - jz .namedone - cmp al, '.' - jz .namedot - dec cx - js .badname - cmp al, 'a' - jb @f - cmp al, 'z' - ja @f - sub al, 'a'-'A' + lodsb + test al, al + jz .namedone + cmp al, '/' + jz .namedone + cmp al, '.' + jz .namedot + dec cx + js .badname + cmp al, 'a' + jb @f + cmp al, 'z' + ja @f + sub al, 'a'-'A' @@: - stosb - jmp .nameloop + stosb + jmp .nameloop .namedot: - inc bx - jp .badname - add di, cx - mov cl, 3 - jmp .nameloop + inc bx + jp .badname + add di, cx + mov cl, 3 + jmp .nameloop .badname: - mov si, badname_msg - jmp find_error_si + mov si, badname_msg + jmp find_error_si .namedone: ; scan directory - pop ax ; eax = cluster of directory - ; high word of eax is preserved by operations above - push ds - push si + pop ax ; eax = cluster of directory + ; high word of eax is preserved by operations above + push ds + push si ; read a folder sector-by-sector and scan ; first, try to use the cache - push ss - pop ds - mov bx, -2 - mov cx, [bp + rootcache_size - dat] - cmp [bp + root_clus - dat], eax - jz .lookcache_root - mov di, foldcache_mark - xor bx, bx - mov cx, [bp + cachelimit - dat] + push ss + pop ds + mov bx, -2 + mov cx, [bp + rootcache_size - dat] + cmp [bp + root_clus - dat], eax + jz .lookcache_root + mov di, foldcache_mark + xor bx, bx + mov cx, [bp + cachelimit - dat] @@: - lea si, [di+bx] - mov edx, dword [foldcache_clus+si-foldcache_mark+bx] - cmp edx, eax - jz .cacheok - test edx, edx - jz .cacheadd ; the cache has place for new entry - inc bx - inc bx - dec cx - js @b + lea si, [di+bx] + mov edx, dword [foldcache_clus+si-foldcache_mark+bx] + cmp edx, eax + jz .cacheok + test edx, edx + jz .cacheadd ; the cache has place for new entry + inc bx + inc bx + dec cx + js @b ; the folder is not present in the cache, so add it ; the cache is full; find the oldest entry and replace it with the new one - mov bx, -2 - mov dx, [bp + cachelimit - dat] + mov bx, -2 + mov dx, [bp + cachelimit - dat] @@: - inc bx - inc bx - cmp word [di+bx], dx ; marks have values 0 through [cachelimit] - jnz @b + inc bx + inc bx + cmp word [di+bx], dx ; marks have values 0 through [cachelimit] + jnz @b .cacheadd: - or word [di+bx], 0xFFFF ; very big value, it will be changed soon - and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet - lea si, [di+bx] - mov dword [foldcache_clus+si-foldcache_mark+bx], eax + or word [di+bx], 0xFFFF ; very big value, it will be changed soon + and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet + lea si, [di+bx] + mov dword [foldcache_clus+si-foldcache_mark+bx], eax .cacheok: ; update cache marks - mov dx, [di+bx] - mov cx, [foldcache_size+di-foldcache_mark+bx] - mov di, [bp + cachelimit - dat] - add di, di + mov dx, [di+bx] + mov cx, [foldcache_size+di-foldcache_mark+bx] + mov di, [bp + cachelimit - dat] + add di, di .cacheupdate: - cmp [foldcache_mark+di], dx - adc [foldcache_mark+di], 0 - dec di - dec di - jns .cacheupdate - and [foldcache_mark+bx], 0 + cmp [foldcache_mark+di], dx + adc [foldcache_mark+di], 0 + dec di + dec di + jns .cacheupdate + and [foldcache_mark+bx], 0 ; done, bx contains (position in cache)*2 .lookcache_root: ; bx = (position in cache)*2 for non-root folders; bx = -2 for root folder - ;mov dx, bx - ;shl dx, 8 - ;add dx, 0x9200 - lea dx, [bx + 0x92] - xchg dl, dh - mov ds, dx - mov si, fat_filename ; ss:si -> filename in FAT style - call fat_scan_for_filename - jz .lookup_done + ;mov dx, bx + ;shl dx, 8 + ;add dx, 0x9200 + lea dx, [bx + 0x92] + xchg dl, dh + mov ds, dx + mov si, fat_filename ; ss:si -> filename in FAT style + call fat_scan_for_filename + jz .lookup_done ; cache miss, read folder data from disk ; we are reading parent directory, it can result in disk read errors; restore [cur_obj] - mov di, sp - mov bx, [bp + cur_obj - dat] - xchg bx, [ss:di+4] - mov [bp + cur_obj - dat], bx - mov bx, cx - add bx, 0xF - shr bx, 4 - shl cx, 5 - mov di, cx ; es:di -> free space in cache entry + mov di, sp + mov bx, [bp + cur_obj - dat] + xchg bx, [ss:di+4] + mov [bp + cur_obj - dat], bx + mov bx, cx + add bx, 0xF + shr bx, 4 + shl cx, 5 + mov di, cx ; es:di -> free space in cache entry ; external loop: scan clusters .folder_next_cluster: ; internal loop: scan sectors in cluster - movzx ecx, byte [ss:0x320D] ; BPB_SecPerClus - push eax + movzx ecx, byte [ss:0x320D] ; BPB_SecPerClus + push eax ; FAT12/16 root - special handling - test eax, eax - jnz .folder_notroot - mov cx, [ss:0x3211] ; BPB_RootEntCnt - mov dx, cx - add cx, 0xF - rcr cx, 1 - shr cx, 3 - mov eax, [bp + root_start - dat] - jmp .folder_next_sector + test eax, eax + jnz .folder_notroot + mov cx, [ss:0x3211] ; BPB_RootEntCnt + mov dx, cx + add cx, 0xF + rcr cx, 1 + shr cx, 3 + mov eax, [bp + root_start - dat] + jmp .folder_next_sector .folder_notroot: - mul ecx - add eax, [bp + data_start - dat] + mul ecx + add eax, [bp + data_start - dat] .folder_next_sector: - sub dx, 0x10 + sub dx, 0x10 ; skip first bx sectors - dec bx - jns .folder_skip_sector - push cx - push es di - push 0x8000 - pop es - xor bx, bx - mov cx, 1 - push es - call read - jc ..found_disk_error + dec bx + jns .folder_skip_sector + push cx + push es di + push 0x8000 + pop es + xor bx, bx + mov cx, 1 + push es + call read + jc ..found_disk_error ; copy data to the cache... - pop ds - pop di es - cmp di, 0x2000 ; ...if there is free space, of course - jae @f - pusha - mov cx, 0x100 - xor si, si - rep movsw - mov di, es - shr di, 8 - cmp di, 0x90 - jz .update_rootcache_size - add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache - jmp .updated_cachesize + pop ds + pop di es + cmp di, 0x2000 ; ...if there is free space, of course + jae @f + pusha + mov cx, 0x100 + xor si, si + rep movsw + mov di, es + shr di, 8 + cmp di, 0x90 + jz .update_rootcache_size + add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache + jmp .updated_cachesize .update_rootcache_size: - mov cl, 0x10 - cmp cx, dx - jb @f - mov cx, dx + mov cl, 0x10 + cmp cx, dx + jb @f + mov cx, dx @@: - add [bp + rootcache_size - dat], cx + add [bp + rootcache_size - dat], cx .updated_cachesize: - popa + popa @@: - push es - mov cl, 0x10 ; ch=0 at this point - cmp cx, dx - jb @f - mov cx, dx + push es + mov cl, 0x10 ; ch=0 at this point + cmp cx, dx + jb @f + mov cx, dx @@: - call fat_scan_for_filename - pop es - pop cx - jz .lookup_done_pop + call fat_scan_for_filename + pop es + pop cx + jz .lookup_done_pop .folder_skip_sector: - inc eax - loop .folder_next_sector - pop eax ; eax = current cluster - test eax, eax - jz @f - call [bp + get_next_cluster_ptr - dat] - jc .folder_next_cluster + inc eax + loop .folder_next_sector + pop eax ; eax = current cluster + test eax, eax + jz @f + call [bp + get_next_cluster_ptr - dat] + jc .folder_next_cluster @@: - stc - push eax + stc + push eax .lookup_done_pop: - pop eax + pop eax .lookup_done: - pop si + pop si ; CF=1 <=> failed - jnc .found - pop ds - pop [bp + cur_obj - dat] - mov si, error_not_found - jmp find_error_si + jnc .found + pop ds + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si .found: - mov eax, [di+20-2] - mov edx, [di+28] - mov ax, [di+26] ; get cluster - test byte [di+11], 10h ; directory? - pop ds - pop [bp + cur_obj - dat] ; forget old [cur_obj] - jz .regular_file - cmp byte [si-1], 0 - jnz .parse_dir_loop + mov eax, [di+20-2] + mov edx, [di+28] + mov ax, [di+26] ; get cluster + test byte [di+11], 10h ; directory? + pop ds + pop [bp + cur_obj - dat] ; forget old [cur_obj] + jz .regular_file + cmp byte [si-1], 0 + jnz .parse_dir_loop ..directory_error: - mov si, directory_string - jmp find_error_si + mov si, directory_string + jmp find_error_si .regular_file: - cmp byte [si-1], 0 - jz @f + cmp byte [si-1], 0 + jz @f ..notdir_error: - mov si, notdir_string - jmp find_error_si + mov si, notdir_string + jmp find_error_si @@: ; ok, we have found a regular file and the caller requested it ; parse FAT chunk - push ss - pop es - push ss - pop ds - mov di, 0x4005 - mov byte [di-5], 1 ; non-resident attribute - mov dword [di-4], 1 - stosd - pop cx - push cx + push ss + pop es + push ss + pop ds + mov di, 0x4005 + mov byte [di-5], 1 ; non-resident attribute + mov dword [di-4], 1 + stosd + pop cx + push cx .parsefat: - call [bp + get_next_cluster_ptr - dat] - jnc .done - mov esi, [di-8] - add esi, [di-4] - cmp eax, esi - jz .contc - mov dword [di], 1 - scasd - stosd - jmp @f + call [bp + get_next_cluster_ptr - dat] + jnc .done + mov esi, [di-8] + add esi, [di-4] + cmp eax, esi + jz .contc + mov dword [di], 1 + scasd + stosd + jmp @f .contc: - inc dword [di-8] + inc dword [di-8] @@: - sub cl, [0x320D] - sbb ch, 0 - ja .parsefat + sub cl, [0x320D] + sbb ch, 0 + ja .parsefat .done: - xor eax, eax - stosd - mov si, 0x4000 + xor eax, eax + stosd + mov si, 0x4000 load_file_common_end: - xor ecx, ecx - pop cx - pop bx - pop es - mov [bp + filesize - dat], edx - mov [bp + sectors_read - dat], ecx - add edx, 0x1FF - shr edx, 9 - mov [bp + filesize_sectors - dat], edx - cmp edx, ecx - seta al - mov ah, 0 - push ax - call read_file_chunk + xor ecx, ecx + pop cx + pop bx + pop es + mov [bp + filesize - dat], edx + mov [bp + sectors_read - dat], ecx + add edx, 0x1FF + shr edx, 9 + mov [bp + filesize_sectors - dat], edx + cmp edx, ecx + seta al + mov ah, 0 + push ax + call read_file_chunk continue_load_common_end: - mov [bp + cur_chunk_ptr - dat], si - pop bx - mov ax, word [bp + filesize - dat] - mov dx, word [bp + filesize+2 - dat] - jnc @f - mov bl, 3 ; read error + mov [bp + cur_chunk_ptr - dat], si + pop bx + mov ax, word [bp + filesize - dat] + mov dx, word [bp + filesize+2 - dat] + jnc @f + mov bl, 3 ; read error @@: - ret + ret continue_load_file: ; es:bx -> buffer for output, ecx = cx = number of sectors - mov si, [bp + cur_chunk_ptr - dat] - push ecx - add ecx, [bp + sectors_read - dat] - mov [bp + sectors_read - dat], ecx - cmp [bp + filesize_sectors - dat], ecx - pop ecx - seta al - mov ah, 0 - push ax - push continue_load_common_end - push ss - pop ds - cmp [bp + cur_chunk_resident - dat], ah - jnz .nonresident + mov si, [bp + cur_chunk_ptr - dat] + push ecx + add ecx, [bp + sectors_read - dat] + mov [bp + sectors_read - dat], ecx + cmp [bp + filesize_sectors - dat], ecx + pop ecx + seta al + mov ah, 0 + push ax + push continue_load_common_end + push ss + pop ds + cmp [bp + cur_chunk_resident - dat], ah + jnz .nonresident .resident: - mov ax, word [bp + num_sectors - dat] - jmp read_file_chunk.resident.continue + mov ax, word [bp + num_sectors - dat] + jmp read_file_chunk.resident.continue .nonresident: - mov eax, [bp + cur_cluster - dat] - mov edx, [bp + num_sectors - dat] - add eax, [bp + cur_delta - dat] - jmp read_file_chunk.nonresident.continue + mov eax, [bp + cur_cluster - dat] + mov edx, [bp + num_sectors - dat] + add eax, [bp + cur_delta - dat] + jmp read_file_chunk.nonresident.continue fat_scan_for_filename: ; in: ss:si -> 11-bytes FAT name @@ -355,52 +355,52 @@ fat_scan_for_filename: ; out: if found: CF=0, ZF=1, es:di -> directory entry ; out: if not found, but continue required: CF=1 and ZF=0 ; out: if not found and zero item reached: CF=1 and ZF=1 - push ds - pop es - xor di, di - push cx - jcxz .noent + push ds + pop es + xor di, di + push cx + jcxz .noent .loop: - cmp byte [di], 0 - jz .notfound - test byte [di+11], 8 ; volume label? - jnz .cont ; ignore volume labels - pusha - mov cx, 11 - repz cmps byte [ss:si], byte [es:di] - popa - jz .done + cmp byte [di], 0 + jz .notfound + test byte [di+11], 8 ; volume label? + jnz .cont ; ignore volume labels + pusha + mov cx, 11 + repz cmps byte [ss:si], byte [es:di] + popa + jz .done .cont: - add di, 0x20 - loop .loop + add di, 0x20 + loop .loop .noent: - inc cx ; clear ZF flag + inc cx ; clear ZF flag .notfound: - stc + stc .done: - pop cx - ret + pop cx + ret fat12_get_next_cluster: ; in: ax = cluster (high word of eax is zero) ; out: if there is next cluster: CF=1, ax = next cluster ; out: if there is no next cluster: CF=0 - push si - push ds - push 0x6000 - pop ds - mov si, ax - shr si, 1 - add si, ax - test al, 1 - lodsw - jz @f - shr ax, 4 + push si + push ds + push 0x6000 + pop ds + mov si, ax + shr si, 1 + add si, ax + test al, 1 + lodsw + jz @f + shr ax, 4 @@: - and ax, 0xFFF - cmp ax, 0xFF7 - pop ds si - ret + and ax, 0xFFF + cmp ax, 0xFF7 + pop ds si + ret fat16_get_next_cluster: ; in: ax = cluster (high word of eax is zero) @@ -408,102 +408,102 @@ fat16_get_next_cluster: ; out: if there is no next cluster: CF=0 ; each sector contains 200h bytes = 100h FAT entries ; so ah = # of sector, al = offset in sector - push si - mov si, ax - shr si, 8 + push si + mov si, ax + shr si, 8 ; calculate segment for this sector of FAT table ; base for FAT table is 6000:0000, so the sector #si has to be loaded to (60000 + 200*si) ; segment = 6000 + 20*si, offset = 0 - push es - push si - shl si, 5 - add si, 0x6000 - mov es, si - pop si - cmp byte [ss:0x3400+si], 0 ; sector already loaded? - jnz .noread + push es + push si + shl si, 5 + add si, 0x6000 + mov es, si + pop si + cmp byte [ss:0x3400+si], 0 ; sector already loaded? + jnz .noread ; load corresponding sector, try all FATs if disk read error detected - pusha - movzx di, byte [ss:0x3210] ; BPB_NumFATs - xor bx, bx - mov ax, [ss:0x320E] ; BPB_RsvdSecCnt - xor dx, dx - add ax, si - adc dx, bx + pusha + movzx di, byte [ss:0x3210] ; BPB_NumFATs + xor bx, bx + mov ax, [ss:0x320E] ; BPB_RsvdSecCnt + xor dx, dx + add ax, si + adc dx, bx @@: - push es - push dx ax - pop eax - mov cx, 1 ; read 1 sector - call read - pop es - jnc @f - add ax, [ss:0x3216] ; BPB_FATSz16 - adc dx, bx - dec di - jnz @b + push es + push dx ax + pop eax + mov cx, 1 ; read 1 sector + call read + pop es + jnc @f + add ax, [ss:0x3216] ; BPB_FATSz16 + adc dx, bx + dec di + jnz @b ..found_disk_error: - mov si, disk_error_msg - jmp find_error_si + mov si, disk_error_msg + jmp find_error_si @@: - popa + popa .noread: - mov si, ax - and si, 0xFF - add si, si - mov ax, [es:si] - pop es - cmp ax, 0xFFF7 - pop si - ret + mov si, ax + and si, 0xFF + add si, si + mov ax, [es:si] + pop es + cmp ax, 0xFFF7 + pop si + ret fat32_get_next_cluster: ; in: eax = cluster ; out: if there is next cluster: CF=1, eax = next cluster ; out: if there is no next cluster: CF=0 - push di - push ax - shr eax, 7 + push di + push ax + shr eax, 7 ; eax = FAT sector number; look in cache - push si - mov si, cache1head - call cache_lookup - pop si - jnc .noread + push si + mov si, cache1head + call cache_lookup + pop si + jnc .noread ; read FAT, try all FATs if disk read error detected - push es - pushad - movzx edx, word [ss:0x320E] ; BPB_RsvdSecCnt - add eax, edx - movzx si, byte [ss:0x3210] ; BPB_NumFATs + push es + pushad + movzx edx, word [ss:0x320E] ; BPB_RsvdSecCnt + add eax, edx + movzx si, byte [ss:0x3210] ; BPB_NumFATs @@: - lea cx, [di - 0x3400 + (0x6000 shr (9-3))] - shl cx, 9-3 - mov es, cx - xor bx, bx - mov cx, 1 - call read - jnc @f - add eax, [ss:0x3224] ; BPB_FATSz32 - dec si - jnz @b - jmp ..found_disk_error + lea cx, [di - 0x3400 + (0x6000 shr (9-3))] + shl cx, 9-3 + mov es, cx + xor bx, bx + mov cx, 1 + call read + jnc @f + add eax, [ss:0x3224] ; BPB_FATSz32 + dec si + jnz @b + jmp ..found_disk_error @@: - popad - pop es + popad + pop es .noread: ; get requested item - lea ax, [di - 0x3400 + (0x6000 shr (9-3))] - pop di - and di, 0x7F - shl di, 2 - shl ax, 9-3 - push ds - mov ds, ax - and byte [di+3], 0x0F - mov eax, [di] - pop ds - pop di - ;and eax, 0x0FFFFFFF - cmp eax, 0x0FFFFFF7 - ret + lea ax, [di - 0x3400 + (0x6000 shr (9-3))] + pop di + and di, 0x7F + shl di, 2 + shl ax, 9-3 + push ds + mov ds, ax + and byte [di+3], 0x0F + mov eax, [di] + pop ds + pop di + ;and eax, 0x0FFFFFFF + cmp eax, 0x0FFFFFF7 + ret diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/kordldr.win.txt b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/kordldr.win.txt index 703c5a447..3affd3f39 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/kordldr.win.txt +++ b/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. Добавляет сектор в конец списка (самый новый вход). diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/ntfs.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/ntfs.inc index f92f13caa..2d1353e8d 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/ntfs.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/ntfs.inc @@ -27,376 +27,376 @@ restore_usa: ; Update Sequence Array restore ; in: ds:bx -> USA-protected structure - push bx - lea di, [bx+1feh] - mov cx, [bx+6] - add bx, [bx+4] - dec cx + push bx + lea di, [bx+1feh] + mov cx, [bx+6] + add bx, [bx+4] + dec cx @@: - mov ax, [bx+2] - mov [di], ax - inc bx - inc bx - add di, 200h - loop @b - pop bx - ret + mov ax, [bx+2] + mov [di], ax + inc bx + inc bx + add di, 200h + loop @b + pop bx + ret find_attr: ; in: ds:di->file record, ax=attribute ; out: ds:di->attribute or di=0 if not found - add di, [di+14h] + add di, [di+14h] .1: ; attributes' codes are formally dwords, but all of them fit in word - cmp word [di], -1 - jz .notfound - cmp word [di], ax - jnz .continue + cmp word [di], -1 + jz .notfound + cmp word [di], ax + jnz .continue ; for $DATA attribute, scan only unnamed - cmp ax, 80h - jnz .found - cmp byte [di+9], 0 - jz .found + cmp ax, 80h + jnz .found + cmp byte [di+9], 0 + jz .found .continue: - add di, [di+4] - jmp .1 + add di, [di+4] + jmp .1 .notfound: - xor di, di + xor di, di .found: - ret + ret process_mcb_nonres: ; in: ds:si->attribute, es:di->buffer ; out: es:di->buffer end - pushad - pop di - add si, [si+20h] - xor ebx, ebx + pushad + pop di + add si, [si+20h] + xor ebx, ebx .loop: - lodsb - test al, al - jz .done - push invalid_read_request_string - movzx cx, al - shr cx, 4 - jz find_error_sp - xchg ax, dx - and dx, 0Fh - jz find_error_sp - add si, cx - add si, dx - pop ax - push si - dec si - movsx eax, byte [si] - dec cx - jz .l1e + lodsb + test al, al + jz .done + push invalid_read_request_string + movzx cx, al + shr cx, 4 + jz find_error_sp + xchg ax, dx + and dx, 0Fh + jz find_error_sp + add si, cx + add si, dx + pop ax + push si + dec si + movsx eax, byte [si] + dec cx + jz .l1e .l1: - dec si - shl eax, 8 - mov al, [si] - loop .l1 + dec si + shl eax, 8 + mov al, [si] + loop .l1 .l1e: - xchg ebp, eax - dec si - movsx eax, byte [si] - mov cx, dx - dec cx - jz .l2e + xchg ebp, eax + dec si + movsx eax, byte [si] + mov cx, dx + dec cx + jz .l2e .l2: - dec si - shl eax, 8 - mov al, byte [si] - loop .l2 + dec si + shl eax, 8 + mov al, byte [si] + loop .l2 .l2e: - pop si - add ebx, ebp + pop si + add ebx, ebp ; eax=length, ebx=disk block - stosd - mov eax, ebx - stosd - cmp di, 0x8000 - 12 - jbe .loop + stosd + mov eax, ebx + stosd + cmp di, 0x8000 - 12 + jbe .loop ..attr_overflow: - mov si, fragmented_string - jmp find_error_si + mov si, fragmented_string + jmp find_error_si .done: - xor ax, ax - stosw - stosw - push di - popad - ret + xor ax, ax + stosw + stosw + push di + popad + ret load_attr: ; in: ax=attribute, ds:bx->base record ; out: if found: CF=0, attribute loaded to [freeattr], [freeattr] updated, -; edx=size of attribute in bytes +; edx=size of attribute in bytes ; out: if not found: CF=1 - mov di, [bp + freeattr - dat] - push ss - pop es - mov byte [es:di], 1 - inc di - cmp di, 0x8000 - 12 - ja ..attr_overflow - or edx, -1 ; file size is not known yet + mov di, [bp + freeattr - dat] + push ss + pop es + mov byte [es:di], 1 + inc di + cmp di, 0x8000 - 12 + ja ..attr_overflow + or edx, -1 ; file size is not known yet ; scan for attribute - push di - mov di, bx - add di, [di+14h] + push di + mov di, bx + add di, [di+14h] @@: - call find_attr.1 - test di, di - jz .notfound1 - cmp byte [di+8], 0 - jnz .nonresident - mov si, di - pop di - push ds - jmp .resident + call find_attr.1 + test di, di + jz .notfound1 + cmp byte [di+8], 0 + jnz .nonresident + mov si, di + pop di + push ds + jmp .resident .aux_resident: - mov ax, ds - mov si, di - pop di ds bx ds edx - push ss - pop es - push ds - mov ds, ax + mov ax, ds + mov si, di + pop di ds bx ds edx + push ss + pop es + push ds + mov ds, ax ; resident attribute .resident: - dec di - mov al, 0 - stosb - mov ax, [si+10h] - stosw - push di - add di, ax - cmp di, 0x8000 - 12 - pop di - ja ..attr_overflow - movzx edx, ax ; length of attribute - xchg ax, cx - add si, [si+14h] - rep movsb - mov [bp + freeattr - dat], di - pop ds - ret + dec di + mov al, 0 + stosb + mov ax, [si+10h] + stosw + push di + add di, ax + cmp di, 0x8000 - 12 + pop di + ja ..attr_overflow + movzx edx, ax ; length of attribute + xchg ax, cx + add si, [si+14h] + rep movsb + mov [bp + freeattr - dat], di + pop ds + ret .nonresident: ; nonresident attribute - cmp dword [di+10h], 0 - jnz @b + cmp dword [di+10h], 0 + jnz @b ; read start of data - mov si, di - mov edx, [di+30h] ; size of attribute - pop di - call process_mcb_nonres - sub di, 4 - push di + mov si, di + mov edx, [di+30h] ; size of attribute + pop di + call process_mcb_nonres + sub di, 4 + push di .notfound1: - pop di - push edx + pop di + push edx ; $ATTRIBUTE_LIST is always in base file record - cmp ax, 20h - jz .nofragmented + cmp ax, 20h + jz .nofragmented ; try to load $ATTRIBUTE_LIST = 20h - push ax - mov ax, 20h - push [bp + freeattr - dat] - mov [bp + freeattr - dat], di - push di - call load_attr - pop di - pop [bp + freeattr - dat] - pop ax - jc .nofragmented - push ds bx - pusha - mov si, di - push ss - pop ds - push 0x8100 - pop es - xor ecx, ecx - mov cl, 0x78 - xor bx, bx - push es - call read_file_chunk - pop ds - jc ..found_disk_error - test cx, cx - jz ..attr_overflow - popa - push ss - pop es - xor bx, bx + push ax + mov ax, 20h + push [bp + freeattr - dat] + mov [bp + freeattr - dat], di + push di + call load_attr + pop di + pop [bp + freeattr - dat] + pop ax + jc .nofragmented + push ds bx + pusha + mov si, di + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + popa + push ss + pop es + xor bx, bx .1: - cmp [bx], ax - jnz .continue1 + cmp [bx], ax + jnz .continue1 ; only unnamed $DATA attributes! - cmp ax, 80h - jnz @f - cmp byte [bx+6], 0 - jnz .continue1 + cmp ax, 80h + jnz @f + cmp byte [bx+6], 0 + jnz .continue1 @@: - cmp dword [bx+10h], 0 - jz .continue1 - cmp dword [bx+8], 0 - jnz @f - dec di - cmp di, [bp + freeattr - dat] - lea di, [di+1] - jnz .continue1 + cmp dword [bx+10h], 0 + jz .continue1 + cmp dword [bx+8], 0 + jnz @f + dec di + cmp di, [bp + freeattr - dat] + lea di, [di+1] + jnz .continue1 @@: - push ds di - push ax - mov eax, [bx+10h] - mov ecx, [bx+8] - call read_file_record - pop ax - mov di, [14h] + push ds di + push ax + mov eax, [bx+10h] + mov ecx, [bx+8] + call read_file_record + pop ax + mov di, [14h] .2: - call find_attr.1 - cmp byte [di+8], 0 - jz .aux_resident - cmp dword [di+10h], ecx - jnz .2 - mov si, di - mov di, sp - cmp dword [ss:di+8], -1 - jnz @f - push dword [si+30h] ; size of attribute - pop dword [ss:di+8] + call find_attr.1 + cmp byte [di+8], 0 + jz .aux_resident + cmp dword [di+10h], ecx + jnz .2 + mov si, di + mov di, sp + cmp dword [ss:di+8], -1 + jnz @f + push dword [si+30h] ; size of attribute + pop dword [ss:di+8] @@: - pop di - call process_mcb_nonres - sub di, 4 - pop ds + pop di + call process_mcb_nonres + sub di, 4 + pop ds .continue1: - add bx, [bx+4] - cmp bx, dx - jb .1 - pop bx ds + add bx, [bx+4] + cmp bx, dx + jb .1 + pop bx ds .nofragmented: - pop edx - dec di - cmp di, [bp + freeattr - dat] - jnz @f - stc - ret + pop edx + dec di + cmp di, [bp + freeattr - dat] + jnz @f + stc + ret @@: - inc di - xor ax, ax - stosw - stosw - mov [bp + freeattr - dat], di - ret + inc di + xor ax, ax + stosw + stosw + mov [bp + freeattr - dat], di + ret read_file_record: ; in: eax = index of record ; out: ds:0 -> record ; find place in cache - push di - push si - mov si, cache1head - call cache_lookup - pop si - pushf - sub di, 3400h - shl di, 10-3 - add di, 0x6000 - mov ds, di - popf - pop di - jnc .noread + push di + push si + mov si, cache1head + call cache_lookup + pop si + pushf + sub di, 3400h + shl di, 10-3 + add di, 0x6000 + mov ds, di + popf + pop di + jnc .noread ; read file record to ds:0 - pushad - push ds - push es - movzx ecx, [bp + frs_size - dat] - shr cx, 9 - mul ecx - push ds - pop es - push ss - pop ds - mov si, 0x4000 - xor bx, bx - push [bp + cur_obj - dat] - mov [bp + cur_obj - dat], mft_string - push es - call read_attr + pushad + push ds + push es + movzx ecx, [bp + frs_size - dat] + shr cx, 9 + mul ecx + push ds + pop es + push ss + pop ds + mov si, 0x4000 + xor bx, bx + push [bp + cur_obj - dat] + mov [bp + cur_obj - dat], mft_string + push es + call read_attr ; initialize cache for $INDEX_ALLOCATION for this record - pop si - push si - sub si, 0x6000 - mov ax, si - shr si, 10-3 - shr ax, 2 - add si, 3480h - add ax, 3500h - mov [si], si - mov [si+2], si - mov [si+4], ax - pop ds - call restore_usa - pop [bp + cur_obj - dat] - pop es - pop ds - popad + pop si + push si + sub si, 0x6000 + mov ax, si + shr si, 10-3 + shr ax, 2 + add si, 3480h + add ax, 3500h + mov [si], si + mov [si+2], si + mov [si+4], ax + pop ds + call restore_usa + pop [bp + cur_obj - dat] + pop es + pop ds + popad .noread: - ret + ret read_attr: ; in: eax = offset in sectors, ecx = size in sectors (<10000h), es:bx -> buffer, ds:si -> attribute - push invalid_read_request_string - cmp byte [si], 0 - jnz .nonresident - cmp eax, 10000h shr 9 - jae find_error_sp - shl ax, 9 - shl cx, 9 - cmp ax, [si+2] - jae find_error_sp - cmp cx, [si+2] - ja find_error_sp - add si, 3 - add si, ax - mov di, bx - rep movsb - pop ax - ret + push invalid_read_request_string + cmp byte [si], 0 + jnz .nonresident + cmp eax, 10000h shr 9 + jae find_error_sp + shl ax, 9 + shl cx, 9 + cmp ax, [si+2] + jae find_error_sp + cmp cx, [si+2] + ja find_error_sp + add si, 3 + add si, ax + mov di, bx + rep movsb + pop ax + ret .nonresident: - inc si + inc si .loop: - mov edx, dword [si] - add si, 8 - test edx, edx - jz find_error_sp - imul edx, [bp + sect_per_clust - dat] - sub eax, edx - jnc .loop - add eax, edx - sub edx, eax - push cx - cmp ecx, edx - jb @f - mov cx, dx + mov edx, dword [si] + add si, 8 + test edx, edx + jz find_error_sp + imul edx, [bp + sect_per_clust - dat] + sub eax, edx + jnc .loop + add eax, edx + sub edx, eax + push cx + cmp ecx, edx + jb @f + mov cx, dx @@: - push bx - mov ebx, [si-4] - imul ebx, [bp + sect_per_clust - dat] - add eax, ebx - pop bx - call read - jc ..found_disk_error - mov dx, cx - pop cx - xor eax, eax - sub cx, dx - jnz .loop - pop ax - ret + push bx + mov ebx, [si-4] + imul ebx, [bp + sect_per_clust - dat] + add eax, ebx + pop bx + call read + jc ..found_disk_error + mov dx, cx + pop cx + xor eax, eax + sub cx, dx + jnz .loop + pop ax + ret load_file_ntfs: ; in: ss:bp = 0:dat @@ -405,183 +405,183 @@ load_file_ntfs: ; in: cx = limit in sectors ; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part has been loaded, bx=2 - file not found ; out: dx:ax = file size (0xFFFFFFFF if file not found) - push es bx cx - mov eax, 5 ; root cluster - mov [bp + cur_obj - dat], root_string + push es bx cx + mov eax, 5 ; root cluster + mov [bp + cur_obj - dat], root_string .parse_dir_loop: - push ds si - call read_file_record + push ds si + call read_file_record ; find attributes $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP - mov ax, [bp + freeattr - dat] - mov [bp + index_root - dat], ax - mov ax, 90h ; $INDEX_ROOT - xor bx, bx - call load_attr - mov si, noindex_string - jc find_error_si - mov ax, [bp + freeattr - dat] - mov [bp + index_alloc - dat], ax - mov ax, 0A0h ; $INDEX_ALLOCATION - call load_attr - jnc @f - mov [bp + index_alloc - dat], bx + mov ax, [bp + freeattr - dat] + mov [bp + index_root - dat], ax + mov ax, 90h ; $INDEX_ROOT + xor bx, bx + call load_attr + mov si, noindex_string + jc find_error_si + mov ax, [bp + freeattr - dat] + mov [bp + index_alloc - dat], ax + mov ax, 0A0h ; $INDEX_ALLOCATION + call load_attr + jnc @f + mov [bp + index_alloc - dat], bx @@: - push ds + push ds ; search for entry - mov si, [bp + index_root - dat] - push ss - pop ds - push 0x8100 - pop es - xor ecx, ecx - mov cl, 0x78 - xor bx, bx - push es - call read_file_chunk - pop ds - jc ..found_disk_error - test cx, cx - jz ..attr_overflow - mov si, invalid_read_request_string - cmp word [bx+10], 0 - jnz find_error_si + mov si, [bp + index_root - dat] + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + mov si, invalid_read_request_string + cmp word [bx+10], 0 + jnz find_error_si ; calculate number of items in cache - mov di, [bx+8] ; subnode_size - mov ax, 0x4000 - sub ax, word [bp + frs_size - dat] - cwd - div di - test ax, ax - jz find_error_si - mov si, invalid_volume_msg - test di, 0x1FF - jnz find_error_si - pop cx - mov [bp + cur_index_seg - dat], cx - shl ax, 3 - sub cx, 6000h - mov si, cx - shr cx, 2 - shr si, 10-3 - add cx, ax - add si, 3480h - mov [bp + cur_index_cache - dat], si - add cx, 3500h - mov [ss:si+6], cx - mov dx, di - add bx, 10h + mov di, [bx+8] ; subnode_size + mov ax, 0x4000 + sub ax, word [bp + frs_size - dat] + cwd + div di + test ax, ax + jz find_error_si + mov si, invalid_volume_msg + test di, 0x1FF + jnz find_error_si + pop cx + mov [bp + cur_index_seg - dat], cx + shl ax, 3 + sub cx, 6000h + mov si, cx + shr cx, 2 + shr si, 10-3 + add cx, ax + add si, 3480h + mov [bp + cur_index_cache - dat], si + add cx, 3500h + mov [ss:si+6], cx + mov dx, di + add bx, 10h .scan_record: - add bx, [bx] + add bx, [bx] .scan: - test byte [bx+0Ch], 2 - jnz .look_child - movzx cx, byte [bx+50h] ; namelen - lea di, [bx+52h] ; name - push ds - pop es - pop si ds - push ds si - xor ax, ax + test byte [bx+0Ch], 2 + jnz .look_child + movzx cx, byte [bx+50h] ; namelen + lea di, [bx+52h] ; name + push ds + pop es + pop si ds + push ds si + xor ax, ax .1: - lodsb - cmp al, '/' - jnz @f - mov al, 0 + lodsb + cmp al, '/' + jnz @f + mov al, 0 @@: - cmp al, 'A' - jb .nocapital - cmp al, 'Z' - ja .nocapital - or al, 20h + cmp al, 'A' + jb .nocapital + cmp al, 'Z' + ja .nocapital + or al, 20h .nocapital: - cmp al, 'a' - jb .notletter - cmp al, 'z' - ja .notletter - or byte [es:di], 20h + cmp al, 'a' + jb .notletter + cmp al, 'z' + ja .notletter + or byte [es:di], 20h .notletter: - scasw - loopz .1 - jb .look_child - ja @f - cmp byte [si], 0 - jz .file_found - cmp byte [si], '/' - jz .file_found + scasw + loopz .1 + jb .look_child + ja @f + cmp byte [si], 0 + jz .file_found + cmp byte [si], '/' + jz .file_found @@: - push es - pop ds - add bx, [bx+8] - jmp .scan + push es + pop ds + add bx, [bx+8] + jmp .scan .look_child: - push es - pop ds - test byte [bx+0Ch], 1 - jz .not_found - mov si, [bp + index_alloc - dat] - test si, si - jz .not_found - add bx, [bx+8] - mov eax, [bx-8] - mov es, [bp + cur_index_seg - dat] - push si - mov si, [bp + cur_index_cache - dat] - call cache_lookup - pop si - pushf - mov bx, di - mov bh, 0 - shr bx, 3 - imul bx, dx - add bx, [bp + frs_size - dat] - popf - jnc .noread - push es - push dx - push ss - pop ds - movzx ecx, dx - shr cx, 9 - mul [bp + sect_per_clust - dat] - call read_attr - pop dx - pop es - push es - pop ds - call restore_usa + push es + pop ds + test byte [bx+0Ch], 1 + jz .not_found + mov si, [bp + index_alloc - dat] + test si, si + jz .not_found + add bx, [bx+8] + mov eax, [bx-8] + mov es, [bp + cur_index_seg - dat] + push si + mov si, [bp + cur_index_cache - dat] + call cache_lookup + pop si + pushf + mov bx, di + mov bh, 0 + shr bx, 3 + imul bx, dx + add bx, [bp + frs_size - dat] + popf + jnc .noread + push es + push dx + push ss + pop ds + movzx ecx, dx + shr cx, 9 + mul [bp + sect_per_clust - dat] + call read_attr + pop dx + pop es + push es + pop ds + call restore_usa .noread: - push es - pop ds - add bx, 18h - jmp .scan_record + push es + pop ds + add bx, 18h + jmp .scan_record .not_found: - pop [bp + cur_obj - dat] - mov si, error_not_found - jmp find_error_si + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si .file_found: - pop [bp + cur_obj - dat] - pop cx - mov ax, [bp + index_root - dat] - mov [bp + freeattr - dat], ax - mov eax, [es:bx] - test byte [es:bx+48h+3], 10h - jz .regular_file - cmp byte [si], 0 - jz ..directory_error - inc si - jmp .parse_dir_loop + pop [bp + cur_obj - dat] + pop cx + mov ax, [bp + index_root - dat] + mov [bp + freeattr - dat], ax + mov eax, [es:bx] + test byte [es:bx+48h+3], 10h + jz .regular_file + cmp byte [si], 0 + jz ..directory_error + inc si + jmp .parse_dir_loop .regular_file: - cmp byte [si], 0 - jnz ..notdir_error + cmp byte [si], 0 + jnz ..notdir_error ; read entry - call read_file_record - xor bx, bx - mov ax, 80h - call load_attr - mov si, nodata_string - jc find_error_si - mov si, [bp + index_root - dat] - mov [bp + freeattr - dat], si - push ss - pop ds - jmp load_file_common_end + call read_file_record + xor bx, bx + mov ax, 80h + call load_attr + mov si, nodata_string + jc find_error_si + mov si, [bp + index_root - dat] + mov [bp + freeattr - dat], si + push ss + pop ds + jmp load_file_common_end diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.asm index 4d03989b3..9c0072afb 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.asm @@ -417,7 +417,7 @@ parse_dir: jbe @f push cx mov cx, 8000h - rep movsw + rep movsw mov cx, ds add cx, 1000h mov ds, cx @@ -429,7 +429,7 @@ parse_dir: @@: add cx, 1000h shl cx, 3 - rep movsw + rep movsw pop si pop ds ; correct positions in cache for existing items @@ -491,7 +491,7 @@ parse_dir: xor di, di mov cx, 0x1800 sub cx, si - rep movsb + rep movsb pop ax push di popa @@ -879,7 +879,7 @@ read_many_bytes.with_first: @@: pop di sub [cur_limit], ecx - rep movsb + rep movsb mov esi, ebx mov bx, di call normalize @@ -921,7 +921,7 @@ read_many_bytes.with_first: mov si, 1000h sub word [cur_limit], cx sbb word [cur_limit+2], 0 - rep movsb + rep movsb mov bx, di call normalize .nopost: diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.txt b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.txt index 969c52bba..7fedad177 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.txt +++ b/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. Если же нет, то считываются все сектора, кроме последнего, после чего + последний сектор считывается отдельно во временную область, и уже + оттуда нужная часть данных копируется в буфер. diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.asm index 772829f06..033257c54 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.asm @@ -291,7 +291,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmpsb + repz cmpsb popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.txt b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.txt index 26a9621e1..c0449aa69 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.txt +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.asm index 3dbee8865..eb0b7c936 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.asm +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.asm @@ -284,7 +284,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmpsb + repz cmpsb popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.txt b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.txt index 858b52a8b..caabcb33f 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.txt +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/kordldr.f32.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/kordldr.f32.asm index 9913b442b..fa7d73bae 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/kordldr.f32.asm +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/kordldr.f32.asm @@ -111,7 +111,7 @@ nomem: mov di, foldcache_clus mov cx, 8*4/2 + 1 xor ax, ax - rep stosw + rep stosw ; bootsector code caches one FAT sector, [bp-14], in 6000:0000 ; initialize our (more advanced) FAT caching from this mov di, 8400h @@ -167,7 +167,7 @@ kordldr_full: pop es xor si, si xor di, di - rep movsw + rep movsw pop ds ; ...continue loading... mov di, secondary_loader_info @@ -310,7 +310,7 @@ filename equ bp push di mov cx, 8+3 mov al, ' ' - rep stosb + rep stosb pop di mov cl, 8 ; 8 symbols per name mov bl, 1 @@ -434,7 +434,7 @@ folder_next_sector: pusha mov cx, 0x100 xor si, si - rep movsw + rep movsw mov di, es shr di, 8 add [ss:foldcache_size+di-0x90], 0x10 ; 0x10 new entries in the cache @@ -591,7 +591,7 @@ sloop: jnz scont ; ignore volume labels pusha mov cx, 11 - repz cmps byte [ss:si], byte [es:di] + repz cmps byte [ss:si], byte [es:di] popa jz sdone scont: diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/debug_msg.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/debug_msg.inc index 42a5c2804..a0d8dff67 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/debug_msg.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/debug_msg.inc @@ -24,14 +24,14 @@ ; 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 -show_string db 'Have loaded size:' -show_decode db ' ',0 -show_db1 db ' -Message debug1',0 -show_db2 db ' -Message debug2',0 +cseg_msg db ' - Adress of code segment',0 +stack_msg db 'Set stack & segments is have completed',0 +show_string db 'Have loaded size:' +show_decode db ' ',0 +show_db1 db ' -Message debug1',0 +show_db2 db ' -Message debug2',0 lm_l_found db '[loader] is found',0 @@ -64,7 +64,7 @@ check_name_fat_msg_n db 'Name is not present that is GOOD',0 name_of_seg_get_64 db ' -name of seg where we get 64 Kb of data',0 convertion_file_name_msg_y db '->Destination name of file is GOOD',0 convertion_file_name_msg_n db '->Destination name of file is BAD',0 -alarm_msg db '%%%%%%%% WARNING: MISS THE FILE %%%%%%%%%%%',0 +alarm_msg db '%%%%%%%% WARNING: MISS THE FILE %%%%%%%%%%%',0 start_making_FAT12_msg db '>>>>>> Begin make a RAMDISK and FS after 1 Mb <<<<<<<',0 make_fat12_RFS_msg db '-Make FAT12 Ram FS',0 get_type_FS_msg db '-End make RamDisk',0 diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootet.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootet.inc index c29b2b64d..40614a804 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootet.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootet.inc @@ -29,7 +29,7 @@ d80x25_bottom_num = 3 novesa db "Ekraan: EGA/CGA",13,10,0 vervesa db "Vesa versioon: Vesa x.x",13,10,0 vervesa_off=20 -msg_apm db " APM x.x ", 0 +msg_apm db " APM x.x ", 0 gr_mode db 186," Vesa 2.0+ 16 M LFB: [1] 640x480, [2] 800x600, " db "[3] 1024x768, [4] 1280x1024",13,10 db 186," Vesa 1.2 16 M Bnk: [5] 640x480, [6] 800x600, " diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootge.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootge.inc index 11b157a64..2fa58be3e 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootge.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootge.inc @@ -28,7 +28,7 @@ d80x25_bottom_num = 3 novesa db "Anzeige: EGA/CGA ",13,10,0 vervesa db "Vesa-Version: Vesa ",13,10,0 vervesa_off=22 -msg_apm db " APM x.x ", 0 +msg_apm db " APM x.x ", 0 gr_mode db 186," Vesa 2.0+ 16 M LFB: [1] 640x480, [2] 800x600, " db "[3] 1024x768, [4] 1280x1024",13,10 db 186," Vesa 1.2 16 M Bnk: [5] 640x480, [6] 800x600, " diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootvesa.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootvesa.inc index 556c4cbbc..1356aaea4 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootvesa.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/bootvesa.inc @@ -192,7 +192,7 @@ calc_vmodes_table: mov word [es:bx], 640 mov word [es:bx+2], 480 mov word [es:bx+6], 0x13 - + mov word [es:bx+10], 640 mov word [es:bx+12], 480 mov word [es:bx+16], 0x12 @@ -249,7 +249,7 @@ calc_vmodes_table: mov [es:bx+6], cx ; +6 : mode number movzx ax, byte [es:mi.BitsPerPixel] mov word [es:bx+8], ax ; +8 : bits per pixel - add bx, size_of_step ; size of record + add bx, size_of_step ; size of record @@: add si, 2 @@ -276,11 +276,11 @@ calc_vmodes_table: ; je .exit ; add bx,word [es:bp+2] ; add bx,word [es:bp+8] -; +; ; cmp ax,bx ; ja .loops ; jmp .again -;.loops: +;.loops: ; push dword [es:si] ; push dword [es:si+4] ; push dword [es:si+8] @@ -455,7 +455,7 @@ check_first_parm: ;----------------------------------------------------------------------------- ;default_vmode: - + ;----------------------------------------------------------------------------- draw_vmodes_table: _setcursor 9, 2 @@ -477,10 +477,10 @@ draw_vmodes_table: add cx, size_of_step*long_v_table - + cmp ax, cx jb .ok - + sub cx, size_of_step*long_v_table add cx, size_of_step cmp cx, word[end_cursor] @@ -586,7 +586,7 @@ draw_vmodes_table: ;draw cursor mov word[ds:_rs+21], '>>' mov word[ds:_rs+46], '<<' - + .next: @@ -682,7 +682,7 @@ set_vmode: mov si, word [preboot_graph] ;[preboot_graph] mov cx, word [es:si+6] ; number of mode - + mov ax, word [es:si+0] ; resolution X mov bx, word [es:si+2] ; resolution Y diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/kolibri_ldm.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/kolibri_ldm.asm index a39df33f6..1f8d1fe96 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/kolibri_ldm.asm +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/kolibri_ldm.asm @@ -514,7 +514,7 @@ cfgmanager: push word [es:bp+6] pop word [number_vm] mov word [preboot_graph], bp ;save choose - + jmp .d .change_b: diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/macros.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/macros.inc index 01c180f3a..05ae7ada3 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/macros.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/macros.inc @@ -73,14 +73,14 @@ iglobal_nested a db 'K : ',string,13,10,0 endg_nested if ~ f eq - pushfd + pushfd end if - push esi - mov esi, a - call sys_msg_board_str - pop esi + push esi + mov esi, a + call sys_msg_board_str + pop esi if ~ f eq - popfd + popfd end if } ; \end{diamond}[29.09.2006] diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/preboot.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/preboot.inc index b3fdb3831..8ec8f730b 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/preboot.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/kolibri_ldm/preboot.inc @@ -6,31 +6,31 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -display_modechg db 0 ; display mode change for text, yes/no (0 or 2) - ; - ; !! Important note !! - ; - ; Must be set to 2, to avoid two screenmode - ; changes within a very short period of time. +display_modechg db 0 ; display mode change for text, yes/no (0 or 2) + ; + ; !! Important note !! + ; + ; Must be set to 2, to avoid two screenmode + ; changes within a very short period of time. -display_atboot db 0 ; show boot screen messages ( 2-no ) +display_atboot db 0 ; show boot screen messages ( 2-no ) -preboot_graph dw 0 ; graph mode -x_save dw 0 ; x -y_save dw 0 ; y -number_vm dw 0 ; +preboot_graph dw 0 ; graph mode +x_save dw 0 ; x +y_save dw 0 ; y +number_vm dw 0 ; ;pixel_save dw 0 ; per to pixel -preboot_gprobe db 0 ; probe vesa3 videomodes (1-no, 2-yes) -preboot_vrrm db 0 ; use VRR_M (1-yes, 2- no) -preboot_dma db 0 ; use DMA for access to HDD (1-always, 2-only for read, 3-never) -preboot_device db 0 ; boot device - ; (1-floppy 2-harddisk 3-kernel restart 4-format ram disk) - ;!!!! 0 - autodetect !!!! +preboot_gprobe db 0 ; probe vesa3 videomodes (1-no, 2-yes) +preboot_vrrm db 0 ; use VRR_M (1-yes, 2- no) +preboot_dma db 0 ; use DMA for access to HDD (1-always, 2-only for read, 3-never) +preboot_device db 0 ; boot device + ; (1-floppy 2-harddisk 3-kernel restart 4-format ram disk) + ;!!!! 0 - autodetect !!!! preboot_blogesc = 0 ; start immediately after bootlog -preboot_biosdisk db 0 ; use V86 to access disks through BIOS (1-yes, 2-no) +preboot_biosdisk db 0 ; use V86 to access disks through BIOS (1-yes, 2-no) ; if $>0x200 ;ERROR: prebooting parameters must fit in first sector!!! ; end if -;hdsysimage db 'KOLIBRI IMG' ; load from -;image_save db 'KOLIBRI IMG' ; save to +;hdsysimage db 'KOLIBRI IMG' ; load from +;image_save db 'KOLIBRI IMG' ; save to diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/listing.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/listing.inc index 4ef5f8eeb..faf2e09bc 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/listing.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/listing.inc @@ -1,94 +1,94 @@ ; Listing generator ; LocoDelAssembly 2007.06.01 -INSTRUCTIONS equ bt in ja jb jc je jg jl jo jp js jz or \ - aaa aad aam aas adc add and bsf bsr btc btr bts cbw cdq clc \ - cld cli cmc cmp cqo cwd daa das dec div fld fst hlt inc ins \ - int jae jbe jge jle jmp jna jnb jnc jne jng jnl jno jnp jns \ - jnz jpe jpo lar lds lea les lfs lgs lsl lss ltr mov mul neg \ - nop not out pop por rcl rcr ret rol ror rsm sal sar sbb shl \ - shr stc std sti str sub ud2 xor \ - arpl call cdqe clgi clts cmps cwde emms fabs fadd fbld fchs \ - fcom fcos fdiv feni fild fist fld1 fldz fmul fnop fsin fstp \ - fsub ftst fxam fxch idiv imul insb insd insw int1 int3 into \ - invd iret jcxz jnae jnbe jnge jnle lahf lgdt lidt lldt lmsw \ - lods loop movd movq movs orpd orps outs pand popa popd popf \ - popq popw push pxor retd retf retn retq retw sahf salc scas \ - seta setb setc sete setg setl seto setp sets setz sgdt shld \ - shrd sidt sldt smsw stgi stos test verr verw wait xadd xchg \ - xlat \ - addpd addps addsd addss andpd andps bound bswap cmova cmovb \ - cmovc cmove cmovg cmovl cmovo cmovp cmovs cmovz cmppd cmpps \ - cmpsb cmpsd cmpsq cmpss cmpsw cpuid divpd divps divsd divss \ - enter f2xm1 faddp fbstp fclex fcomi fcomp fdisi fdivp fdivr \ - femms ffree fiadd ficom fidiv fimul finit fistp fisub fldcw \ - fldpi fmulp fneni fprem fptan fsave fsqrt fstcw fstsw fsubp \ - fsubr fucom fwait fyl2x icebp iretd iretq iretw jecxz jrcxz \ - lddqu leave lodsb lodsd lodsq lodsw loopd loope loopq loopw \ - loopz maxpd maxps maxsd maxss minpd minps minsd minss movsb \ - movsd movsq movss movsw movsx movzx mulpd mulps mulsd mulss \ - mwait outsb outsd outsw pabsb pabsd pabsw paddb paddd paddq \ - paddw pandn pause pavgb pavgw pf2id pf2iw pfacc pfadd pfmax \ - pfmin pfmul pfrcp pfsub pi2fd pi2fw popad popaw popfd popfq \ - popfw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb \ - psubd psubq psubw pusha pushd pushf pushq pushw rcpps rcpss \ - rdmsr rdpmc rdtsc retfd retfq retfw retnd retnq retnw scasb \ - scasd scasq scasw setae setbe setge setle setna setnb setnc \ - setne setng setnl setno setnp setns setnz setpe setpo stosb \ - stosd stosq stosw subpd subps subsd subss vmrun vmxon wrmsr \ - xlatb xorpd xorps \ - andnpd andnps cmovae cmovbe cmovge cmovle cmovna cmovnb cmovnc\ - cmovne cmovng cmovnl cmovno cmovnp cmovns cmovnz cmovpe cmovpo\ - comisd comiss fcmovb fcmove fcmovu fcomip fcompp fdivrp ffreep\ - ficomp fidivr fisttp fisubr fldenv fldl2e fldl2t fldlg2 fldln2\ - fnclex fndisi fninit fnsave fnstcw fnstsw fpatan fprem1 frstor\ - frstpm fscale fsetpm fstenv fsubrp fucomi fucomp fxsave haddpd\ - haddps hsubpd hsubps invlpg lfence looped loopeq loopew loopne\ - loopnz loopzd loopzq loopzw mfence movapd movaps movdqa movdqu\ - movhpd movhps movlpd movlps movnti movntq movsxd movupd movups\ - paddsb paddsw pextrw pfnacc pfsubr phaddd phaddw phsubd phsubw\ - pinsrw pmaxsw pmaxub pminsw pminub pmulhw pmullw psadbw pshufb\ - pshufd pshufw psignb psignd psignw pslldq psrldq psubsb psubsw\ - pswapd pushad pushaw pushfd pushfq pushfw rdmsrq rdtscp setalc\ - setnae setnbe setnge setnle sfence shufpd shufps skinit sqrtpd\ - sqrtps sqrtsd sqrtss swapgs sysret vmcall vmload vmread vmsave\ - vmxoff wbinvd wrmsrq \ - clflush cmovnae cmovnbe cmovnge cmovnle cmpeqpd cmpeqps \ - cmpeqsd cmpeqss cmplepd cmpleps cmplesd cmpless cmpltpd \ - cmpltps cmpltsd cmpltss cmpxchg fcmovbe fcmovnb fcmovne \ - fcmovnu fdecstp fincstp fnstenv frndint fsincos fucomip \ - fucompp fxrstor fxtract fyl2xp1 invlpga ldmxcsr loopned \ - loopneq loopnew loopnzd loopnzq loopnzw monitor movddup \ - movdq2q movhlps movlhps movntdq movntpd movntps movq2dq \ - paddusb paddusw palignr pavgusb pcmpeqb pcmpeqd pcmpeqw \ - pcmpgtb pcmpgtd pcmpgtw pfcmpeq pfcmpge pfcmpgt pfpnacc \ - pfrsqrt phaddsw phsubsw pmaddwd pmulhrw pmulhuw pmuludq \ - pshufhw pshuflw psubusb psubusw rsqrtps rsqrtss stmxcsr \ - syscall sysexit sysretq ucomisd ucomiss vmclear vmmcall \ - vmptrld vmptrst vmwrite \ - addsubpd addsubps cmpneqpd cmpneqps cmpneqsd cmpneqss cmpnlepd\ - cmpnleps cmpnlesd cmpnless cmpnltpd cmpnltps cmpnltsd cmpnltss\ - cmpordpd cmpordps cmpordsd cmpordss cvtdq2pd cvtdq2ps cvtpd2dq\ - cvtpd2pi cvtpd2ps cvtpi2pd cvtpi2ps cvtps2dq cvtps2pd cvtps2pi\ - cvtsd2si cvtsd2ss cvtsi2sd cvtsi2ss cvtss2sd cvtss2si fcmovnbe\ - maskmovq movmskpd movmskps movshdup movsldup packssdw packsswb\ - packuswb pfrcpit1 pfrcpit2 pfrsqit1 pmovmskb pmulhrsw prefetch\ - sysenter sysexitq unpckhpd unpckhps unpcklpd unpcklps vmlaunch\ - vmresume \ - cmpxchg8b cvttpd2dq cvttpd2pi cvttps2dq cvttps2pi cvttsd2si \ - cvttss2si pmaddubsw prefetchw punpckhbw punpckhdq punpckhwd \ - punpcklbw punpckldq punpcklwd \ - cmpunordpd cmpunordps cmpunordsd cmpunordss cmpxchg16b \ - loadall286 loadall386 maskmovdqu prefetcht0 prefetcht1 \ - prefetcht2 punpckhqdq punpcklqdq prefetchnta +INSTRUCTIONS equ bt in ja jb jc je jg jl jo jp js jz or \ + aaa aad aam aas adc add and bsf bsr btc btr bts cbw cdq clc \ + cld cli cmc cmp cqo cwd daa das dec div fld fst hlt inc ins \ + int jae jbe jge jle jmp jna jnb jnc jne jng jnl jno jnp jns \ + jnz jpe jpo lar lds lea les lfs lgs lsl lss ltr mov mul neg \ + nop not out pop por rcl rcr ret rol ror rsm sal sar sbb shl \ + shr stc std sti str sub ud2 xor \ + arpl call cdqe clgi clts cmps cwde emms fabs fadd fbld fchs \ + fcom fcos fdiv feni fild fist fld1 fldz fmul fnop fsin fstp \ + fsub ftst fxam fxch idiv imul insb insd insw int1 int3 into \ + invd iret jcxz jnae jnbe jnge jnle lahf lgdt lidt lldt lmsw \ + lods loop movd movq movs orpd orps outs pand popa popd popf \ + popq popw push pxor retd retf retn retq retw sahf salc scas \ + seta setb setc sete setg setl seto setp sets setz sgdt shld \ + shrd sidt sldt smsw stgi stos test verr verw wait xadd xchg \ + xlat \ + addpd addps addsd addss andpd andps bound bswap cmova cmovb \ + cmovc cmove cmovg cmovl cmovo cmovp cmovs cmovz cmppd cmpps \ + cmpsb cmpsd cmpsq cmpss cmpsw cpuid divpd divps divsd divss \ + enter f2xm1 faddp fbstp fclex fcomi fcomp fdisi fdivp fdivr \ + femms ffree fiadd ficom fidiv fimul finit fistp fisub fldcw \ + fldpi fmulp fneni fprem fptan fsave fsqrt fstcw fstsw fsubp \ + fsubr fucom fwait fyl2x icebp iretd iretq iretw jecxz jrcxz \ + lddqu leave lodsb lodsd lodsq lodsw loopd loope loopq loopw \ + loopz maxpd maxps maxsd maxss minpd minps minsd minss movsb \ + movsd movsq movss movsw movsx movzx mulpd mulps mulsd mulss \ + mwait outsb outsd outsw pabsb pabsd pabsw paddb paddd paddq \ + paddw pandn pause pavgb pavgw pf2id pf2iw pfacc pfadd pfmax \ + pfmin pfmul pfrcp pfsub pi2fd pi2fw popad popaw popfd popfq \ + popfw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb \ + psubd psubq psubw pusha pushd pushf pushq pushw rcpps rcpss \ + rdmsr rdpmc rdtsc retfd retfq retfw retnd retnq retnw scasb \ + scasd scasq scasw setae setbe setge setle setna setnb setnc \ + setne setng setnl setno setnp setns setnz setpe setpo stosb \ + stosd stosq stosw subpd subps subsd subss vmrun vmxon wrmsr \ + xlatb xorpd xorps \ + andnpd andnps cmovae cmovbe cmovge cmovle cmovna cmovnb cmovnc\ + cmovne cmovng cmovnl cmovno cmovnp cmovns cmovnz cmovpe cmovpo\ + comisd comiss fcmovb fcmove fcmovu fcomip fcompp fdivrp ffreep\ + ficomp fidivr fisttp fisubr fldenv fldl2e fldl2t fldlg2 fldln2\ + fnclex fndisi fninit fnsave fnstcw fnstsw fpatan fprem1 frstor\ + frstpm fscale fsetpm fstenv fsubrp fucomi fucomp fxsave haddpd\ + haddps hsubpd hsubps invlpg lfence looped loopeq loopew loopne\ + loopnz loopzd loopzq loopzw mfence movapd movaps movdqa movdqu\ + movhpd movhps movlpd movlps movnti movntq movsxd movupd movups\ + paddsb paddsw pextrw pfnacc pfsubr phaddd phaddw phsubd phsubw\ + pinsrw pmaxsw pmaxub pminsw pminub pmulhw pmullw psadbw pshufb\ + pshufd pshufw psignb psignd psignw pslldq psrldq psubsb psubsw\ + pswapd pushad pushaw pushfd pushfq pushfw rdmsrq rdtscp setalc\ + setnae setnbe setnge setnle sfence shufpd shufps skinit sqrtpd\ + sqrtps sqrtsd sqrtss swapgs sysret vmcall vmload vmread vmsave\ + vmxoff wbinvd wrmsrq \ + clflush cmovnae cmovnbe cmovnge cmovnle cmpeqpd cmpeqps \ + cmpeqsd cmpeqss cmplepd cmpleps cmplesd cmpless cmpltpd \ + cmpltps cmpltsd cmpltss cmpxchg fcmovbe fcmovnb fcmovne \ + fcmovnu fdecstp fincstp fnstenv frndint fsincos fucomip \ + fucompp fxrstor fxtract fyl2xp1 invlpga ldmxcsr loopned \ + loopneq loopnew loopnzd loopnzq loopnzw monitor movddup \ + movdq2q movhlps movlhps movntdq movntpd movntps movq2dq \ + paddusb paddusw palignr pavgusb pcmpeqb pcmpeqd pcmpeqw \ + pcmpgtb pcmpgtd pcmpgtw pfcmpeq pfcmpge pfcmpgt pfpnacc \ + pfrsqrt phaddsw phsubsw pmaddwd pmulhrw pmulhuw pmuludq \ + pshufhw pshuflw psubusb psubusw rsqrtps rsqrtss stmxcsr \ + syscall sysexit sysretq ucomisd ucomiss vmclear vmmcall \ + vmptrld vmptrst vmwrite \ + addsubpd addsubps cmpneqpd cmpneqps cmpneqsd cmpneqss cmpnlepd\ + cmpnleps cmpnlesd cmpnless cmpnltpd cmpnltps cmpnltsd cmpnltss\ + cmpordpd cmpordps cmpordsd cmpordss cvtdq2pd cvtdq2ps cvtpd2dq\ + cvtpd2pi cvtpd2ps cvtpi2pd cvtpi2ps cvtps2dq cvtps2pd cvtps2pi\ + cvtsd2si cvtsd2ss cvtsi2sd cvtsi2ss cvtss2sd cvtss2si fcmovnbe\ + maskmovq movmskpd movmskps movshdup movsldup packssdw packsswb\ + packuswb pfrcpit1 pfrcpit2 pfrsqit1 pmovmskb pmulhrsw prefetch\ + sysenter sysexitq unpckhpd unpckhps unpcklpd unpcklps vmlaunch\ + vmresume \ + cmpxchg8b cvttpd2dq cvttpd2pi cvttps2dq cvttps2pi cvttsd2si \ + cvttss2si pmaddubsw prefetchw punpckhbw punpckhdq punpckhwd \ + punpcklbw punpckldq punpcklwd \ + cmpunordpd cmpunordps cmpunordsd cmpunordss cmpxchg16b \ + loadall286 loadall386 maskmovdqu prefetcht0 prefetcht1 \ + prefetcht2 punpckhqdq punpcklqdq prefetchnta -PREFIXES equ rep lock repe repz repne repnz +PREFIXES equ rep lock repe repz repne repnz DATA_DEFINITORS equ db dw du dd dp df dq dt file -DATA_RESERVERS equ rb rw rd rp rf rq rt +DATA_RESERVERS equ rb rw rd rp rf rq rt -CRLF equ 13, 10 ; Remove 13 for Linux -MAX_BYTES equ 13 +CRLF equ 13, 10 ; Remove 13 for Linux +MAX_BYTES equ 13 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MODE MACROSES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -118,7 +118,7 @@ local aux _USE = 32 virtual at 0 - xchg eax, eax + xchg eax, eax load aux byte from 0 if aux = $66 @@ -234,25 +234,25 @@ forward \{ match args, args \\{ - if ~args eqtype "" - irps arg, args - \\\{ - display ' ', \\\`arg - \\\} - else - display " '", args, "'" - end if + if ~args eqtype "" + irps arg, args + \\\{ + display ' ', \\\`arg + \\\} + else + display " '", args, "'" + end if \\} \} match =OFF, _RESOLVE_EQUATES \{ if ~args eqtype "" - irps arg, args - \\{ - display ' ', \\`arg - \\} + irps arg, args + \\{ + display ' ', \\`arg + \\} else - display " '", args, "'" + display " '", args, "'" end if \} end if @@ -334,10 +334,10 @@ local aux display \`mnemonic virtual at 0 - db \`mnemonic - repeat 11 - $ - display ' ' - end repeat + db \`mnemonic + repeat 11 - $ + display ' ' + end repeat end virtual display_args args @@ -384,11 +384,11 @@ local aux aux = aux + MAX_BYTES repeat ($ - aux + MAX_BYTES - 1) / MAX_BYTES - display_address aux - display_bytes aux - display CRLF + display_address aux + display_bytes aux + display CRLF - aux = aux + MAX_BYTES + aux = aux + MAX_BYTES end repeat \\} @@ -424,11 +424,11 @@ local aux aux = aux + MAX_BYTES repeat ($ - aux + MAX_BYTES - 1) / MAX_BYTES - display_address aux - display_bytes aux - display CRLF + display_address aux + display_bytes aux + display CRLF - aux = aux + MAX_BYTES + aux = aux + MAX_BYTES end repeat \\} @@ -532,7 +532,7 @@ macro enable_listing \\{ irps ins, instructions \\\{ - instruction ins + instruction ins \\\} \\} @@ -540,7 +540,7 @@ macro enable_listing \\{ irps prefix, prefixes \\\{ - def_prefix prefix + def_prefix prefix \\\} \\} @@ -548,7 +548,7 @@ macro enable_listing \\{ irps def, data_definitors \\\{ - data_define def + data_define def \\\} \\} @@ -556,13 +556,13 @@ macro enable_listing \\{ irps def, data_reservers \\\{ - data_reserve def + data_reserve def \\\} \\} \} _MACROSES_INSTALLED equ 1 - _LISTING equ 1 + _LISTING equ 1 } macro disable_listing @@ -630,6 +630,6 @@ macro disable [feature*] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _MACROSES_INSTALLED equ 0 -_ON_VIRTUAL equ 0 +_ON_VIRTUAL equ 0 disable resolve_equates diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.asm b/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.asm index b4eba71e3..4489ac94c 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.asm +++ b/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_any.inc' include 'parse_def_sect.inc' include 'parse_err.inc' -file_data dw 0x0,ini_data_ ;: : .. les -size_data dw 16 ;16 4 . 64 +file_data dw 0x0,ini_data_ ;формат: смещение: сегмент т.к. используется les +size_data dw 16 ;16 блоков по 4 кб т.е предел до 64 кб name_ini_f db 'kord/startos.ini',0 ;//////////// @@ -86,7 +86,7 @@ start: call printplain mov al, '#' mov cx, 80 -;input cx=size al=char cx +;input cx=size al=char будет вывден символ сколько раз указано в cx @@: call putchar loop @b @@ -94,7 +94,7 @@ start: if DEBUG pushad mov ax, cs - shl eax, 4 ; + shl eax, 4 ; в десятичной системе адрес сегмента mov cx, 0xa mov di, cseg_msg call decode @@ -162,7 +162,7 @@ cpugood: ; Load startos.ini - mov cx, loop_read_startos_file ;- startos.ini + mov cx, loop_read_startos_file ;кол-во попыток чтения файла конфигурации startos.ini align 4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Load startos.ini ; @@ -254,32 +254,32 @@ table_15_87: db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 fat12_buffer: -.BS_jmpBoot db 0x90,0x90,0x90 ;3 NOP - -.BS_OEMName db 'K SyS 64' ;8 -.BPB_BytsPerSec dw 512 ;- 512 1024 2048 4096 2 -.BPB_SecPerClus db 0x1 ;- -.BPB_RsvdSecCnt dw 0x1 ; FAt12/16 1, FAT32 32 -.BPB_NumFATs db 0x1 ;- , -.BPB_RootEntCnt dw 512 ; fat16 -.BPB_TotSec16 dw 0x0 ;- +.BS_jmpBoot db 0x90,0x90,0x90 ;3 байта NOP инструкция - ничего не делать +.BS_OEMName db 'K SyS 64' ;8 байт +.BPB_BytsPerSec dw 512 ;кол-во байтов в секторе может быть любое 512 1024 2048 4096 2 байта +.BPB_SecPerClus db 0x1 ;кол-во секторов в кластере +.BPB_RsvdSecCnt dw 0x1 ;для FAt12/16 только 1, для FAT32 обычно 32 +.BPB_NumFATs db 0x1 ;кол-во фат таблиц, на тот случай если будет сброс на дискету образа рам диска +.BPB_RootEntCnt dw 512 ;для мак совместимости с fat16 +.BPB_TotSec16 dw 0x0 ;кл-во секторов .BPB_Media db 0xF0 .BPB_FATSz16 dw 0x0 -.BPB_SecPerTrk dw 0x0 ; RAMFS , , . +.BPB_SecPerTrk dw 0x0 ;содержит геометрию диска для RAMFS на как бы без разницы, пока пустое поле, позже внести реальные значения. .BPB_NumHeads dw 0x0 -.BPB_HiddSec dd 0x0 ;- +.BPB_HiddSec dd 0x0 ;кол-во скрытых секторов .BPB_TotSec32 dd 0x0 -.BS_DrvNum db 'R' ; RAM +.BS_DrvNum db 'R' ;от слова RAM .BS_Reserved1 db 0x0 .BS_BootSig db 0x29 .BS_VolID db 'RFKS' -.BS_VolLab db 'RAM DISK FS' ;11 -.BS_FilSysType db 'FAT12 ' ;8 -;62 fat12. +.BS_VolLab db 'RAM DISK FS' ;11 символов +.BS_FilSysType db 'FAT12 ' ;8 символов +;62 байта структура fat12. db (512-($-fat12_buffer))dup(0x90) -; fat +;структура для дирректории fat struc FAT_32_entry ;Byte Directory Entry Structure { .DIR_Name rb 11 @@ -297,21 +297,21 @@ struc FAT_32_entry ;Byte Directory Entry Structure } -; , .... +;Тут будут распологатсья данные, которые затруднительно распологать в стековой области.... ;;; ;timer -shot_name_fat rb 11 ; fat12, FAT /* +shot_name_fat rb 11 ;временный буфер для fat12, в нем храняться имена файлов приведенные к правилам FAT /* вдальнейшем перенести в стэк if DEBUG - rb 1 ; + rb 1 ;нужен для отладки и вывода имени файла после преобразования dest_name_fat db 24 dup('_');12 db 0x0 end if value_timeout rw 1 ;value to timeout -old_timer rd 1 ; -start_timer rd 1 ; -timer_ rd 1 ; .. SL +old_timer rd 1 ;старое значение вектора таймера +start_timer rd 1 ;значение таймера +timer_ rd 1 ;новое значение вектора таймера т.е. SL start_stack rw 1 ;save stack save_bp_from_timer rw 1 ;save bp from timer diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.lst b/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.lst index 3984e145d..e3e51fc98 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.lst +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.lst @@ -905,7 +905,7 @@ flat assembler version 1.68 (65535 kilobytes memory) 0E58: E2 FC loop @b 0E5A: BF E0 01 mov di, 480 0E5D: B4 0E mov ah, color_sym_yellow -0E5F: B0 C4 mov al, '' +0E5F: B0 C4 mov al, 0xC4 0E61: B9 3D 00 mov cx, 61 0E64: F3 rep 0E65: AB stosw diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse.inc index 34ba5a449..72c1e7e9e 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse.inc +++ b/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,23 +63,23 @@ parse_start: .start: call get_firs_sym ;get first symbol on new line -.first_ret: ; +.first_ret: ;первый возврат ; jcxz .end_file ;.end_loader ;found or not found parametrs in section exit in section test cx, cx jz error.not_loader cmp al, '[' jz .parse_loader jmp .start -;////// loader +;////// проверка на наличее секции loader use_parse_loader ;pause if DEBUG xor ax, ax int 16h end if -;////// , , +;////// вывод графического экрана, выбор, секции под дефолту use_any_sec -; .. +;парсинг выбраной или дефолтной секции т.е. разбор параметров выполнение сценария use_parse_def_sect ;////////////////// diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_any.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_any.inc index 84378d672..f85fef13d 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_any.inc +++ b/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 @@ color_sym_yellow equ 14 macro use_any_sec { -; .. = timeout, 0, . -; . +;узнаем работу предыдущего шага т.е. чему = timeout, если он 0, то визуальная часть не будет отображена на дисплее с выбором загрузочных секций. +;иначе мы ее должны отобразить и ждать заявленое время для выбора и конигурирования пукнктов секции от пользователя. if DEBUG pusha - mov ax, word [value_timeout]; timeout, , ,.. =0 , + mov ax, word [value_timeout];идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти ; mov ax,cx mov cx, 0x0a mov di, show_db1 @@ -62,7 +62,7 @@ end if test ax, ax jz .parse_run_only -; . +;отобразим полный список всех найденых секций. if DEBUG pusha mov si, show_all_sect @@ -70,7 +70,7 @@ if DEBUG popa end if ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - mov al, 0xf6 ; , + mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование out 0x60, al xor cx, cx .wait_loop: ; variant 2 @@ -102,7 +102,7 @@ end if mov dword [start_timer], eax mov word [timer_], newtimer mov word [timer_+2], cs -; .. ~18 +;установить свое прерывание на таймер т.е. код будет перрываться ~18 раз в сек и переходить на обработчик cli push 0 pop es @@ -112,7 +112,7 @@ end if pop dword [es:8*4] sti -; +;процедура формирования буфера для скролинга секций ;if DEBUG ; pusha ; mov ax,point_default @@ -130,20 +130,20 @@ end if ; int 0x16 ; popa ;end if -;;;;;;;;;;;;; =0 +;;;;;;;;;;;;;размер предыдущей сеции установим =0 mov save_descript_size, 18 -; black screen +;отобразить black screen show_bl_sc ;es=0xb800 .show_all_scr: get_frame_buffer ;es=0x2000 -; +;отображение секций call show_bl_sc_sect ;es=0xb800 -; +;отобразить активный курсор .show_active_cursor: show_act_cursor -show_descript ; +show_descript ;макрос по отображению описания секции -; Press any key .... +;отобразить Press any key .... mov eax, dword [old_timer] cmp eax, dword [timer_] jz .interrupt_16 @@ -151,7 +151,7 @@ show_descript ; show_timer_message mov word [start_stack], sp .interrupt_16: - xor ax, ax ; + xor ax, ax ;получим информацию о том что нажато int 0x16 ;check on change mov ebx, dword [old_timer] @@ -161,7 +161,7 @@ show_timer_message cli push 0 pop es -; mov eax,dword [old_timer] ; +; mov eax,dword [old_timer] ; восстановим прежднее прерывание mov [es:8*4], ebx mov dword [timer_], ebx sti @@ -172,7 +172,7 @@ clear_timer_msg @@: call clean_active_cursor ;clean old cursor ;es=0xb800 - cmp ah, 0x48 ; + cmp ah, 0x48 ;реакция системы на события jz .up cmp ah, 0x50 jz .down @@ -188,9 +188,9 @@ clear_timer_msg cmp al, 0xD jnz .show_active_cursor - jmp .end_show_all ; point_default + jmp .end_show_all ;парсинг секции которая указана в point_default .up: - mov si, point_to_point_def ; + mov si, point_to_point_def ;значение указателя add si, 2 lea ax, point_to_hframe @@ -208,8 +208,8 @@ clear_timer_msg .down: - mov si, point_to_point_def ; - mov ax, point_to_eframe ; + mov si, point_to_point_def ;значение указателя + mov ax, point_to_eframe ;указатель на последний элемент sub si, 2 cmp si, ax jb @f @@ -255,7 +255,7 @@ clear_timer_msg -; . +; тут мы будем парсить только дефолтную секцию и выполнять ее ничего не предлагая пользователю из диалогов. .parse_run_only: if DEBUG pusha @@ -286,7 +286,7 @@ end if macro show_bl_sc { ;;;;;;;;;;;;;;; -; +;очистим экран и выведем меню ; draw frames xor ax, ax if DEBUG @@ -324,7 +324,7 @@ end if ;;;;;;;;;;;;;;;;;;;;;;; show '__________________________' mov di, 480 mov ah, color_sym_yellow - mov al, '' + mov al, 0xC4 ; '─' mov cx, 61 rep stosw ;;;;;;;;;;;;;;;;;;;;;;; show 'Select section' @@ -418,7 +418,7 @@ macro get_frame_buffer mov si, di ;point frame mov bx, cx mov dx, size_show_section -; mov point_to_hframe,di ; , +; mov point_to_hframe,di ; внесем значение, так подстраховка не более mov al, byte [es:di] push word .first_ret_bl_sc @@ -443,14 +443,14 @@ macro get_frame_buffer .start_bl: call get_firs_sym ;get first symbol on new line -.first_ret_bl_sc: ; +.first_ret_bl_sc: ;первый возврат test cx, cx jz error.correct_exit_bl ;critical error not found default point it's not possible because it's param chacking before .analisist_al: cmp al, '[' jnz .start_bl -; ini default -; default +;просматриваем ini файл с начала в поисках секции указаной как default +;поиск фрейма в котором содержиться значение default .found_sect_bl: cmp di, point_loader jz .start_bl @@ -464,12 +464,12 @@ macro get_frame_buffer .save_point_def: -; frame , - mov di, si ; +;итак далее мы должны заполнить frame буфер адресов секций, что бы потом по нему быстро перемещаться не вычисляя снова адреса + mov di, si ;указатель на начало mov cx, bx lea si, point_to_hframe - mov dx, size_show_section+1 ;.. , 1 . -; + mov dx, size_show_section+1 ;т.к. у нас структура содержит размер между первым и вторым указателем, то нам нужно на 1 адрес больше обсчитать секций. +;переходим на обработку значения указателя mov al, byte [es:di] push word .first_ret_mfb cmp al, ' ' @@ -480,7 +480,7 @@ macro get_frame_buffer .start_mfb: call get_firs_sym ;get first symbol on new line -.first_ret_mfb: ; +.first_ret_mfb: ;первый возврат jcxz .val_buff_comp ;.end_loader ;found or not found parametrs in section exit in section cmp al, '[' jnz .start_mfb @@ -509,7 +509,7 @@ macro get_frame_buffer macro show_act_cursor { -; +;отображение курсора по умолчанию lea si, point_to_hframe mov di, 962-160 mov ax, point_default @@ -554,7 +554,7 @@ end if } macro show_descript -; , +;Этот макрос показывает краткое описание, если оно есть у секции в пункте ;Section description { local .start_p_sh_d @@ -568,14 +568,14 @@ local .show_mess_prev_eq mov si, point_to_point_def pop es sub si, 2 - mov cx, [si] ; - sub cx, di ; -;di - .. cx - . + mov cx, [si] ;загрузим указатель наследующию секцию + sub cx, di ;вот теперь имеем истиный размер +;di - указатель на дефолтную секцию т.е. выбранную cx - размер области. для просмотра .start_p_sh_d: call get_firs_sym ;get first symbol on new line test cx, cx - jz .exit ;? - ) + jz .exit ;нету? ну ладно - следующее значение тогда ) cmp al, 'd' jnz .start_p_sh_d ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -591,7 +591,7 @@ local .show_mess_prev_eq sub bx, parse_descript_e - parse_descript;correct cx add bx, cx mov cx, bx -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ' = ' +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; разбор аля ' = ' mov ax, 0x3d20 ;cut al=' ' ah='=' repe scasb jcxz .rest_value_loop_sh_d ;not found param timeout @@ -602,10 +602,10 @@ local .show_mess_prev_eq repe scasb ;cut ' ' inc cx dec di -;;;;;;;;;;;;;;;;;;;;di , . -; 37 . -; .. -;es:di - , ds:si +;;;;;;;;;;;;;;;;;;;;di указывает на строчку, которую нам нужно выводить. +;строчка будет выводиться блоками по 37 символов. +;настроим куда будем выводить т.е. начало +;es:di - указывают на строчку из которой мы берем символ, ds:si куда будем выводить push di pop si diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_dat.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_dat.inc index 8279f2517..0525f5221 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_dat.inc +++ b/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' diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_def_sect.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_def_sect.inc index f5855816c..9e1fa8364 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_def_sect.inc +++ b/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 @@ ATTR_ARCHIVE equ 0x20 -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 @@ macro use_parse_def_sect 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 @@ local .found_end_str 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 @@ local .found_end_str 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 @@ local .found_end_str inc di dec cx jnz @b -;;;not found , +;;;not found допустим,что это конец файла и он не имеет привычного заверешния строки .found_end_str: push word [es:di] @@ -189,7 +189,7 @@ local .found_end_str } macro use_RamdiskFS -; , + . +; формирование рам диска, + обработка всего связанного. { if DEBUG local ._not_memory_in_sys @@ -200,19 +200,19 @@ local ._not_memory_in_sys 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 .rest_value_loop_RS 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 @@ local ._end_parse_RS 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 @@ local ._end_parse_RS 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 @@ local ._end_parse_RS 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 @@ local ._end_parse_RS .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 @@ end if macro use_free_memory { local _support_function_use_free_memory -; , 1 . -; 088 015 -; , 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,17 +380,17 @@ macro show_ERRORS } -macro get_type_FS ; RFS. +macro get_type_FS ;получить и создать образ для заданной RFS. { mov di, point_default ;restore value mov cx, save_cx_d .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 @@ macro get_type_FS ; 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 @@ macro get_type_FS ; 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 @@ macro get_type_FS ; repe cmpsb jnz .krfs_cmp ;is not compare -make_FAT_RamFS ; +make_FAT_RamFS ;сделать if DEBUG pusha @@ -464,7 +464,7 @@ end if .end_get_FRS_ERROR_1: -; - :( +;сообщение об ошибке - данный участок кода не был корректно обработан :( or show_errors_sect, show_error_1 jmp ._end_parse_FRS .end_get_FRS_ERROR_2: @@ -486,55 +486,55 @@ macro make_FAT_RamFS 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 ;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 @@ use_RamdiskCluster ; .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 @@ use_RamdiskCluster ; 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 @@ use_RamdiskCluster ; 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 @@ use_RamdiskCluster ; dec eax cdq idiv edi -;FATSz = eax, edx +;FATSz = сейчас частное в eax, а остаток в edx mov word [fat12_buffer.BPB_FATSz16], ax @@ -619,7 +619,7 @@ use_RamdiskCluster ; .fat12: ;fat12 if DEBUG -; , c FS=fat12 +; выведем в отладке, что собираемся делать образ диска c FS=fat12 pushad mov si, start_making_FAT12_msg call printplain @@ -628,8 +628,8 @@ end if -; 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 @@ end if 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 .. 02600 -; ) 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,18 +715,18 @@ end if macro use_RamdiskSector { -; FS +;для некоторых FS будет игнорироваться mov di, point_default ;restore value mov cx, save_cx_d .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 @@ macro use_RamdiskSector 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 @@ macro use_RamdiskSector 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 @@ end if macro use_RamdiskCluster { -; FS +;для некоторых FS будет игнорироваться ; push es ; push di mov di, point_default ;restore value @@ -820,10 +820,10 @@ macro use_RamdiskCluster .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 @@ macro use_RamdiskCluster 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 @@ macro use_RamdiskCluster 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 @@ end if } macro use_Loader_Image -; 1 . -; 1.44 +;предназначен для загрузки образов выше 1 Мб. +;первоначальная версия загружает образ дискеты 1.44 мб { local .start_p_LI local .exit @@ -902,14 +902,14 @@ local .rest_value_loop 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 @@ local .found_end_str 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 @@ local .found_end_str 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 @@ local .found_end_str 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 @@ local .found_end_str jnz .error_LM -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 64 1 . +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; забрасывание блока в 64 кб выше 1 мб. mov si, table_15_87 push es push ds @@ -1027,7 +1027,7 @@ local .found_end_str macro name_in_root_fat -;, +;макрос, который записывает информацию о загруженном файле в корневую фат таблицу { } @@ -1036,9 +1036,9 @@ macro name_in_root_fat macro use_RamdiskFile { -; callback -; , .. -; 087 int 0x15 - 64 1 +;загрузка файлов с использование callback сервиса первичного загрузчика +;используется только для загрузки необходимых и небольших файлов, т.к. достаточно медленно работает +;для загрузки использует 0х87 функцию int 0x15 прерывания - загрузка блоков данных до 64 кб выше 1 мб local .start_loop local ._end local .rest_value_loop @@ -1046,14 +1046,14 @@ local .error 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 @@ local .error 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 @@ local .error 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,14 +1115,14 @@ local .error ;@@: mov point_to_dest_file_name, di inc di -; +;проверка индивидуальности имени файла check_name_file ;/restore di - point and cx -size section mov di, save_di_RAMDISK mov cx, save_cx_RAMDISK test al, al - jnz .start_loop ; al =0, . + jnz .start_loop ;если в al значение не =0, то такое имя уже существует в системе. @@ -1132,13 +1132,13 @@ check_name_file 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 @@ end if 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 @@ end if -; -; 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 @@ move_file_up @@: -; +;тут организован цикл по загрузке файлов в корневую директорию mov di, save_di_RAMDISK mov cx, save_cx_RAMDISK if DEBUG @@ -1278,7 +1278,7 @@ end if jmp .start_loop ._end: -; 1- +;перенесем за 1-ый мб фат и рут дир move_up_fat_and_root_d @@ -1286,7 +1286,7 @@ move_up_fat_and_root_d -; +;загрузка блока ; mov ah,0x87 ; mov cx, ;size in byte @@ -1296,8 +1296,8 @@ move_up_fat_and_root_d } -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 @@ macro use_BPB_RAM ; 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 @@ if DEBUG end if } macro first_create_fat_table -; 3 fat , , 0 -; . +;данный макрос создает оформляет 3 первых байта fat таблицы, и устанавливает указатель на следующий блок, и вносит 0 значение +;для смещения в корневой таблице. { mov al, byte [fat12_buffer.BPB_Media] @@ -1361,12 +1361,12 @@ if DEBUG 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,30 +1390,30 @@ end if } macro register_file_in_fat -; Fat -; 12, )) -; fat/ +;макрос регистрации файла в файловой структуре Fat +;пока поддерживается только фат12, пока )) +;вычисление смежных кластеров и занесение инфы в fat/ { local .step2 local .step3 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 @@ end if -;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 @@ end if 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 @@ end if 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 @@ end if 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,25 +1543,25 @@ end if 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 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 mov si, dest_name_fat mov cx, 11 -; +;запишем в структуру имя @@: mov al, byte [es:di] inc di @@ -1585,7 +1585,7 @@ END IF - add point_to_free_root, 32 ; . + add point_to_free_root, 32 ;увелицим смещение до следующего значения. pop es } @@ -1595,8 +1595,8 @@ END IF 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 @@ macro get_firstDataSector 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 @@ macro get_firstDataSector if DEBUG pushad - mov ax, firstDataSect ; + mov ax, firstDataSect ;первый сектор данных mov cx, 0x0a mov di, firstDataSect_msg call decode @@ -1629,7 +1629,7 @@ if DEBUG 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 @@ if DEBUG 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 @@ end if } 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 @@ end if loop @b ;.succesfuly: -;, :( +;печально, такое имя уже имеется :( or ax, -1 jmp .exit @@ -1799,9 +1799,9 @@ end if 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_s 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 @@ local .st5 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 @@ local .st5 cmp al, 0x60 jbe .st2_l - xor al, 0x20; + xor al, 0x20;сделаем заглавные буквы .st2_l: mov byte [si], al inc di @@ -1909,7 +1909,7 @@ local .st5 xor ax, ax jmp @f -;;;;;;;; , +;;;;;;;;файл закончился, и нужно внести в конец пробелы .st4_s: mov al, ' ' .st4: @@ -1941,32 +1941,32 @@ end if } 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 movzx ebx, word [fat12_buffer.BPB_BytsPerSec] movzx edx, byte [fat12_buffer.BPB_SecPerClus] - imul bx, dx ; + imul bx, dx ;получим размер кластера push ebx ;save bx imul eax, ebx -; shl eax,9 ; 512 +; shl eax,9 ;умножим на 512 if DEBUG pushad @@ -2002,25 +2002,25 @@ end if @@: - 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 ; 08000 + sub save_file_size, ecx ;отнимим +; jmp .st1 ;получим 0х8000 -; +;корректировка значения должна быть выполенена на размер кластера .correct_on_byte: -;/ +;/узнаем размер кластера pop eax ;restore size of claster push ecx @@: @@ -2040,9 +2040,9 @@ end if 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 @@ end if macro move_up_fat_and_root_d -;, 1 +;макрос, который позволяет перенести выше 1 мб в структуру образа фат таблицу и рут директорию { local .st1 @@ -2077,20 +2077,20 @@ local .st1 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 diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_err.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_err.inc index d8c17ff3a..794600b00 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_err.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_err.inc @@ -30,7 +30,7 @@ error: mov cx, bx jmp ret_on_ch ;return -;///// default +;///// ошибка при находжении длинны секции в параметре default .error_get_size_d_sect: leave ;clear array in stack mov si, not_found_def_sect @@ -42,7 +42,7 @@ error: mov si, not_found_sec_loader jmp err_show_ini -.default_eq_loader: ; default = loader +.default_eq_loader: ;критическая ошибка default секция = loader leave mov si, default_eq_loader jmp err_show_ini diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_loader.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_loader.inc index 3650ee8b0..60dff0bbd 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_loader.inc +++ b/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 - '[' 0a -;cx - - +;блок макросов по обработке секции [loader] +;входные данные: +;es:di - указатель на секцию начинающиюся с '[' встечающиюся после 0хa +;cx - счетчик кол-во байт для проверке в кадре ; macro use_parse_loader { @@ -35,16 +35,16 @@ macro use_parse_loader ;////////////////// ;/ parse [loader] ;////////////////// - mov bx, cx ;c + mov bx, cx ;cохраним в регистры значения счетчика и указателя mov ax, di ; mov word [bp-4],.start ;is alredy set, see up mov si, parse_loader mov cx, parse_loader_e - parse_loader repe cmpsb - jnz error.rest_value ; :( .. )) + jnz error.rest_value ;цепочка не совпала :( перейдем далее т.е. будем снова искать)) - ; loader, + ;сохраним указательна loader, что бы потом больше его не искать mov point_loader, ax sub bx, parse_loader_e - parse_loader;correct cx add bx, cx @@ -63,7 +63,7 @@ end if mov dx, di @@: call get_firs_sym - jcxz .loader_f_end ;.end_loader ; end [loader] + jcxz .loader_f_end ;.end_loader ; end даже если мы не нашли секцию предположим что секция [loader] стоит в конце cmp al, '[' jnz @b @@ -74,7 +74,7 @@ end if ;//timeout=5 ;//default=main ; mov di,dx ;set pointer on section [loader] i think it's not need - mov cx, bx ;set counter for parsing section [loader] cx= - [loader] + mov cx, bx ;set counter for parsing section [loader] cx= кол-ву символов в секции [loader] mov ret_on_ch, .get_next_str; return point ;;;;;;; parse timeout & default .get_next_str: @@ -82,7 +82,7 @@ end if test cx, cx jz .end_loader -; jcxz .end_loader ; timeout & default +; jcxz .end_loader ;завершение парсинга значений timeout & default cmp al, 't' jz .loader_timeout cmp al, 'd' @@ -96,7 +96,7 @@ end if mov cx, parse_l_default_e - parse_l_default repe cmpsb - jnz error.rest_value ;is not compare + jnz error.rest_value ;is not compare цепочка не совпала sub bx, parse_l_default_e - parse_l_default;correct cx add bx, cx @@ -105,7 +105,7 @@ end if test status_flag, flag_found_default jz .correct_is_not_set - mov si, found_equal_default ; , + mov si, found_equal_default ;мы нашли что флаг уже установлен, информируем call printplain jmp .get_next_str @@ -121,12 +121,12 @@ end if repe scasb ;cut ' ' inc cx dec di -; es:di , loader .. -; si +;сейчас es:di указывают на название секции, имя секции по дефолту не должно быть loader т.е. иначе возможно зацикливание +;установим указатель si на это значение и сначала проверим -; -; cx=bx -; di=ax +;получение длинны секции +; cx=bx содержит длинну остатка секции +; di=ax указатель на текущию секцию mov bx, cx mov dx, di @@ -135,7 +135,7 @@ end if inc di dec cx test cx, cx - jz error.error_get_size_d_sect ; + jz error.error_get_size_d_sect ;переход на обработку ошибки по нахождению длины дефолтной секции cmp al, ' ' jz @b cmp al, 0xd @@ -146,13 +146,13 @@ end if ; inc cx ;correct cx mov ax, bx - sub bx, cx ; bx + sub bx, cx ; в bx длина секции которая определена по дефолту mov save_cx_d, bx mov di, dx mov cx, bx ;set size default section -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; =loader -;save in reg point and +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;проверка на =loader +;save in reg point and счетчик ;check on loader mov bx, ax mov ax, dx @@ -160,22 +160,22 @@ end if mov si, parse_loader inc si ;set only loader and 6 char in counter repe cmpsb - jnz .check_section ; :( )) + jnz .check_section ;цепочка не совпала :( перейдем далее )) значит не исключение - jmp error.default_eq_loader ;error .. [loader] + jmp error.default_eq_loader ;error критическая ошибка т.е. в дефолте присутствует имя [loader] -.check_section: ; +.check_section: ;поиск соответствующей секции нам нужно будет узнать адрес этой секции mov cx, bx mov di, ax ;///////////////////////////// ; mov ret_on_ch,.start_d ;set return - mov si, di ; , + mov si, di ;установим указатель на нашу секцию, которая по дефолту push di ;save point di push cx ;save cx -; es:di ini +;установим указатель es:di на начало ini файла mov cx, save_cx ;it's placed size of ini file les di, dword [file_data] @@ -190,13 +190,13 @@ end if .start_d: call get_firs_sym ;get first symbol on new line -.first_ret_d: ; +.first_ret_d: ;первый возврат jcxz .correct_exit ;.end_loader ;found or not found parametrs in section exit in section cmp al, '[' jz .found_sect_d jmp .start_d -; ini default -; timeout, , ,.. =0 , +;просматриваем ini файл с начала в поисках секции указаной как default +;идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти .found_sect_d: ;check on name section @@ -215,9 +215,9 @@ end if pop ds pop si - jnz .not_compare_d_s ; :( )) + jnz .not_compare_d_s ;цепочка не совпала :( перейдем далее )) значит не исключение cmp byte[es:di], ']' - jnz .not_compare_d_s ; :( + jnz .not_compare_d_s ;нет в конце нашей секции завершающего символа :( @@ -243,7 +243,7 @@ end if jmp .start_d .correct_exit: - pop cx ; + pop cx ;восстановим значение счетчика pop di @@ -272,7 +272,7 @@ end if test status_flag, flag_found_timeout jz .correct_is_not_set_t - mov si, found_equal_timeout ; , + mov si, found_equal_timeout ;мы нашли что флаг уже установлен, информируем call printplain jmp .get_next_str @@ -288,7 +288,7 @@ end if inc cx dec di ;get timeout value -;2 a .. 0 99 +;2 знакa может быть обработано т.е. значение от 0 до 99 секунд push cx xor bx, bx mov cx, 2 diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_equ.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_equ.inc index c19d2393a..15ef90ccd 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_equ.inc +++ b/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_equ.inc @@ -23,76 +23,76 @@ ; (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 ; , -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 -flag_found_timeout equ 0x2 ;timeout value is found -flag_found_LM equ 0x1 ;found LM value -flag_found_RS equ 0x2 ;found RS value -flag_found_GTRFMS equ 0x4 ;found type RamFS -flag_found_RamdiskSector equ 0x8 ;found RamdiskSector -flag_found_RamdiskCluster equ 0x16 ;found RamdiskCluster -;statick data . -save_cx equ word [bp-2] ;save cx size ini file -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 +; Предопределения +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 +flag_found_timeout equ 0x2 ;timeout value is found +flag_found_LM equ 0x1 ;found LM value +flag_found_RS equ 0x2 ;found RS value +flag_found_GTRFMS equ 0x4 ;found type RamFS +flag_found_RamdiskSector equ 0x8 ;found RamdiskSector +flag_found_RamdiskCluster equ 0x16 ;found RamdiskCluster +;statick data эти данные не предопределяются в течении выполнения всей программы. +save_cx equ word [bp-2] ;save cx size ini file +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] -point_to_3 equ word [bp-20] -point_to_4 equ word [bp-22] -point_to_5 equ word [bp-24] -point_to_6 equ word [bp-26] -point_to_7 equ word [bp-28] -point_to_8 equ word [bp-30] -point_to_9 equ word [bp-32] -point_to_10 equ word [bp-34] -point_to_11 equ word [bp-36] -point_to_12 equ word [bp-38] -point_to_13 equ word [bp-40] -point_to_14 equ word [bp-42] -point_to_15 equ word [bp-44] -point_to_16 equ word [bp-46] -point_to_16 equ word [bp-48] -point_to_17 equ word [bp-50] -point_to_18 equ word [bp-52] +;данные которые зависимы от ветки выполнения и которые могут быть переопределены в процессе выполнения программы. +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] +point_to_3 equ word [bp-20] +point_to_4 equ word [bp-22] +point_to_5 equ word [bp-24] +point_to_6 equ word [bp-26] +point_to_7 equ word [bp-28] +point_to_8 equ word [bp-30] +point_to_9 equ word [bp-32] +point_to_10 equ word [bp-34] +point_to_11 equ word [bp-36] +point_to_12 equ word [bp-38] +point_to_13 equ word [bp-40] +point_to_14 equ word [bp-42] +point_to_15 equ word [bp-44] +point_to_16 equ word [bp-46] +point_to_16 equ word [bp-48] +point_to_17 equ word [bp-50] +point_to_18 equ word [bp-52] ;here array for fast scroling 16 word - poin to start section -point_to_point_def equ word [bp-54] -point_to_eframe equ word [bp-56] ;point on point frame +point_to_point_def equ word [bp-54] +point_to_eframe equ word [bp-56] ;point on point frame -; cx di -find_sec_di equ word [bp-58] ; di -info_real_mode_size equ word [bp-60]; .. , -free_ad_memory equ word [bp-62] ; -show_errors_sect equ word [bp-64] ; . -save_descript_size equ word [bp-66] ;save descript size previos section -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] ; +; тут расположено временное хранилище для 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] ;сохраним результат выполенения загрузки файла ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; , .. Enter, -; , .. 01000:0000 +;данные которые используются при обработке секции, т.е. после нажатия Enter, уже не возможно вернуться в первоначальный экран +;для возврата, необходимо перезапустить полностью код т.е. стартовать с 0х1000:0000 diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_proc.inc b/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_proc.inc index 7e23ba884..8032f4dd3 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_proc.inc +++ b/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_yellow equ 14 color_sym_white equ 15 if DEBUG decode: -;input eax - , es:di , cx=10 +;input eax - число, es:di куда писать, cx=10 cmp eax, ecx jb @f xor edx, edx @@ -198,7 +198,7 @@ show_name_section: dec di -; )) +;все вырезали и все готово для вывода имени секции )) push es pop ds @@ -249,52 +249,52 @@ end if pop si ret -.not_name_sec_fb: ; - +.not_name_sec_fb: ;нет имени в названии секции - значит так и скажем об этом push cs pop ds mov di, default_section_name jmp .def_sect_name ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; point_default , -; , +;процедура поиска вверх следующей секции +;в point_default содержиться указатель на дефаулт секцию, и в пределах врейма мы бегаем по заранее пропарсеными значениям указателей +;для того что бы отобразить и пропарсить следующий фрейм, нам нужно получить за пердыдущий или следующий указатель find_before_sect: mov di, point_default .e: push ini_data_ pop es - mov cx, di ; , di = - mov bx, cx ; + mov cx, di ;предположим будем просматривать к началу, текущая позиция di = сколько символов от начала документа имеется + mov bx, cx ;копия -; -; +;настроили указатель на дефаулт секцию +;будем искать вверх .find_start_section: - std ; - -; .. '[' + std ;установка флага направления - будем просматирвать к началу нашего ини файла +;будем искать начало секции т.е. '[' этот символ mov al, 0xa - repnz scasb ; - jcxz .go_ ; , ;(( ) + repnz scasb ;просканируем на наличее символа начала секции + jcxz .go_ ;мы просмотрели до начала файла, но так и ничего не нашли ;(( по тихому выйдем ) - mov find_sec_di, di ; + mov find_sec_di, di ;сохраним данные mov cx, di ; sub bx, cx - mov cx, bx ; x - - + mov cx, bx ;в сx значение - кол-во символов cld call get_firs_sym .ret_go: - jcxz ._not_section ; 0xa ... ; hello [] + jcxz ._not_section ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее - cmp di, point_loader; loader + cmp di, point_loader; секцию loader мы не заносим иначе крах jz ._not_section -; +;все удачно мы нашли вхождение секции предыдущей cmp al, '[' jnz ._not_section mov point_default, di .exit_scan_sect: ret -;;;;;;;; )) +;;;;;;;; восстановим значения и продолжим поиски начала секции которая нас устроит )) ._not_section: mov di, find_sec_di mov cx, di @@ -302,7 +302,7 @@ find_before_sect: jmp .find_start_section .go_: cld - mov cx, bx ; x - - + mov cx, bx ;в сx значение - кол-во символов mov al, byte [es:di] push word .f_go @@ -313,11 +313,11 @@ find_before_sect: jmp get_firs_sym.first_sp .f_go: - jcxz .exit_scan_sect ; 0xa ... ; hello [] + jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее - cmp di, point_loader; loader + cmp di, point_loader; секцию loader мы не заносим иначе крах jz .exit_scan_sect -; +;все удачно мы нашли вхождение секции предыдущей cmp al, '[' jnz .exit_scan_sect mov point_default, di @@ -336,14 +336,14 @@ find_next_sect: mov di, point_default push ini_data_ pop es - mov cx, save_cx;di ; , di = - sub cx, di ; cx .. + mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется + sub cx, di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало jmp .let_s_go .h: push ini_data_ pop es - mov cx, save_cx;di ; , di = -; sub cx,di ; cx .. + mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется +; sub cx,di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало mov al, byte [es:di] push word .let_s_go_ret @@ -356,17 +356,17 @@ find_next_sect: -; -; +;настроили указатель на дефаулт секцию +;будем искать вниз .let_s_go: call get_firs_sym .let_s_go_ret: - jcxz .exit_scan_sect ; 0xa ... ; hello [] + jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее cmp al, '[' jnz .let_s_go cmp di, point_loader jz .let_s_go -; +;все удачно мы нашли вхождение секции предыдущей mov point_default, di .exit_scan_sect: ret @@ -374,8 +374,8 @@ find_next_sect: ;;;;;;;;;;;;;;;;;;;;;;;;;; ;clean old cursor clean_active_cursor: -; ax -; +;не изменяет значение ax +;отображение курсора по умолчанию lea si, point_to_hframe mov di, 962-160 mov dx, point_default @@ -405,7 +405,7 @@ end if pop ax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; +;установка таймера и отображение счетчика времени gettime: mov ah, 0 int 1Ah @@ -462,13 +462,13 @@ newtimer: mov dword [timer_], eax mov sp, word [start_stack] mov bp, word [save_bp_from_timer] -;; :( +;;не восстановленый стек :( sti jmp parse_start.parse_run_only .decode: -;input ax - , es:di , bx=10 +;input ax - число, es:di куда писать, bx=10 cmp ax, bx jb @f xor dx, dx @@ -485,9 +485,9 @@ newtimer: ret show_bl_sc_sect: -;1) . - - Section unname -; . -; es:di - - cx +;1) отображение списка секций. Если секция не имет имя - ошибка - вывод Section unname +;проверка на наличее имени. +;входные данные es:di -указатель на секцию - cx размер секции ; push bp mov bx, point_to_eframe lea si, point_to_hframe diff --git a/kernel/branches/Kolibri-acpi/sec_loader/trunk/startos.ini b/kernel/branches/Kolibri-acpi/sec_loader/trunk/startos.ini index cb9c409e2..9de8796b0 100644 --- a/kernel/branches/Kolibri-acpi/sec_loader/trunk/startos.ini +++ b/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" diff --git a/kernel/branches/Kolibri-acpi/video/cursors.inc b/kernel/branches/Kolibri-acpi/video/cursors.inc index dfbfda3f1..98b928b1d 100644 --- a/kernel/branches/Kolibri-acpi/video/cursors.inc +++ b/kernel/branches/Kolibri-acpi/video/cursors.inc @@ -846,7 +846,7 @@ check_mouse_area_for_getpixel_new: 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 @@ align 4 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 @@ init_display: 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,24 +1008,21 @@ align 4 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 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 diff --git a/kernel/branches/Kolibri-acpi/video/vesa20.inc b/kernel/branches/Kolibri-acpi/video/vesa20.inc index d1373d4c0..753eb363c 100644 --- a/kernel/branches/Kolibri-acpi/video/vesa20.inc +++ b/kernel/branches/Kolibri-acpi/video/vesa20.inc @@ -231,7 +231,7 @@ align 4 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 @@ align 4 ;-------------------------------------- ; 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 @@ align 4 .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 @@ align 4 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 @@ align 4 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 @@ align 4 .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 @@ dp2: 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 @@ nbgp: ;-------------------------------------- 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 @@ dp4: 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 @@ vesa20_drawbackground_stretch: 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 ;-------------------------------------- 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 @@ sdp4: 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 @@ sdpdone: add esp, 44 popad mov [EGA_counter], 1 - cmp [SCR_MODE], dword 0x12 + cmp [SCR_MODE], 0x12 jne @f call VGA_drawbackground ;-------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/video/vga.inc b/kernel/branches/Kolibri-acpi/video/vga.inc index 7bf5fbadc..46002f7d4 100644 --- a/kernel/branches/Kolibri-acpi/video/vga.inc +++ b/kernel/branches/Kolibri-acpi/video/vga.inc @@ -150,7 +150,7 @@ endg ;------------------------------------------------------------------------------ align 4 checkVga_N13: - cmp [SCR_MODE], dword 0x13 + cmp [SCR_MODE], 0x13 jne @f pushad @@ -403,7 +403,7 @@ align 4 align 4 .no_mouseunder: shl ebx, 9 - lea ebx, [ebx+ebx*4] ; 5 + lea ebx, [ebx+ebx*4] ; умножение на 5 lea edx, [ebx+ecx*4] ; + x*BytesPerPixel (Vesa2.0 32) mov edi, edx add edi, [LFBAddress] ; + LFB address @@ -501,7 +501,7 @@ VGA_draw_bar_1: add eax, [temp.cx] and eax, 0xfff8 shl ebx, 9 - lea ebx, [ebx+ebx*4]; 5 + lea ebx, [ebx+ebx*4]; умножение на 5 lea ebx, [ebx+eax*4] ; + x*BytesPerPixel (Vesa2.0 32) mov esi, ebx add esi, [LFBAddress] ; + LFB address