diff --git a/kernel/branches/kolibri-process/COPYING.TXT b/kernel/branches/kolibri-process/COPYING.TXT new file mode 100644 index 000000000..f6213b69c --- /dev/null +++ b/kernel/branches/kolibri-process/COPYING.TXT @@ -0,0 +1,347 @@ + + GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + + GNU GENERAL PUBLIC LICENSE + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kernel/branches/kolibri-process/blkdev/bd_drv.inc b/kernel/branches/kolibri-process/blkdev/bd_drv.inc new file mode 100644 index 000000000..4ae53cf38 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/bd_drv.inc @@ -0,0 +1,293 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4420 $ + + +; Access through BIOS by diamond +iglobal +align 4 +bd_callbacks: + dd bd_callbacks.end - bd_callbacks ; strucsize + dd 0 ; no close function + dd 0 ; no closemedia function + dd bd_querymedia + dd bd_read_interface + dd bd_write_interface + dd 0 ; no flush function + dd 0 ; use default cache size +.end: +endg + +proc bd_read_interface stdcall uses edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer for data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really read +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and edi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov eax, dword [startsector] + mov edi, [buffer] +; 4. Worker procedures take one sectors per time, so loop over all sectors to read. +.sectors_loop: + call bd_read + cmp [hd_error], 0 + jnz .fail + mov ecx, [numsectors] + inc dword [ecx] ; one more sector is read + dec [sectors_todo] + jz .done + inc eax + jnz .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +proc bd_write_interface stdcall uses esi edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer with data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really written +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and esi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov esi, [buffer] + lea edi, [startsector] + mov [cache_chain_ptr], edi +; 4. Worker procedures take max 16 sectors per time, +; loop until all sectors will be processed. +.sectors_loop: + mov ecx, 16 + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [cache_chain_size], cl + call bd_write_cache_chain + cmp [hd_error], 0 + jnz .fail + movzx ecx, [cache_chain_size] + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .done + add [edi], ecx + jc .fail + shl ecx, 9 + add esi, ecx + jmp .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +; This is a stub. +proc bd_querymedia stdcall, hd_data, mediainfo + mov eax, [mediainfo] + mov [eax+DISKMEDIAINFO.Flags], 0 + mov [eax+DISKMEDIAINFO.SectorSize], 512 + or dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF + or dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF + xor eax, eax + ret +endp + +;----------------------------------------------------------------------------- +; \begin{diamond} +uglobal +bios_hdpos dd 0 ; 0 is invalid value for [hdpos] +bios_cur_sector dd ? +bios_read_len dd ? +endg +;----------------------------------------------------------------------------- +align 4 +bd_read: + push eax + push edx + mov edx, [bios_hdpos] + cmp edx, [hdpos] + jne .notread + mov edx, [bios_cur_sector] + cmp eax, edx + jb .notread + add edx, [bios_read_len] + dec edx + cmp eax, edx + ja .notread + sub eax, [bios_cur_sector] + shl eax, 9 + add eax, (OS_BASE+0x9A000) + push ecx esi + mov esi, eax + mov ecx, 512/4 + cld + rep movsd + pop esi ecx + pop edx + pop eax + ret +.notread: + push ecx + mov dl, 42h + mov ecx, 16 + call int13_call + pop ecx + test eax, eax + jnz .v86err + test edx, edx + jz .readerr + mov [bios_read_len], edx + mov edx, [hdpos] + mov [bios_hdpos], edx + pop edx + pop eax + mov [bios_cur_sector], eax + jmp bd_read +.readerr: +.v86err: + mov [hd_error], 1 + jmp hd_read_error +;----------------------------------------------------------------------------- +align 4 +bd_write_cache_chain: + pusha + mov edi, OS_BASE + 0x9A000 + movzx ecx, [cache_chain_size] + push ecx + shl ecx, 9-2 + rep movsd + pop ecx + mov dl, 43h + mov eax, [cache_chain_ptr] + mov eax, [eax] + call int13_call + test eax, eax + jnz .v86err + cmp edx, ecx + jnz .writeerr + popa + ret +.v86err: +.writeerr: + popa + mov [hd_error], 1 + jmp hd_write_error +;----------------------------------------------------------------------------- +uglobal +int13_regs_in rb sizeof.v86_regs +int13_regs_out rb sizeof.v86_regs +endg +;----------------------------------------------------------------------------- +align 4 +int13_call: +; Because this code uses fixed addresses, +; it can not be run simultaniously by many threads. +; In current implementation it is protected by common mutex 'ide_status' + mov word [OS_BASE + 510h], 10h ; packet length + mov word [OS_BASE + 512h], cx ; number of sectors + mov dword [OS_BASE + 514h], 9A000000h ; buffer 9A00:0000 + mov dword [OS_BASE + 518h], eax + and dword [OS_BASE + 51Ch], 0 + push ebx ecx esi edi + mov ebx, int13_regs_in + mov edi, ebx + mov ecx, sizeof.v86_regs/4 + xor eax, eax + rep stosd + mov byte [ebx+v86_regs.eax+1], dl + mov eax, [hdpos] + lea eax, [BiosDisksData+(eax-80h)*4] + mov dl, [eax] + mov byte [ebx+v86_regs.edx], dl + movzx edx, byte [eax+1] +; mov dl, 5 + test edx, edx + jnz .hasirq + dec edx + jmp @f +.hasirq: + pushad + stdcall enable_irq, edx + popad +@@: + mov word [ebx+v86_regs.esi], 510h + mov word [ebx+v86_regs.ss], 9000h + mov word [ebx+v86_regs.esp], 0A000h + mov word [ebx+v86_regs.eip], 500h + mov [ebx+v86_regs.eflags], 20200h + mov esi, [sys_v86_machine] + mov ecx, 0x502 + push fs + call v86_start + pop fs + and [bios_hdpos], 0 + pop edi esi ecx ebx + movzx edx, byte [OS_BASE + 512h] + test byte [int13_regs_out+v86_regs.eflags], 1 + jnz @f + mov edx, ecx +@@: + ret +; \end{diamond} diff --git a/kernel/branches/kolibri-process/blkdev/cd_drv.inc b/kernel/branches/kolibri-process/blkdev/cd_drv.inc new file mode 100644 index 000000000..2250fa836 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/cd_drv.inc @@ -0,0 +1,953 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3742 $ + + +;********************************************************** +; Непосредственная работа с устройством СD (ATAPI) +;********************************************************** +; Автор части исходного текста Кулаков Владимир Геннадьевич +; Адаптация, доработка и разработка Mario79, + +; Максимальное количество повторений операции чтения +MaxRetr equ 10 +; Предельное время ожидания готовности к приему команды +; (в тиках) +BSYWaitTime equ 1000 ;2 +NoTickWaitTime equ 0xfffff +CDBlockSize equ 2048 +;******************************************** +;* ЧТЕНИЕ СЕКТОРА С ПОВТОРАМИ * +;* Многократное повторение чтения при сбоях * +;******************************************** +ReadCDWRetr: +;----------------------------------------------------------- +; input : eax = block to read +; ebx = destination +;----------------------------------------------------------- + pushad + mov eax, [CDSectorAddress] + mov ebx, [CDDataBuf_pointer] + call cd_calculate_cache + xor edi, edi + add esi, 8 + inc edi +.hdreadcache: +; cmp dword [esi+4],0 ; empty +; je .nohdcache + cmp [esi], eax ; correct sector + je .yeshdcache +.nohdcache: + add esi, 8 + inc edi + dec ecx + jnz .hdreadcache + call find_empty_slot_CD_cache ; ret in edi + + push edi + push eax + call cd_calculate_cache_2 + shl edi, 11 + add edi, eax + mov [CDDataBuf_pointer], edi + pop eax + pop edi + + call ReadCDWRetr_1 + cmp [DevErrorCode], 0 + jne .exit + + mov [CDDataBuf_pointer], ebx + 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 + add esi, eax + pop eax + mov edi, ebx;[CDDataBuf_pointer] + mov ecx, 512;/4 + cld + rep movsd ; move data +.exit: + popad + ret + +ReadCDWRetr_1: + pushad + +; Цикл, пока команда не выполнена успешно или не +; исчерпано количество попыток + mov ECX, MaxRetr +@@NextRetr: +; Подать команду +;************************************************* +;* ПОЛНОЕ ЧТЕНИЕ СЕКТОРА КОМПАКТ-ДИСКА * +;* Считываются данные пользователя, информация * +;* субканала и контрольная информация * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале; * +;* CDSectorAddress - адрес считываемого сектора. * +;* Данные считывается в массив CDDataBuf. * +;************************************************* +;ReadCD: + push ecx +; pusha +; Задать размер сектора +; mov [CDBlockSize],2048 ;2352 +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать пакетную команду для считывания +; сектора данных +; Задать код команды Read CD + mov [PacketCommand], byte 0x28;0xBE +; Задать адрес сектора + mov AX, word [CDSectorAddress+2] + xchg AL, AH + mov word [PacketCommand+2], AX + mov AX, word [CDSectorAddress] + xchg AL, AH + mov word [PacketCommand+4], AX +; mov eax,[CDSectorAddress] +; mov [PacketCommand+2],eax +; Задать количество считываемых секторов + mov [PacketCommand+8], byte 1 +; Задать считывание данных в полном объеме +; mov [PacketCommand+9],byte 0xF8 +; Подать команду + call SendPacketDatCommand + pop ecx +; ret + +; cmp [DevErrorCode],0 + test eax, eax + jz @@End_4 + + or ecx, ecx ;{SPraid.simba} (for cd load) + jz @@End_4 + dec ecx + + cmp [timer_ticks_enable], 0 + jne @f + mov eax, NoTickWaitTime +.wait: + dec eax +; test eax,eax + jz @@NextRetr + jmp .wait +@@: +; Задержка на 2,5 секунды +; mov EAX,[timer_ticks] +; add EAX,50 ;250 +;@@Wait: +; call change_task +; cmp EAX,[timer_ticks] +; ja @@Wait + loop @@NextRetr +@@End_4: + mov dword [DevErrorCode], eax + popad + ret + + +; Универсальные процедуры, обеспечивающие выполнение +; пакетных команд в режиме PIO + +; Максимально допустимое время ожидания реакции +; устройства на пакетную команду (в тиках) + +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 - размер принимаемого блока данных. * +; return eax DevErrorCode +;**************************************************** +SendPacketDatCommand: + xor eax, eax +; mov byte [DevErrorCode],al +; Задать режим CHS + mov byte [ATAAddressMode], al +; Послать 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 ;закончить, сохранив код ошибки + +; Ожидание готовности дисковода к приему +; пакетной команды + mov DX, [ATABasePortAddr] + add DX, 7 ;порт 1х7h + mov ecx, NoTickWaitTime +@@WaitDevice0: + cmp [timer_ticks_enable], 0 + jne @f + dec ecx +; test ecx,ecx + jz @@Err1_1 + jmp .test +@@: + call change_task + ; Проверить время выполнения команды + mov EAX, [timer_ticks] + sub EAX, [TickCounter_1] + cmp EAX, BSYWaitTime + ja @@Err1_1 ;ошибка тайм-аута + ; Проверить готовность +.test: + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitDevice0 + test AL, 1 ;состояние сигнала ERR + jnz @@Err6 + test AL, 08h ;состояние сигнала DRQ + jz @@WaitDevice0 +; Послать пакетную команду + cli + mov DX, [ATABasePortAddr] + mov AX, [PacketCommand] + out DX, AX + mov AX, [PacketCommand+2] + out DX, AX + mov AX, [PacketCommand+4] + out DX, AX + mov AX, [PacketCommand+6] + out DX, AX + mov AX, [PacketCommand+8] + out DX, AX + mov AX, [PacketCommand+10] + out DX, AX + sti +; Ожидание готовности данных + mov DX, [ATABasePortAddr] + add DX, 7 ;порт 1х7h + mov ecx, NoTickWaitTime +@@WaitDevice1: + cmp [timer_ticks_enable], 0 + jne @f + dec ecx +; test ecx,ecx + jz @@Err1_1 + jmp .test_1 +@@: + call change_task + ; Проверить время выполнения команды + mov EAX, [timer_ticks] + sub EAX, [TickCounter_1] + cmp EAX, MaxCDWaitTime + ja @@Err1_1 ;ошибка тайм-аута + ; Проверить готовность +.test_1: + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitDevice1 + test AL, 1 ;состояние сигнала ERR + jnz @@Err6_temp + test AL, 08h ;состояние сигнала DRQ + jz @@WaitDevice1 +; Принять блок данных от контроллера + mov EDI, [CDDataBuf_pointer];0x7000 ;CDDataBuf + ; Загрузить адрес регистра данных контроллера + mov DX, [ATABasePortAddr];порт 1x0h + ; Загрузить в счетчик размер блока в байтах + xor ecx, ecx + mov CX, CDBlockSize + ; Вычислить размер блока в 16-разрядных словах + shr CX, 1;разделить размер блока на 2 + ; Принять блок данных + cli + cld + rep insw + sti +; Успешное завершение приема данных +@@End_8: + xor eax, eax + ret + +; Записать код ошибки +@@Err1_1: + xor eax, eax + inc eax + ret +; mov [DevErrorCode],1 +; ret +@@Err6_temp: + mov eax, 7 + ret +; mov [DevErrorCode],7 +; ret +@@Err6: + mov eax, 6 + ret +; mov [DevErrorCode],6 +;@@End_8: +; ret + + + +;*********************************************** +;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, * +;* НЕ ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧИ ДАННЫХ * +;* Входные параметры передаются через * +;* глобальные перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале; * +;* PacketCommand - 12-байтный командный пакет. * +;*********************************************** +SendPacketNoDatCommand: + pushad + xor eax, eax +; mov byte [DevErrorCode],al +; Задать режим CHS + mov byte [ATAAddressMode], al +; Послать ATA-команду передачи пакетной команды + mov byte [ATAFeatures], al + mov byte [ATASectorCount], al + mov byte [ATASectorNumber], al + mov word [ATACylinder], ax + mov byte [ATAHead], al + mov [ATACommand], 0A0h + call SendCommandToHDD_1 +; cmp [DevErrorCode],0 ;проверить код ошибки + test eax, eax + jnz @@End_9 ;закончить, сохранив код ошибки +; Ожидание готовности дисковода к приему +; пакетной команды + mov DX, [ATABasePortAddr] + add DX, 7 ;порт 1х7h +@@WaitDevice0_1: + call change_task + ; Проверить время ожидания + mov EAX, [timer_ticks] + sub EAX, [TickCounter_1] + cmp EAX, BSYWaitTime + ja @@Err1_3 ;ошибка тайм-аута + ; Проверить готовность + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitDevice0_1 + test AL, 1 ;состояние сигнала ERR + jnz @@Err6_1 + test AL, 08h ;состояние сигнала DRQ + jz @@WaitDevice0_1 +; Послать пакетную команду +; cli + mov DX, [ATABasePortAddr] + mov AX, word [PacketCommand] + out DX, AX + mov AX, word [PacketCommand+2] + out DX, AX + mov AX, word [PacketCommand+4] + out DX, AX + mov AX, word [PacketCommand+6] + out DX, AX + mov AX, word [PacketCommand+8] + out DX, AX + mov AX, word [PacketCommand+10] + out DX, AX +; sti + cmp [ignore_CD_eject_wait], 1 + je @@clear_DEC +; Ожидание подтверждения приема команды + mov DX, [ATABasePortAddr] + add DX, 7 ;порт 1х7h +@@WaitDevice1_1: + call change_task + ; Проверить время выполнения команды + mov EAX, [timer_ticks] + sub EAX, [TickCounter_1] + cmp EAX, MaxCDWaitTime + ja @@Err1_3 ;ошибка тайм-аута + ; Ожидать освобождения устройства + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitDevice1_1 + test AL, 1 ;состояние сигнала ERR + jnz @@Err6_1 + test AL, 40h ;состояние сигнала DRDY + jz @@WaitDevice1_1 +@@clear_DEC: + and [DevErrorCode], 0 + popad + ret +; Записать код ошибки +@@Err1_3: + xor eax, eax + inc eax + jmp @@End_9 +@@Err6_1: + mov eax, 6 +@@End_9: + mov [DevErrorCode], eax + popad + ret + +;**************************************************** +;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска (0 или 1); * +;* ATAFeatures - "особенности"; * +;* ATASectorCount - количество секторов; * +;* ATASectorNumber - номер начального сектора; * +;* ATACylinder - номер начального цилиндра; * +;* ATAHead - номер начальной головки; * +;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); * +;* ATACommand - код команды. * +;* После успешного выполнения функции: * +;* в ATABasePortAddr - базовый адрес HDD; * +;* в DevErrorCode - ноль. * +;* При возникновении ошибки в DevErrorCode будет * +;* возвращен код ошибки в eax * +;**************************************************** +SendCommandToHDD_1: +; pushad +; mov [DevErrorCode],0 not need +; Проверить значение кода режима + cmp [ATAAddressMode], 1 + ja @@Err2_4 +; Проверить корректность номера канала + mov BX, [ChannelNumber] + cmp BX, 1 + jb @@Err3_4 + cmp BX, 2 + ja @@Err3_4 +; Установить базовый адрес + dec BX + shl BX, 1 + movzx ebx, bx + mov AX, [ebx+StandardATABases] + mov [ATABasePortAddr], AX +; Ожидание готовности HDD к приему команды + ; Выбрать нужный диск + mov DX, [ATABasePortAddr] + add DX, 6 ;адрес регистра головок + mov AL, [DiskNumber] + 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 +; test ecx,ecx + jz @@Err1_4 + jmp .test +@@: + call change_task + ; Проверить время ожидания + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, BSYWaitTime;300 ;ожидать 3 сек. + ja @@Err1_4 ;ошибка тайм-аута + ; Прочитать регистр состояния +.test: + in AL, DX + ; Проверить состояние сигнала BSY + test AL, 80h + jnz @@WaitHDReady_2 + ; Проверить состояние сигнала DRQ + test AL, 08h + jnz @@WaitHDReady_2 + +; Загрузить команду в регистры контроллера + cli + mov DX, [ATABasePortAddr] + inc DX ;регистр "особенностей" + mov AL, [ATAFeatures] + out DX, AL + inc DX ;счетчик секторов + mov AL, [ATASectorCount] + out DX, AL + inc DX ;регистр номера сектора + mov AL, [ATASectorNumber] + out DX, AL + inc DX ;номер цилиндра (младший байт) + mov AX, [ATACylinder] + out DX, AL + inc DX ;номер цилиндра (старший байт) + mov AL, AH + out DX, AL + inc DX ;номер головки/номер диска + mov AL, [DiskNumber] + shl AL, 4 + cmp [ATAHead], 0Fh;проверить номер головки + ja @@Err5_4 + or AL, [ATAHead] + or AL, 10100000b + mov AH, [ATAAddressMode] + shl AH, 6 + or AL, AH + out DX, AL +; Послать команду + mov AL, [ATACommand] + inc DX ;регистр команд + out DX, AL + sti +; Сбросить признак ошибки +; mov [DevErrorCode],0 +@@End_10: + xor eax, eax + ret +; Записать код ошибки +@@Err1_4: + xor eax, eax + inc eax +; mov [DevErrorCode],1 + ret +@@Err2_4: + mov eax, 2 +; mov [DevErrorCode],2 + ret +@@Err3_4: + mov eax, 3 +; mov [DevErrorCode],3 + ret +@@Err4_4: + mov eax, 4 +; mov [DevErrorCode],4 + ret +@@Err5_4: + mov eax, 5 +; mov [DevErrorCode],5 +; Завершение работы программы + ret +; sti +; popad + +;************************************************* +;* ОЖИДАНИЕ ГОТОВНОСТИ УСТРОЙСТВА К РАБОТЕ * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +WaitUnitReady: + pusha +; Запомнить время начала операции + mov EAX, [timer_ticks] + mov [WURStartTime], EAX +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду TEST UNIT READY + mov [PacketCommand], word 00h +; ЦИКЛ ОЖИДАНИЯ ГОТОВНОСТИ УСТРОЙСТВА + mov ecx, NoTickWaitTime +@@SendCommand: + ; Подать команду проверки готовности + call SendPacketNoDatCommand + cmp [timer_ticks_enable], 0 + jne @f + cmp [DevErrorCode], 0 + je @@End_11 + dec ecx +; cmp ecx,0 + jz .Error + 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 - номер диска на канале. * +;************************************************* +prevent_medium_removal: + pusha +; Очистить буфер пакетной команды + call clear_packet_buffer +; Задать код команды + mov [PacketCommand], byte 0x1E +; Задать код запрета + mov [PacketCommand+4], byte 11b +; Подать команду + call SendPacketNoDatCommand + mov eax, ATAPI_IDE0_lock + add eax, [cdpos] + dec eax + mov [eax], byte 1 + popa + ret + +;************************************************* +;* РАЗРЕШИТЬ СМЕНУ ДИСКА * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +allow_medium_removal: + pusha +; Очистить буфер пакетной команды + call clear_packet_buffer +; Задать код команды + mov [PacketCommand], byte 0x1E +; Задать код запрета + mov [PacketCommand+4], byte 00b +; Подать команду + call SendPacketNoDatCommand + mov eax, ATAPI_IDE0_lock + add eax, [cdpos] + dec eax + mov [eax], byte 0 + popa + ret + +;************************************************* +;* ЗАГРУЗИТЬ НОСИТЕЛЬ В ДИСКОВОД * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +LoadMedium: + pusha +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду START/STOP UNIT + ; Задать код команды + mov [PacketCommand], word 1Bh + ; Задать операцию загрузки носителя + mov [PacketCommand+4], word 00000011b +; Подать команду + call SendPacketNoDatCommand + popa + ret + +;************************************************* +;* ИЗВЛЕЧЬ НОСИТЕЛЬ ИЗ ДИСКОВОДА * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +EjectMedium: + pusha +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать команду START/STOP UNIT + ; Задать код команды + mov [PacketCommand], word 1Bh + ; Задать операцию извлечения носителя + mov [PacketCommand+4], word 00000010b +; Подать команду + call SendPacketNoDatCommand + popa + ret + +;************************************************* +;* Проверить событие нажатия кнопки извлечения * +;* диска * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* 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 + mov al, [DRIVE_DATA+1] + and al, 11b + cmp al, 10b + jz .ide3 +.ide2_1: + mov al, [DRIVE_DATA+1] + and al, 1100b + cmp al, 1000b + jz .ide2 +.ide1_1: + mov al, [DRIVE_DATA+1] + and al, 110000b + cmp al, 100000b + jz .ide1 +.ide0_1: + mov al, [DRIVE_DATA+1] + and al, 11000000b + cmp al, 10000000b + jz .ide0 +.end: + + sti + mov eax, [timer_ticks] + mov [timer_ATAPI_check], eax +.end_1: + popa + ret + +.ide3: + cli + cmp [ATAPI_IDE3_lock], 1 + jne .ide2_1 + cmp [IDE_Channel_2], 0 + jne .ide1_1 + cmp [cd_status], 0 + jne .end + mov [IDE_Channel_2], 1 + mov ecx, ide_channel2_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 1 + mov [cdpos], 4 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + je .eject_ide3 + call syscall_cdaudio.free + jmp .ide2_1 +.eject_ide3: + call .eject + call syscall_cdaudio.free + jmp .ide2_1 + +.ide2: + cli + cmp [ATAPI_IDE2_lock], 1 + jne .ide1_1 + cmp [IDE_Channel_2], 0 + jne .ide1_1 + cmp [cd_status], 0 + jne .end + mov [IDE_Channel_2], 1 + mov ecx, ide_channel2_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 0 + mov [cdpos], 3 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + je .eject_ide2 + call syscall_cdaudio.free + jmp .ide1_1 +.eject_ide2: + call .eject + call syscall_cdaudio.free + jmp .ide1_1 + +.ide1: + cli + cmp [ATAPI_IDE1_lock], 1 + jne .ide0_1 + cmp [IDE_Channel_1], 0 + jne .end + cmp [cd_status], 0 + jne .end + mov [IDE_Channel_1], 1 + mov ecx, ide_channel1_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 1 + mov [cdpos], 2 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + je .eject_ide1 + call syscall_cdaudio.free + jmp .ide0_1 +.eject_ide1: + call .eject + call syscall_cdaudio.free + jmp .ide0_1 + +.ide0: + cli + cmp [ATAPI_IDE0_lock], 1 + jne .end + cmp [IDE_Channel_1], 0 + jne .end + cmp [cd_status], 0 + jne .end + mov [IDE_Channel_1], 1 + mov ecx, ide_channel1_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + mov [cdpos], 1 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + je .eject_ide0 + call syscall_cdaudio.free + jmp .end +.eject_ide0: + call .eject + call syscall_cdaudio.free + jmp .end + +.eject: + call clear_CD_cache + call allow_medium_removal + mov [ignore_CD_eject_wait], 1 + call EjectMedium + mov [ignore_CD_eject_wait], 0 + ret +iglobal +timer_ATAPI_check dd 0 +ATAPI_IDE0_lock db 0 +ATAPI_IDE1_lock db 0 +ATAPI_IDE2_lock db 0 +ATAPI_IDE3_lock db 0 +ignore_CD_eject_wait db 0 +endg +;************************************************* +;* Получить сообщение о событии или состоянии * +;* устройства * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +GetEvent_StatusNotification: + pusha + mov [CDDataBuf_pointer], CDDataBuf +; Очистить буфер пакетной команды + call clear_packet_buffer +; Задать код команды + mov [PacketCommand], byte 4Ah + mov [PacketCommand+1], byte 00000001b +; Задать запрос класса сообщений + mov [PacketCommand+4], byte 00010000b +; Размер выделенной области + mov [PacketCommand+7], byte 8h + mov [PacketCommand+8], byte 0h +; Подать команду + call SendPacketDatCommand + popa + ret + +;************************************************* +; прочитать информацию из TOC +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +Read_TOC: + pusha + mov [CDDataBuf_pointer], CDDataBuf +; Очистить буфер пакетной команды + call clear_packet_buffer +; Сформировать пакетную команду для считывания +; сектора данных + mov [PacketCommand], byte 0x43 + ; Задать формат + mov [PacketCommand+2], byte 1 +; Размер выделенной области + mov [PacketCommand+7], byte 0xFF + mov [PacketCommand+8], byte 0h +; Подать команду + call SendPacketDatCommand + popa + ret + +;************************************************* +;* ОПРЕДЕЛИТЬ ОБЩЕЕ КОЛИЧЕСТВО СЕКТОРОВ НА ДИСКЕ * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;************************************************* +;ReadCapacity: +; pusha +;; Очистить буфер пакетной команды +; call clear_packet_buffer +;; Задать размер буфера в байтах +; mov [CDBlockSize],8 +;; Сформировать команду 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 + ret diff --git a/kernel/branches/kolibri-process/blkdev/cdrom.inc b/kernel/branches/kolibri-process/blkdev/cdrom.inc new file mode 100644 index 000000000..a57d7667b --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/cdrom.inc @@ -0,0 +1,271 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +sys_cd_audio: + + cmp word [cdbase], word 0 + jnz @f + mov eax, 1 + ret + @@: + + ; eax=1 cdplay at ebx 0x00FFSSMM + ; eax=2 get tracklist size of ecx to [ebx] + ; eax=3 stop/pause playing + + cmp eax, 1 + jnz nocdp + call sys_cdplay + ret + nocdp: + + cmp eax, 2 + jnz nocdtl + mov edi, [TASK_BASE] + add edi, TASKDATA.mem_start + add ebx, [edi] + call sys_cdtracklist + ret + nocdtl: + + cmp eax, 3 + jnz nocdpause + call sys_cdpause + ret + nocdpause: + + mov eax, 0xffffff01 + ret + + + +sys_cd_atapi_command: + + pushad + + mov dx, word [cdbase] + add dx, 6 + mov ax, word [cdid] + out dx, al + mov esi, 10 + call delay_ms + mov dx, word [cdbase] + add dx, 7 + in al, dx + and al, 0x80 + cmp al, 0 + jnz res + jmp cdl6 + res: + mov dx, word [cdbase] + add dx, 7 + mov al, 0x8 + out dx, al + mov dx, word [cdbase] + add dx, 0x206 + mov al, 0xe + out dx, al + mov esi, 1 + call delay_ms + mov dx, word [cdbase] + add dx, 0x206 + mov al, 0x8 + out dx, al + mov esi, 30 + call delay_ms + xor cx, cx + cdl5: + inc cx + cmp cx, 10 + jz cdl6 + mov dx, word [cdbase] + add dx, 7 + in al, dx + and al, 0x88 + cmp al, 0x00 + jz cdl5 + mov esi, 100 + call delay_ms + jmp cdl5 + cdl6: + mov dx, word [cdbase] + add dx, 4 + mov al, 0 + out dx, al + mov dx, word [cdbase] + add dx, 5 + mov al, 0 + out dx, al + mov dx, word [cdbase] + add dx, 7 + mov al, 0xec + out dx, al + mov esi, 5 + call delay_ms + mov dx, word [cdbase] + add dx, 1 + mov al, 0 + out dx, al + add dx, 1 + mov al, 0 + out dx, al + add dx, 1 + mov al, 0 + out dx, al + add dx, 1 + mov al, 0 + out dx, al + add dx, 1 + mov al, 128 + out dx, al + add dx, 2 + mov al, 0xa0 + out dx, al + xor cx, cx + mov dx, word [cdbase] + add dx, 7 + cdl1: + inc cx + cmp cx, 100 + jz cdl2 + in al, dx + and ax, 0x88 + cmp al, 0x8 + jz cdl2 + mov esi, 2 + call delay_ms + jmp cdl1 + cdl2: + + popad + ret + + +sys_cdplay: + + mov ax, 5 + push ax + push ebx + cdplay: + call sys_cd_atapi_command + cli + mov dx, word [cdbase] + mov ax, 0x0047 + out dx, ax + mov al, 1 + mov ah, [esp+0]; min xx + out dx, ax + mov ax, [esp+1]; fr sec + out dx, ax + mov ax, 256+99 + out dx, ax + mov ax, 0x0001 + out dx, ax + mov ax, 0x0000 + out dx, ax + mov esi, 10 + call delay_ms + sti + add dx, 7 + in al, dx + test al, 1 + jz cdplayok + mov ax, [esp+4] + dec ax + mov [esp+4], ax + cmp ax, 0 + jz cdplayfail + jmp cdplay + cdplayfail: + cdplayok: + pop ebx + pop ax + xor eax, eax + ret + + +sys_cdtracklist: + + push ebx + tcdplay: + call sys_cd_atapi_command + mov dx, word [cdbase] + mov ax, 0x43+2*256 + out dx, ax + mov ax, 0x0 + out dx, ax + mov ax, 0x0 + out dx, ax + mov ax, 0x0 + out dx, ax + mov ax, 200 + out dx, ax + mov ax, 0x0 + out dx, ax + in al, dx + mov cx, 1000 + mov dx, word [cdbase] + add dx, 7 + cld + cdtrnwewait: + mov esi, 10 + call delay_ms + in al, dx + and al, 128 + cmp al, 0 + jz cdtrl1 + loop cdtrnwewait + cdtrl1: + ; read the result + mov ecx, [esp+0] + mov dx, word [cdbase] + cdtrread: + add dx, 7 + in al, dx + and al, 8 + cmp al, 8 + jnz cdtrdone + sub dx, 7 + in ax, dx + mov [ecx], ax + add ecx, 2 + jmp cdtrread + cdtrdone: + pop ecx + xor eax, eax + ret + + +sys_cdpause: + + call sys_cd_atapi_command + + mov dx, word [cdbase] + mov ax, 0x004B + out dx, ax + mov ax, 0 + out dx, ax + mov ax, 0 + out dx, ax + mov ax, 0 + out dx, ax + mov ax, 0 + out dx, ax + mov ax, 0 + out dx, ax + + mov esi, 10 + call delay_ms + add dx, 7 + in al, dx + + xor eax, eax + ret + diff --git a/kernel/branches/kolibri-process/blkdev/disk.inc b/kernel/branches/kolibri-process/blkdev/disk.inc new file mode 100644 index 000000000..e08cf2803 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/disk.inc @@ -0,0 +1,1330 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Error codes for callback functions. +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_END_OF_MEDIA = 3 ; end of media while reading/writing data +; Driver flags. Represent bits in DISK.DriverFlags. +DISK_NO_INSERT_NOTIFICATION = 1 +; Media flags. Represent bits in DISKMEDIAINFO.Flags. +DISK_MEDIA_READONLY = 1 + +; If too many partitions are detected,there is probably an error on the disk. +; 256 partitions should be enough for any reasonable use. +; Also, the same number is limiting the number of MBRs to process; if +; too many MBRs are visible,there probably is a loop in the MBR structure. +MAX_NUM_PARTITIONS = 256 + +; ============================================================================= +; ================================ Structures ================================= +; ============================================================================= +; 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 + 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 +; implements an old version, the caller can detect this by checking .strucsize, +; so the driver remains compatible. + close dd ? +; The pointer to the function which frees all driver-specific resources for +; the disk. +; Optional, may be NULL. +; void close(void* userdata); + closemedia dd ? +; The pointer to the function which informs the driver that the kernel has +; finished all processing with the current media. If media is removed, the +; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA, +; even if new media is inserted, until this function is called. If media is +; removed, a new call to 'disk_media_changed' is not allowed until this +; function is called. +; Optional, may be NULL (if media is not removable). +; void closemedia(void* userdata); + querymedia dd ? +; The pointer to the function which determines capabilities of the media. +; int querymedia(void* userdata, DISKMEDIAINFO* info); +; Return value: one of DISK_STATUS_* + read dd ? +; The pointer to the function which reads data from the device. +; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors); +; input: *numsectors = number of sectors to read +; output: *numsectors = number of sectors which were successfully read +; Return value: one of DISK_STATUS_* + write dd ? +; The pointer to the function which writes data to the device. +; Optional, may be NULL. +; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors); +; input: *numsectors = number of sectors to write +; output: *numsectors = number of sectors which were successfully written +; Return value: one of DISK_STATUS_* + flush dd ? +; The pointer to the function which flushes the internal device cache. +; Optional, may be NULL. +; int flush(void* userdata); +; Return value: one of DISK_STATUS_* +; Note that read/write are called by the cache manager, so a driver should not +; create a software cache. This function is implemented for flushing a hardware +; cache, if it exists. + adjust_cache_size dd ? +; The pointer to the function which returns the cache size for this device. +; Optional, may be NULL. +; unsigned int adjust_cache_size(unsigned int suggested_size); +; Return value: 0 = disable cache, otherwise = used cache size in bytes. +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 + Flags dd ? +; Combination of DISK_MEDIA_* bits. + SectorSize dd ? +; Size of the sector. + Capacity dq ? +; Size of the media in sectors. +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 + mutex MUTEX +; Lock to protect the cache. +; The following fields are inherited from data32.inc:cache_ideX. + pointer dd ? + data_size dd ? ; unused + data dd ? + sad_size dd ? + search_start dd ? +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 +; Fields of disk object + Next dd ? + Prev dd ? +; All disk devices are linked in one list with these two fields. +; Head of the list is the 'disk_list' variable. + Functions dd ? +; Pointer to the 'DISKFUNC' structure with driver functions. + Name dd ? +; Pointer to the string used for accesses through the global filesystem. + UserData dd ? +; This field is passed to all callback functions so a driver can decide which +; physical device is addressed. + DriverFlags dd ? +; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined. +; If it is set, the driver will never issue 'disk_media_changed' notification +; with argument set to true, so the kernel must try to detect media during +; requests from the file system. + RefCount dd ? +; Count of active references to this structure. One reference is kept during +; the lifetime of the structure between 'disk_add' and 'disk_del'. +; Another reference is taken during any filesystem operation for this disk. +; One reference is added if media is inserted. +; The structure is destroyed when the reference count decrements to zero: +; this usually occurs in 'disk_del', but can be delayed to the end of last +; filesystem operation, if one is active. + MediaLock MUTEX +; Lock to protect the MEDIA structure. See the description after +; 'disk_list_mutex' for the locking strategy. +; Fields of media object + MediaInserted db ? +; 0 if media is not inserted, nonzero otherwise. + MediaUsed db ? +; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is +; nonzero, this field is nonzero too; however, when .MediaRefCount goes +; to zero, there is some time interval during which media object is still used. + dw ? ; padding +; The following fields are not valid unless either .MediaInserted is nonzero +; or they are accessed from a code which has obtained the reference when +; .MediaInserted was nonzero. + MediaRefCount dd ? +; Count of active references to the media object. One reference is kept during +; the lifetime of the media between two calls to 'disk_media_changed'. +; Another reference is taken during any filesystem operation for this media. +; The callback 'closemedia' is called when the reference count decrements to +; zero: this usually occurs in 'disk_media_changed', but can be delayed to the +; end of the last filesystem operation, if one is active. + MediaInfo DISKMEDIAINFO +; This field keeps information on the current media. + NumPartitions dd ? +; Number of partitions on this media. + Partitions dd ? +; Pointer to array of .NumPartitions pointers to PARTITION structures. + cache_size dd ? +; inherited from cache_ideX_size + SysCache DISKCACHE + AppCache DISKCACHE +; Two caches for the disk. +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 + FirstSector dq ? +; First sector of the partition. + Length dq ? +; Length of the partition in sectors. + Disk dd ? +; Pointer to parent DISK structure. + FSUserFunctions dd ? +; Handlers for the sysfunction 70h. This field is a pointer to the following +; array. The first dword is pointer to disconnect handler. +; The first dword is a number of supported subfunctions, other dwords +; point to handlers of corresponding subfunctions. +; ...fs-specific data may follow... +ends + +; This is an external structure, it represents an entry in the partition table. +struct PARTITION_TABLE_ENTRY + Bootable db ? +; 80h = bootable partition, 0 = non-bootable partition, other values = invalid + FirstHead db ? + FirstSector db ? + FirstTrack db ? +; Coordinates of first sector in CHS. + Type db ? +; Partition type, one of predefined constants. 0 = empty, several types denote +; extended partition (see process_partition_table_entry), we are not interested +; in other values. + LastHead db ? + LastSector db ? + LastTrack db ? +; Coordinates of last sector in CHS. + FirstAbsSector dd ? +; Coordinate of first sector in LBA. + Length dd ? +; Length of the partition in sectors. +ends + +; ============================================================================= +; ================================ Global data ================================ +; ============================================================================= +iglobal +; The pseudo-item for the list of all DISK structures. +; Initialized to the empty list. +disk_list: + dd disk_list + dd disk_list +endg +uglobal +; This mutex guards all operations with the global list of DISK structures. +disk_list_mutex MUTEX +; * There are two dependent objects, a disk and a media. In the simplest case, +; disk and media are both non-removable. However, in the general case both +; can be removed at any time, simultaneously or only media,and this makes things +; complicated. +; * For efficiency, both disk and media objects are located in the one +; structure named DISK. However, logically they are different. +; * The following operations use data of disk object: adding (disk_add); +; deleting (disk_del); filesystem (fs_lfn which eventually calls +; dyndisk_handler or dyndisk_enum_root). +; * The following operations use data of media object: adding/removing +; (disk_media_changed); filesystem (fs_lfn which eventually calls +; dyndisk_handler; dyndisk_enum_root doesn't work with media). +; * Notifications disk_add, disk_media_changed, disk_del are synchronized +; between themselves, this is a requirement for the driver. However, file +; system operations are asynchronous, can be issued at any time by any +; thread. +; * We must prevent a situation when a filesystem operation thinks that the +; object is still valid but in fact the notification has destroyed the +; object. So we keep a reference counter for both disk and media and destroy +; the object when this counter goes to zero. +; * The driver must know when it is safe to free driver-allocated resources. +; The object can be alive even after death notification has completed. +; We use special callbacks to satisfy both assertions: 'close' for the disk +; and 'closemedia' for the media. The destruction of the object includes +; calling the corresponding callback. +; * Each filesystem operation keeps one reference for the disk and one +; reference for the media. Notification disk_del forces notification on the +; media death, so the reference counter for the disk is always not less than +; the reference counter for the media. +; * Two operations "get the object" and "increment the reference counter" can +; not be done simultaneously. We use a mutex to guard the consistency here. +; It must be a part of the container for the object, so that this mutex can +; be acquired as a part of getting the object from the container. The +; container for disk object is the global list, and this list is guarded by +; 'disk_list_mutex'. The container for media object is the disk object, and +; the corresponding mutex is DISK.MediaLock. +; * Notifications do not change the data of objects, they can only remove +; objects. Thus we don't need another synchronization at this level. If two +; filesystem operations are referencing the same filesystem data, this is +; better resolved at the level of the filesystem. +endg + +iglobal +; The function 'disk_scan_partitions' needs three 512-byte buffers for +; MBR, bootsector and fs-temporary sector data. It can not use the static +; buffers always, since it can be called for two or more disks in parallel. +; However, this case is not typical. We reserve three static 512-byte buffers +; and a flag that these buffers are currently used. If 'disk_scan_partitions' +; detects that the buffers are currently used, it allocates buffers from the +; heap. +; The flag is implemented as a global dword variable. When the static buffers +; are not used, the value is -1. When the static buffers are used, the value +; is normally 0 and temporarily can become greater. The function increments +; this value. If the resulting value is zero, it uses the buffers and +; 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 +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 +endg + +iglobal +; This is the array of default implementations of driver callbacks. +; Same as DRIVERFUNC structure except for the first field; all functions must +; 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 +endg + +; ============================================================================= +; ================================= Functions ================================= +; ============================================================================= + +; This function registers a disk device. +; This includes: +; - allocating an internal structure describing this device; +; - registering this structure in the global filesystem. +; The function initializes the disk as if there is no media. If a media is +; present, the function 'disk_media_changed' should be called after this +; function succeeds. +; Parameters: +; [esp+4] = pointer to DISKFUNC structure with the callbacks +; [esp+8] = pointer to name (ASCIIZ string) +; [esp+12] = userdata to be passed to the callbacks as is. +; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit +; is defined. +; Return value: +; NULL = operation has failed +; non-NULL = handle of the disk. This handle can be used +; 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 +; 1. Allocate the DISK structure. +; 1a. Call the heap manager. + movi eax, sizeof.DISK + call malloc +; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0. + 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 +@@: + inc eax + cmp byte [ebx+eax-1], 0 + jnz @b +; 2b. Call the heap manager. + call malloc +; 2c. Check the result. If allocation failed, go to 7. + 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 +; 2e. Copy the name. +@@: + 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 +; 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 + mov [esi+DISK.MediaRefCount], 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 +; 5b. Insert item to the tail of double-linked list. + mov edx, disk_list + list_add_tail esi, edx ;esi= new edx= list head +; 5c. Release the mutex. + call mutex_unlock +; 6. Return with eax = pointer to DISK. + 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 +; 8. Return with eax = 0. + xor eax, eax +.nothing: +; 9. Return. + 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: +; - removing a media including all partitions; +; - deleting this structure from the global filesystem; +; - dereferencing the DISK structure and possibly destroying it. +; Parameters: +; [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 +; 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 +; 2. Delete the structure from the global list. +; 2a. Acquire the mutex. + 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 +; 2c. Release the mutex. + 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 +; 4. Return. + 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 +; finalize all associated data, and afterwards frees the DISK structure. +; esi = pointer to DISK structure +disk_dereference: +; 1. Decrement reference counter. Use atomic operation to correctly handle +; possible simultaneous calls. + 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 +; 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 +; 3b. Free the structure. + xchg eax, esi + push ebx + call free + pop ebx +; 4. Return. +.nothing: + 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' +; callback to signal the driver that the processing has finished and it is safe +; to inform about a new media. +; esi = pointer to DISK structure +disk_media_dereference: +; 1. Decrement reference counter. Use atomic operation to correctly handle +; possible simultaneous calls. + 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 +; 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 +; objects, so we must guarantee that reusing does not happen while freeing. +; Reusing is only possible when someone processes a new media. There are two +; mutually exclusive variants: +; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit +; in DISK.DriverFlags is not set). In this case, we require from the driver +; that such notification (except for the first one) can occur only after a +; call to 'closemedia' callback. +; * driver does not issue media insert notifications. In this case, the kernel +; itself must sometimes check whether media is inserted. We have the flag +; DISK.MediaUsed, visible to the kernel. This flag signals to the other parts +; of kernel that the way is free. +; In the first case other parts of the kernel do not use DISK.MediaUsed, so it +; 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 +.freeloop: + lodsd + mov ecx, [eax+PARTITION.FSUserFunctions] + call dword [ecx] + dec edi + jnz .freeloop +.nofree: + pop edi esi +; 3b. Free the cache. + call disk_free_cache +; 3c. Call the driver. + mov al, DISKFUNC.closemedia + stdcall disk_call_driver +; 3d. Clear the flag. + mov [esi+DISK.MediaUsed], 0 +.nothing: + 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 +; immediately after 'disk_add' and once from 'disk_del'. +; Parameters: +; [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 +; 1. Remove the existing media, if it is present. + 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 +; We really need to remove the media. +; 1b. Acquire mutex. + lea ecx, [esi+DISK.MediaLock] + call mutex_lock +; 1c. Clear the flag. + mov [esi+DISK.MediaInserted], 0 +; 1d. Release mutex. + call mutex_unlock +; 1e. Remove the "lifetime" reference and possibly destroy the structure. + call disk_media_dereference +.noremove: +; 2. Test whether there is new media. + 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 +; media referencers) and note that calls to this function are synchronized +; (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 +; 3b. Check the result of the callback. Abort if it failed. + 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 +; 3d. Acquire the lifetime reference for the media object. + inc [esi+DISK.MediaRefCount] +; 3e. Scan for partitions. Ignore result; the list of partitions is valid even +; on errors. + call disk_scan_partitions +; 3f. Media is inserted and available for use. + 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 + +; This function is a thunk for all functions of a disk driver. +; It checks whether the referenced function is implemented in the driver. +; If so, this function jumps to the function in the driver. +; Otherwise, it jumps to the default implementation. +; al = offset of function in the DISKFUNC structure; +; esi = pointer to the DISK structure; +; 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 +; 1. Prepend the first argument to the stack. + 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 +; 3. Check that the required function is implemented. If not, go to 5. + mov ecx, [ecx+eax] + test ecx, ecx + jz .default +; 4. Jump to the required function. + jmp ecx +.default: +; 5. Driver does not implement the required function; use default implementation. + jmp dword [disk_default_callbacks+eax-4] + +; The default implementation of DISKFUNC.querymedia. +disk_default_querymedia: + movi eax, DISK_STATUS_INVALID_CALL + ret 8 + +; The default implementation of DISKFUNC.read and DISKFUNC.write. +disk_default_read: +disk_default_write: + movi eax, DISK_STATUS_INVALID_CALL + 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 + +; The default implementation of DISKFUNC.adjust_cache_size. +disk_default_adjust_cache_size: + mov eax, [esp+8] + ret 8 + +; This is an internal function called from 'disk_media_changed' when a new media +; is detected. It creates the list of partitions for the media. +; If media is not partitioned, then the list consists of one partition which +; covers all the media. +; 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 +; 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 +.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 + lock inc [partition_buffer_users] + 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 +.buffer_acquired: +; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no +; more than MAX_NUM_PARTITION times. +; 4. Prepare things for the loop. +; ebp will hold the sector number for current MBR/EBR. +; [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 +.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 +; 6. If the read has failed, abort the loop. + 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 +; 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 +; 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. +; The following algorithm is used to distinguish between these cases. +; A. If at least one entry of the partition table is invalid, this is +; a bootsector. See the description of 'is_partition_table_entry' for +; definition of validity. +; B. If all entries are empty (filesystem type field is zero) and the first +; byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to +; have zeros in the place of partition table. +; 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 +; 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 +; 9c. Empty partition table or bootsector with many zeroes? (rule B) + 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], esi + 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 +; 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 +; 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 +; 12c. If extended partition has not yet started, start it. + 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 +.mbr_failed: +.done: +; 13. Cleanup after the loop. + 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 +; 14b. If we have allocated it, free it. + xchg eax, ebx + call free + jmp .nothing +; 14c. Otherwise, release reference. +.release_partition_buffer: + lock dec [partition_buffer_users] +.nothing: +; 15. Return. + 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. +; The entry is valid if the first byte is 0 or 80h, the first sector plus the +; length is less than twice the size of media. Multiplication by two is +; required since the size mentioned in the partition table can be slightly +; 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 +; 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 +; 4. Divide by two. + 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 +.valid: +; 5. Return success: CF is cleared. + clc + ret +.invalid: +; 6. Return fail: CF is set. + stc + ret + +; This is an internal function called from disk_scan_partitions. It processes +; the entry pointed to by ecx. +; * If the entry is invalid, just ignore this entry. +; * If the type is zero, just ignore this entry. +; * If the type is one of types for extended partition, store the address +; of this partition as the new MBR in [esp+4]. +; * Otherwise, add the partition to the list of partitions for this disk. +; We don't use the type from the entry to identify the file system; +; 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 +; 2. Check for empty entry. If invalid, return (go to 5). + 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 +{ + 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, esi + pop ecx +.nothing: +; 5. Return. + 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 + +; 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. +; Important note: start, length, disk MUST be present and +; MUST be in the same order as in PARTITION structure. +; esi duplicates [disk]. +proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword, disk:dword +; 1. Check that this partition will not exceed the limit on total number. + 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. +; Since the number of partitions is usually very small and is guaranteed not +; 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 +.scan_existing: +; 2b. Get the next partition. + 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 +; 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 +.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 +.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 +.next_existing: +; 2g. The partition does not overlap with the current partition. Continue the +; loop. + 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 +; 3b. Test the result. If failed, return with nothing done. + 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 +; 3d. Set the field in the DISK structure to the new array. + xchg [esi+DISK.Partitions], eax +; 3e. Free the old array. + 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 +; 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 +; 5. Insert the new partition to the list. + stosd + inc [esi+DISK.NumPartitions] +; 6. Return. +.nothing: + ret +endp + +; This is an internal function called from disk_add_partition. +; It tries to recognize the file system on the partition and allocates the +; corresponding PARTITION structure with filesystem-specific fields. +disk_detect_partition: +; This function inherits the stack frame from disk_add_partition. In stdcall +; 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 ? +.disk dd ? +end virtual +; 1. Read the bootsector to the buffer. +; When disk_add_partition is called, ebx contains a pointer to +; a three-sectors-sized buffer. This function saves ebx in the stack +; immediately before ebp. + mov ebx, [ebp-4] ; get buffer + add ebx, 512 ; advance over MBR data to bootsector data + add ebp, 8 ; ebp points to part of PARTITION structure + xor eax, eax ; first sector of the partition + call fs_read32_sys + push eax +; 2. Run tests for all supported filesystems. If at least one test succeeded, +; go to 4. +; For tests: +; ebp -> first three fields of PARTITION structure, .start, .length, .disk; +; [esp] = error code after bootsector read: 0 = ok, otherwise = failed, +; ebx points to the buffer for bootsector, +; ebx+512 points to 512-bytes buffer that can be used for anything. + call fat_create_partition + test eax, eax + jnz .success + call ntfs_create_partition + test eax, eax + jnz .success + call ext2_create_partition + test eax, eax + jnz .success + call xfs_create_partition + test eax, eax + jnz .success +; 3. No file system has recognized the volume, so just allocate the PARTITION +; structure without extra fields. + movi eax, sizeof.PARTITION + call malloc + test eax, eax + jz .nothing + mov edx, dword [ebp+PARTITION.FirstSector] + mov dword [eax+PARTITION.FirstSector], edx + mov edx, dword [ebp+PARTITION.FirstSector+4] + mov dword [eax+PARTITION.FirstSector+4], edx + mov edx, dword [ebp+PARTITION.Length] + mov dword [eax+PARTITION.Length], edx + mov edx, dword [ebp+PARTITION.Length+4] + mov dword [eax+PARTITION.Length+4], edx + mov [eax+PARTITION.Disk], esi + mov [eax+PARTITION.FSUserFunctions], default_fs_functions +.success: +.nothing: + sub ebp, 8 ; restore ebp +; 4. Return with eax = pointer to PARTITION or NULL. + pop ecx + ret + +iglobal +align 4 +default_fs_functions: + dd free + dd 0 ; no user functions +endg + +; This function is called from file_system_lfn. +; This handler gets the control each time when fn 70 is called +; with unknown item of root subdirectory. +; in: esi -> name +; ebp = 0 or rest of name relative to esi +; out: if the handler processes path, it must not return in file_system_lfn, +; 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 +; 1. Acquire the mutex. + mov ecx, disk_list_mutex + call mutex_lock +; 2. Loop over the list of DISK structures. +; 2a. Initialize. + mov ebx, disk_list +.scan: +; 2b. Get the next item. + mov ebx, [ebx+DISK.Next] +; 2c. Check whether the list is done. If so, go to 3. + cmp ebx, disk_list + jz .notfound +; 2d. Compare names. If names match, go to 5. + 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 +; edi points to the disk name. + 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 +.wrongname: +; 2f. Names don't match. Continue the loop. + pop esi + jmp .scan +.notfound: +; The loop is done and no name matches. +; 3. Release the mutex. + call mutex_unlock +; 4. Return normally. + 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 +.eoin: + 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 + 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 +; 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] +@@: +; 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 +; 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 +; 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 +; 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 +@@: +; 11c. Let the procedure from fs_lfn.inc do the job. + 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 +; 12b. Store callback functions. + 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 +.access_denied: +; 13. Fail the operation with the appropriate code. + mov dword [esp+32], ERROR_ACCESS_DENIED +.cleanup: +; 14. Cleanup. + 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 +@@: + call disk_dereference +; 15. Return. + 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 + +; 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 +.nomore: + 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 +.nomore: + 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 ; edx = pointer to DISK, dword [esp] = NULL or edx +; If the driver does not support insert notifications and we are the only fs +; operation with this disk, ask the driver whether the media +; was inserted/removed/changed. Otherwise, assume that media status is valid. + test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION + jz .media_accurate + push ecx esi + mov esi, edx + cmp dword [esp+8], 0 + jz .test_no_media + cmp [esi+DISK.MediaRefCount], 2 + jnz .media_accurate_pop + lea edx, [esi+DISK.MediaInfo] + and [edx+DISKMEDIAINFO.Flags], 0 + mov al, DISKFUNC.querymedia + stdcall disk_call_driver, edx + test eax, eax + jz .media_accurate_pop + stdcall disk_media_dereference ; drop our reference so that disk_media_changed could close the media + stdcall disk_media_changed, esi, 0 + and dword [esp+8], 0 ; no media +.test_no_media: + stdcall disk_media_changed, esi, 1 ; issue fake notification + ; if querymedia() inside disk_media_changed returns error, the notification is ignored + cmp [esi+DISK.MediaInserted], 0 + jz .media_accurate_pop + lock inc [esi+DISK.MediaRefCount] + mov dword [esp+8], esi +.media_accurate_pop: + mov edx, esi + pop esi ecx +.media_accurate: + pop eax + test eax, eax + jz .nomedia +.main: + cmp ecx, [edx+DISK.NumPartitions] + jae .notfound + mov eax, [edx+DISK.Partitions] + mov eax, [eax+ecx*4] + mov edi, [eax+PARTITION.FSUserFunctions] + mov ecx, [ebx] + cmp [edi+4], ecx + jbe .unsupported + push edx + push ebp + mov ebp, eax + call dword [edi+8+ecx*4] + pop ebp + pop edx + mov dword [esp+32], eax + mov dword [esp+20], ebx +.cleanup: + 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 +.unsupported: + cmp edi, default_fs_functions + jz .nofs + mov dword [esp+32], ERROR_UNSUPPORTED_FS + jmp .cleanup +.nomedia: + test ecx, ecx + jnz .notfound + 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 +; and must return all items which can be handled by this. +; It is called several times, first time with eax=0 +; in: eax = 0 for first call, previously returned value for subsequent calls +; 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 +; 1. If this is the first call, acquire the mutex and initialize. + test eax, eax + jnz .notfirst + call mutex_lock + mov eax, disk_list +.notfirst: +; 2. Get next item. + mov eax, [eax+DISK.Next] +; 3. If there are no more items, go to 6. + cmp eax, disk_list + jz .last +; 4. Copy name from the DISK structure to edi. + push eax esi + mov esi, [eax+DISK.Name] +@@: + 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 +.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 diff --git a/kernel/branches/kolibri-process/blkdev/disk_cache.inc b/kernel/branches/kolibri-process/blkdev/disk_cache.inc new file mode 100644 index 000000000..1b862a0de --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/disk_cache.inc @@ -0,0 +1,588 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4133 $ + +; 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 +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_sys: +; Save ecx, set ecx to SysCache and let the common part do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.SysCache + jmp fs_read32_common + +; This function is intended to replace the old 'hd_read' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_app: +; Save ecx, set ecx to AppCache and let the common part do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.AppCache + +; This label is the common part of fs_read32_sys and fs_read32_app. +fs_read32_common: +; 1. Check that the required sector is inside the partition. If no, return +; DISK_STATUS_END_OF_MEDIA. + cmp dword [ebp+PARTITION.Length+4], 0 + jnz @f + cmp dword [ebp+PARTITION.Length], eax + ja @f + mov eax, DISK_STATUS_END_OF_MEDIA + pop ecx + ret +@@: +; 2. Get the absolute sector on the disk. + push edx esi + xor edx, edx + add eax, dword [ebp+PARTITION.FirstSector] + adc edx, dword [ebp+PARTITION.FirstSector+4] +; 3. If there is no cache for this disk, just pass the request to the driver. + cmp [ecx+DISKCACHE.pointer], 0 + jnz .scancache + push 1 + push esp ; numsectors + push edx ; startsector + push eax ; startsector + push ebx ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + pop esi edx + pop ecx + ret +.scancache: +; 4. Scan the cache. + push edi ecx ; scan cache + push edx eax +virtual at esp +.sector_lo dd ? +.sector_hi dd ? +.cache dd ? +end virtual +; The following code is inherited from hd_read. The differences are: +; all code is protected by the cache lock; instead of static calls +; to hd_read_dma/hd_read_pio/bd_read the dynamic call to DISKFUNC.read is used; +; sector is 64-bit, not 32-bit. + call mutex_lock + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov esi, [ecx+DISKCACHE.pointer] + mov ecx, [ecx+DISKCACHE.sad_size] + add esi, 12 + + mov edi, 1 + +.hdreadcache: + + cmp dword [esi+8], 0 ; empty + je .nohdcache + + cmp [esi], eax ; correct sector + jne .nohdcache + cmp [esi+4], edx ; correct sector + je .yeshdcache + +.nohdcache: + + add esi, 12 + inc edi + dec ecx + jnz .hdreadcache + + mov esi, [.cache] + call find_empty_slot64 ; ret in edi + test eax, eax + jnz .read_done + + push 1 + push esp + push edx + push [.sector_lo+12] + mov ecx, [.cache+16] + mov eax, edi + shl eax, 9 + add eax, [ecx+DISKCACHE.data] + push eax + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + dec ecx + jnz .read_done + + mov ecx, [.cache] + lea eax, [edi*3] + mov esi, [ecx+DISKCACHE.pointer] + lea esi, [eax*4+esi] + + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov [esi], eax ; sector number + mov [esi+4], edx ; sector number + mov dword [esi+8], 1; hd read - mark as same as in hd + +.yeshdcache: + + mov esi, edi + mov ecx, [.cache] + shl esi, 9 + add esi, [ecx+DISKCACHE.data] + + mov edi, ebx + mov ecx, 512/4 + rep movsd ; move data + xor eax, eax ; successful read +.read_done: + mov ecx, [.cache] + push eax + call mutex_unlock + pop eax + add esp, 12 + pop edi esi edx ecx + ret + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 0, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_sys: +; Save ecx, set ecx to SysCache and let the common part do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.SysCache + jmp fs_write32_common + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_app: +; Save ecx, set ecx to AppCache and let the common part do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.AppCache + +; This label is the common part of fs_read32_sys and fs_read32_app. +fs_write32_common: +; 1. Check that the required sector is inside the partition. If no, return +; DISK_STATUS_END_OF_MEDIA. + cmp dword [ebp+PARTITION.Length+4], 0 + jnz @f + cmp dword [ebp+PARTITION.Length], eax + ja @f + mov eax, DISK_STATUS_END_OF_MEDIA + pop ecx + ret +@@: + push edx esi +; 2. Get the absolute sector on the disk. + xor edx, edx + add eax, dword [ebp+PARTITION.FirstSector] + adc edx, dword [ebp+PARTITION.FirstSector+4] +; 3. If there is no cache for this disk, just pass request to the driver. + cmp [ecx+DISKCACHE.pointer], 0 + jnz .scancache + push 1 + push esp ; numsectors + push edx ; startsector + push eax ; startsector + push ebx ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver + pop ecx + pop esi edx + pop ecx + ret +.scancache: +; 4. Scan the cache. + push edi ecx ; scan cache + push edx eax +virtual at esp +.sector_lo dd ? +.sector_hi dd ? +.cache dd ? +end virtual +; The following code is inherited from hd_write. The differences are: +; all code is protected by the cache lock; +; sector is 64-bit, not 32-bit. + call mutex_lock + + ; check if the cache already has the sector and overwrite it + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov esi, [ecx+DISKCACHE.pointer] + mov ecx, [ecx+DISKCACHE.sad_size] + add esi, 12 + + mov edi, 1 + +.hdwritecache: + cmp dword [esi+8], 0 ; if cache slot is empty + je .not_in_cache_write + + cmp [esi], eax ; if the slot has the sector + jne .not_in_cache_write + cmp [esi+4], edx ; if the slot has the sector + je .yes_in_cache_write + +.not_in_cache_write: + + add esi, 12 + inc edi + dec ecx + jnz .hdwritecache + + ; sector not found in cache + ; write the block to a new location + + mov esi, [.cache] + call find_empty_slot64 ; ret in edi + test eax, eax + jne .hd_write_access_denied + + mov ecx, [.cache] + lea eax, [edi*3] + mov esi, [ecx+DISKCACHE.pointer] + lea esi, [eax*4+esi] + + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov [esi], eax ; sector number + mov [esi+4], edx ; sector number + +.yes_in_cache_write: + + mov dword [esi+8], 2 ; write - differs from hd + + shl edi, 9 + mov ecx, [.cache] + add edi, [ecx+DISKCACHE.data] + + mov esi, ebx + mov ecx, 512/4 + rep movsd ; move data + xor eax, eax ; success +.hd_write_access_denied: + mov ecx, [.cache] + push eax + call mutex_unlock + pop eax + add esp, 12 + pop edi esi edx ecx + ret + +; This internal function is called from fs_read32_* and fs_write32_*. It is the +; analogue of find_empty_slot for 64-bit sectors. +find_empty_slot64: +;----------------------------------------------------------- +; find empty or read slot, flush cache if next 12.5% is used by write +; output : edi = cache slot +;----------------------------------------------------------- +.search_again: + mov ecx, [esi+DISKCACHE.sad_size] + mov edi, [esi+DISKCACHE.search_start] + shr ecx, 3 +.search_for_empty: + inc edi + cmp edi, [esi+DISKCACHE.sad_size] + jbe .inside_cache + mov edi, 1 +.inside_cache: + lea eax, [edi*3] + shl eax, 2 + add eax, [esi+DISKCACHE.pointer] + cmp dword [eax+8], 2 + jb .found_slot ; it's empty or read + dec ecx + jnz .search_for_empty + stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all + test eax, eax + jne .found_slot_access_denied + jmp .search_again ; and start again +.found_slot: + mov [esi+DISKCACHE.search_start], edi + xor eax, eax ; success +.found_slot_access_denied: + ret + +; This function is intended to replace the old 'write_cache' function. +proc write_cache64 uses ecx edx esi edi, disk:dword +locals +cache_chain_started dd 0 +cache_chain_size dd ? +cache_chain_pos dd ? +cache_chain_ptr dd ? +endl +saved_esi_pos = 16+12 ; size of local variables + size of registers before esi +; If there is no cache for this disk, nothing to do. + cmp [esi+DISKCACHE.pointer], 0 + jz .flush +;----------------------------------------------------------- +; write all changed sectors to disk +;----------------------------------------------------------- + + ; write difference ( 2 ) from cache to DISK + mov ecx, [esi+DISKCACHE.sad_size] + mov esi, [esi+DISKCACHE.pointer] + add esi, 12 + mov edi, 1 +.write_cache_more: + cmp dword [esi+8], 2 ; if cache slot is not different + jne .write_chain + 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 + jnz .nonext + push eax edx + add eax, 1 + adc edx, 0 + cmp eax, [esi+12] + jnz @f + cmp edx, [esi+12+4] +@@: + pop edx 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 +.nonext: + call .flush_cache_chain + test eax, eax + jnz .nothing + mov [cache_chain_size], 1 + mov [cache_chain_ptr], esi + call .write_cache_sector + test eax, eax + jnz .nothing + jmp .continue +.write_chain: + call .flush_cache_chain + test eax, eax + jnz .nothing +.continue: + add esi, 12 + inc edi + dec ecx + jnz .write_cache_more + call .flush_cache_chain + test eax, eax + jnz .nothing +.flush: + mov esi, [disk] + mov al, DISKFUNC.flush + call disk_call_driver +.nothing: + ret + +.flush_cache_chain: + xor eax, eax + cmp [cache_chain_started], eax + jz @f + call .write_cache_chain + mov [cache_chain_started], 0 +@@: + retn + +.write_cache_sector: + mov [cache_chain_size], 1 + mov [cache_chain_pos], edi +.write_cache_chain: + pusha + mov edi, [cache_chain_pos] + mov ecx, [ebp-saved_esi_pos] + shl edi, 9 + add edi, [ecx+DISKCACHE.data] + mov ecx, [cache_chain_size] + push ecx + push esp ; numsectors + mov eax, [cache_chain_ptr] + pushd [eax+4] + pushd [eax] ; startsector + push edi ; buffer + mov esi, [ebp] + mov esi, [esi+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver + pop ecx + mov [esp+28], eax + popa + retn +endp + +; This internal function is called from disk_add to initialize the caching for +; a new DISK. +; The algorithm is inherited from getcache.inc: take 1/32 part of the available +; physical memory, round down to 8 pages, limit by 128K from below and by 1M +; from above. Reserve 1/8 part of the cache for system data and 7/8 for app +; data. +; After the size is calculated, but before the cache is allocated, the device +; driver can adjust the size. In particular, setting size to zero disables +; caching: there is no sense in a cache for a ramdisk. In fact, such action +; is most useful example of a non-trivial adjustment. +; esi = pointer to DISK structure +disk_init_cache: +; 1. Calculate the suggested cache size. +; 1a. Get the size of free physical memory in pages. + mov eax, [pg_data.pages_free] +; 1b. Use the value to calculate the size. + shl eax, 12 - 5 ; 1/32 of it in bytes + and eax, -8*4096 ; round down to the multiple of 8 pages +; 1c. Force lower and upper limits. + cmp eax, 1024*1024 + jb @f + mov eax, 1024*1024 +@@: + cmp eax, 128*1024 + ja @f + mov eax, 128*1024 +@@: +; 1d. Give a chance to the driver to adjust the size. + push eax + mov al, DISKFUNC.adjust_cache_size + call disk_call_driver +; Cache size calculated. + mov [esi+DISK.cache_size], eax + test eax, eax + jz .nocache +; 2. Allocate memory for the cache. +; 2a. Call the allocator. + stdcall kernel_alloc, eax + test eax, eax + jnz @f +; 2b. If it failed, say a message and return with eax = 0. + dbgstr 'no memory for disk cache' + jmp .nothing +@@: +; 3. Fill two DISKCACHE structures. + mov [esi+DISK.SysCache.pointer], eax + lea ecx, [esi+DISK.SysCache.mutex] + call mutex_init + lea ecx, [esi+DISK.AppCache.mutex] + call mutex_init +; The following code is inherited from getcache.inc. + mov edx, [esi+DISK.SysCache.pointer] + and [esi+DISK.SysCache.search_start], 0 + and [esi+DISK.AppCache.search_start], 0 + mov eax, [esi+DISK.cache_size] + shr eax, 3 + mov [esi+DISK.SysCache.data_size], eax + add edx, eax + imul eax, 7 + mov [esi+DISK.AppCache.data_size], eax + mov [esi+DISK.AppCache.pointer], edx + + mov eax, [esi+DISK.SysCache.data_size] + push ebx + call calculate_for_hd64 + pop ebx + add eax, [esi+DISK.SysCache.pointer] + mov [esi+DISK.SysCache.data], eax + mov [esi+DISK.SysCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.SysCache.pointer] + lea ecx, [(ecx+1)*3] + xor eax, eax + rep stosd + pop edi + + mov eax, [esi+DISK.AppCache.data_size] + push ebx + call calculate_for_hd64 + pop ebx + add eax, [esi+DISK.AppCache.pointer] + mov [esi+DISK.AppCache.data], eax + mov [esi+DISK.AppCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.AppCache.pointer] + lea ecx, [(ecx+1)*3] + xor eax, eax + rep stosd + pop edi + +; 4. Return with nonzero al. + mov al, 1 +; 5. Return. +.nothing: + ret +; No caching is required for this driver. Zero cache pointers and return with +; nonzero al. +.nocache: + mov [esi+DISK.SysCache.pointer], eax + mov [esi+DISK.AppCache.pointer], eax + mov al, 1 + ret + +calculate_for_hd64: + push eax + mov ebx, eax + shr eax, 9 + lea eax, [eax*3] + shl eax, 2 + sub ebx, eax + shr ebx, 9 + mov ecx, ebx + shl ebx, 9 + pop eax + sub eax, ebx + dec ecx + ret + + +; This internal function is called from disk_media_dereference to free the +; allocated cache, if there is one. +; esi = pointer to DISK structure +disk_free_cache: +; The algorithm is straightforward. + mov eax, [esi+DISK.SysCache.pointer] + test eax, eax + jz .nothing + stdcall kernel_free, eax +.nothing: + ret + +; This function flushes all modified data from both caches for the given DISK. +; esi = pointer to DISK +disk_sync: +; The algorithm is straightforward. + push esi + push esi ; for second write_cache64 + push esi ; for first write_cache64 + add esi, DISK.SysCache + call write_cache64 + add esi, DISK.AppCache - DISK.SysCache + call write_cache64 + pop esi + ret diff --git a/kernel/branches/kolibri-process/blkdev/fdc.inc b/kernel/branches/kolibri-process/blkdev/fdc.inc new file mode 100644 index 000000000..fe075a646 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/fdc.inc @@ -0,0 +1,68 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + + +uglobal + dmasize db 0x0 + dmamode db 0x0 +endg + +fdc_init: ;start with clean tracks. + mov edi, OS_BASE+0xD201 + mov al, 0 + mov ecx, 160 + rep stosb + ret + +save_image: + cmp [ramdisk_actual_size], FLOPPY_CAPACITY + jnz .fail + pusha + mov ecx, floppy_mutex + call mutex_lock + mov [flp_number], bl + call floppy_read_bootsector + cmp [FDC_Status], 0 + jne .unnecessary_save_image + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 1; Сектор + mov esi, RAMDISK + call SeekTrack +.save_image_1: + call take_data_from_application_1 + call WriteSectWithRetr +; call WriteSector + cmp [FDC_Status], 0 + jne .unnecessary_save_image + inc [FDD_Sector] + cmp [FDD_Sector], 19 + jne .save_image_1 + mov [FDD_Sector], 1 + inc [FDD_Head] + cmp [FDD_Head], 2 + jne .save_image_1 + mov [FDD_Head], 0 + inc [FDD_Track] + call SeekTrack + cmp [FDD_Track], 80 + jne .save_image_1 +.unnecessary_save_image: + cmp [FDC_Status], 0 + pushf + mov ecx, floppy_mutex + call mutex_unlock + popf + popa + jnz .fail + xor eax, eax + ret +.fail: + movi eax, 1 + ret diff --git a/kernel/branches/kolibri-process/blkdev/flp_drv.inc b/kernel/branches/kolibri-process/blkdev/flp_drv.inc new file mode 100644 index 000000000..aa1a3b8ce --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/flp_drv.inc @@ -0,0 +1,949 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + + +;********************************************************** +; Непосредственная работа с контроллером гибкого диска +;********************************************************** +; Автор исходного текста Кулаков Владимир Геннадьевич. +; Адаптация и доработка Mario79 + +;give_back_application_data: ; переслать приложению +; mov edi,[TASK_BASE] +; mov edi,[edi+TASKDATA.mem_start] +; add edi,ecx +give_back_application_data_1: + mov esi, FDD_BUFF;FDD_DataBuffer ;0x40000 + mov ecx, 128 + cld + rep movsd + ret + +;take_data_from_application: ; взять из приложени +; mov esi,[TASK_BASE] +; mov esi,[esi+TASKDATA.mem_start] +; add esi,ecx +take_data_from_application_1: + mov edi, FDD_BUFF;FDD_DataBuffer ;0x40000 + mov ecx, 128 + 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 ;сектор не найден + +; Максимальные значения координат сектора (заданные +; значения соответствуют параметрам стандартного +; трехдюймового гибкого диска объемом 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 ? +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 + mov al, 0 + out 0x0c, al; reset the flip-flop to a known state. + mov al, 6 ; mask channel 2 so we can reprogram it. + out 0x0a, al + mov al, [dmamode]; 0x46 -> Read from floppy - 0x4A Write to floppy + out 0x0b, al + mov al, 0 + out 0x0c, al; reset the flip-flop to a known state. + mov eax, 0xD000 + out 0x04, al; set the channel 2 starting address to 0 + shr eax, 8 + out 0x04, al + shr eax, 8 + out 0x81, al + mov al, 0 + out 0x0c, al; reset flip-flop + mov al, 0xff;set count (actual size -1) + out 0x5, al + mov al, 0x1;[dmasize] ;(0x1ff = 511 / 0x23ff =9215) + out 0x5, al + mov al, 2 + out 0xa, al + popad + ret + +;*********************************** +;* ЗАПИСАТЬ БАЙТ В ПОРТ ДАННЫХ FDC * +;* Параметры: * +;* AL - выводимый байт. * +;*********************************** +FDCDataOutput: +; DEBUGF 1,'K : FDCDataOutput(%x)',al +; pusha + push eax ecx edx + mov AH, AL ;запомнить байт в AH +; Сбросить переменную состояния контроллера + mov [FDC_Status], FDC_Normal +; Проверить готовность контроллера к приему данных + mov DX, 3F4h ;(порт состояния FDC) + mov ecx, 0x10000 ;установить счетчик тайм-аута +@@TestRS: + in AL, DX ;прочитать регистр RS + and AL, 0C0h ;выделить разряды 6 и 7 + cmp AL, 80h ;проверить разряды 6 и 7 + je @@OutByteToFDC + loop @@TestRS +; Ошибка тайм-аута +; DEBUGF 1,' timeout\n' + mov [FDC_Status], FDC_TimeOut + jmp @@End_5 +; Вывести байт в порт данных +@@OutByteToFDC: + inc DX + mov AL, AH + out DX, AL +; DEBUGF 1,' ok\n' +@@End_5: +; popa + pop edx ecx eax + ret + +;****************************************** +;* ПРОЧИТАТЬ БАЙТ ИЗ ПОРТА ДАННЫХ FDC * +;* Процедура не имеет входных параметров. * +;* Выходные данные: * +;* AL - считанный байт. * +;****************************************** +FDCDataInput: + push ECX + push DX +; Сбросить переменную состояния контроллера + mov [FDC_Status], FDC_Normal +; Проверить готовность контроллера к передаче данных + mov DX, 3F4h ;(порт состояния FDC) + mov ecx, 0x10000 ;установить счетчик тайм-аута +@@TestRS_1: + in AL, DX ;прочитать регистр RS + and AL, 0C0h ;выдлить разряды 6 и 7 + cmp AL, 0C0h ;проверить разряды 6 и 7 + je @@GetByteFromFDC + loop @@TestRS_1 +; Ошибка тайм-аута +; DEBUGF 1,'K : FDCDataInput: timeout\n' + mov [FDC_Status], FDC_TimeOut + jmp @@End_6 +; Ввести байт из порта данных +@@GetByteFromFDC: + inc DX + in AL, DX +; DEBUGF 1,'K : FDCDataInput: %x\n',al +@@End_6: + pop DX + pop ECX + ret + +;********************************************* +;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД * +;********************************************* +FDCInterrupt: +; dbgstr 'FDCInterrupt' +; Установить флаг прерывания + mov [FDD_IntFlag], 1 + mov al, 1 + ret + +;******************************************* +;* ОЖИДАНИЕ ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД * +;******************************************* +WaitFDCInterrupt: + pusha +; Сбросить байт состояния операции + mov [FDC_Status], FDC_Normal +; Обнулить счетчик тиков + mov eax, [timer_ticks] + mov [TickCounter], eax +; Ожидать установки флага прерывания НГМД +@@TestRS_2: + call change_task + cmp [FDD_IntFlag], 0 + jnz @@End_7 ;прерывание произошло + mov eax, [timer_ticks] + sub eax, [TickCounter] + cmp eax, 200;50 ;25 ;5 ;ожидать 5 тиков + jb @@TestRS_2 +; jl @@TestRS_2 +; Ошибка тайм-аута +; dbgstr 'WaitFDCInterrupt: timeout' + mov [FDC_Status], FDC_TimeOut +@@End_7: + popa + ret + +;********************************* +;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" * +;********************************* +FDDMotorON: +; dbgstr 'FDDMotorON' + pusha +; cmp [fdd_motor_status],1 +; je fdd_motor_on + mov al, [flp_number] + cmp [fdd_motor_status], al + je fdd_motor_on +; Произвести сброс контроллера НГМД + mov DX, 3F2h;порт управления двигателями + mov AL, 0 + out DX, AL +; Выбрать и включить мотор дисковода + cmp [flp_number], 1 + jne FDDMotorON_B +; call FDDMotorOFF_B + mov AL, 1Ch ; Floppy A + jmp FDDMotorON_1 +FDDMotorON_B: +; call FDDMotorOFF_A + mov AL, 2Dh ; Floppy B +FDDMotorON_1: + out DX, AL +; Обнулить счетчик тиков + mov eax, [timer_ticks] + mov [TickCounter], eax +; Ожидать 0,5 с +@@dT: + call change_task + mov eax, [timer_ticks] + sub eax, [TickCounter] + cmp eax, 50 ;10 + jb @@dT +; Read results of RESET command + push 4 +; DEBUGF 1,'K : floppy reset results:' +@@: + mov al, 8 + call FDCDataOutput + call FDCDataInput +; DEBUGF 1,' %x',al + call FDCDataInput +; DEBUGF 1,' %x',al + dec dword [esp] + jnz @b +; DEBUGF 1,'\n' + pop eax + cmp [flp_number], 1 + jne fdd_motor_on_B + mov [fdd_motor_status], 1 + jmp fdd_motor_on +fdd_motor_on_B: + mov [fdd_motor_status], 2 +fdd_motor_on: + call save_timer_fdd_motor + popa + ret + +;***************************************** +;* СОХРАНЕНИЕ УКАЗАТЕЛЯ ВРЕМЕНИ * +;***************************************** +save_timer_fdd_motor: + mov eax, [timer_ticks] + mov [timer_fdd_motor], eax + ret + +;***************************************** +;* ПРОВЕРКА ЗАДЕРЖКИ ВЫКЛЮЧЕНИЯ МОТОРА * +;***************************************** +proc check_fdd_motor_status_has_work? + 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 + je end_check_fdd_motor_status_1 + mov eax, [timer_ticks] + sub eax, [timer_fdd_motor] + cmp eax, 500 + jb end_check_fdd_motor_status + call FDDMotorOFF + mov [fdd_motor_status], 0 +end_check_fdd_motor_status_1: +end_check_fdd_motor_status: + ret + +;********************************** +;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА * +;********************************** +FDDMotorOFF: +; dbgstr 'FDDMotorOFF' + push AX + push DX + cmp [flp_number], 1 + jne FDDMotorOFF_1 + call FDDMotorOFF_A + jmp FDDMotorOFF_2 +FDDMotorOFF_1: + call FDDMotorOFF_B +FDDMotorOFF_2: + pop DX + pop AX + ; сброс флагов кеширования в связи с устареванием информации + or [floppy_media_flags+0], FLOPPY_MEDIA_NEED_RESCAN + or [floppy_media_flags+1], FLOPPY_MEDIA_NEED_RESCAN + ret + +FDDMotorOFF_A: + mov DX, 3F2h;порт управления двигателями + mov AL, 0Ch ; Floppy A + out DX, AL + ret + +FDDMotorOFF_B: + mov DX, 3F2h;порт управления двигателями + mov AL, 5h ; Floppy B + out DX, AL + ret + +;******************************* +;* РЕКАЛИБРОВКА ДИСКОВОДА "A:" * +;******************************* +RecalibrateFDD: +; dbgstr 'RecalibrateFDD' + pusha + call save_timer_fdd_motor +; Сбросить флаг прерывания + mov [FDD_IntFlag], 0 +; Подать команду "Рекалибровка" + mov AL, 07h + call FDCDataOutput + mov AL, 00h + call FDCDataOutput +; Ожидать завершения операции + call WaitFDCInterrupt + cmp [FDC_Status], 0 + jne .fail +; Read results of RECALIBRATE command +; DEBUGF 1,'K : floppy recalibrate results:' + mov al, 8 + call FDCDataOutput + call FDCDataInput +; DEBUGF 1,' %x',al + call FDCDataInput +; DEBUGF 1,' %x',al +; DEBUGF 1,'\n' +.fail: + call save_timer_fdd_motor + popa + ret + +;***************************************************** +;* ПОИСК ДОРОЖКИ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1). * +;* Результат операции заносится в FDC_Status. * +;***************************************************** +SeekTrack: +; dbgstr 'SeekTrack' + pusha + call save_timer_fdd_motor +; Сбросить флаг прерывания + mov [FDD_IntFlag], 0 +; Подать команду "Поиск" + 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 + ; Номер головки совпадает с заданным? +; The H bit (Head Address) in ST0 will always return a "0" (c) 82077AA datasheet, +; description of SEEK command. So we can not verify the proper head. +; mov AL, [FDC_ST0] +; and AL, 100b +; shr AL, 2 +; cmp AL, [FDD_Head] +; jne @@Err + ; Операция завершена успешно +; dbgstr 'SeekTrack: FDC_Normal' + mov [FDC_Status], FDC_Normal + jmp @@Exit +@@Err: ; Трек не найден +; dbgstr 'SeekTrack: FDC_TrackNotFound' + mov [FDC_Status], FDC_TrackNotFound +@@Exit: + call save_timer_fdd_motor + popa + ret + +;******************************************************* +;* ЧТЕНИЕ СЕКТОРА ДАННЫХ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции чтения * +;* содержимое сектора будет занесено в FDD_DataBuffer. * +;******************************************************* +ReadSector: +; dbgstr 'ReadSector' + pushad + call save_timer_fdd_motor +; Сбросить флаг прерывания + mov [FDD_IntFlag], 0 +; Установить скорость передачи 500 Кбайт/с + mov AX, 0 + mov DX, 03F7h + out DX, AL +; Инициализировать канал прямого доступа к памяти + mov [dmamode], 0x46 + call Init_FDC_DMA +; Подать команду "Чтение данных" + mov AL, 0E6h ;чтение в мультитрековом режиме + call FDCDataOutput + mov AL, [FDD_Head] + shl AL, 2 + call FDCDataOutput + mov AL, [FDD_Track] + call FDCDataOutput + mov AL, [FDD_Head] + call FDCDataOutput + mov AL, [FDD_Sector] + call FDCDataOutput + mov AL, 2 ;код размера сектора (512 байт) + call FDCDataOutput + mov AL, 18 ;+1; 3Fh ;число секторов на дорожке + call FDCDataOutput + mov AL, 1Bh ;значение GPL + call FDCDataOutput + 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 +; dbgstr 'ReadSector: FDC_Normal' + mov [FDC_Status], FDC_Normal + jmp @@Exit_1 +@@Err_1: +; dbgstr 'ReadSector: FDC_SectorNotFound' + mov [FDC_Status], FDC_SectorNotFound +@@Exit_1: + call save_timer_fdd_motor + popad + ret + +;******************************************************* +;* ЧТЕНИЕ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) * +;* Параметры передаются через глобальные переменные: * +;* 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 + cmp [FDC_Status], 0 + je @@Exit_2 + cmp [FDC_Status], 1 + je @@Err_3 + ; Троекратное повторение чтени + inc [ReadRepCounter] + cmp [ReadRepCounter], 3 + jb @@ReadSector_1 + ; Троекратное повторение рекалибровки + call RecalibrateFDD + call SeekTrack + inc [RecalRepCounter] + cmp [RecalRepCounter], 3 + jb @@TryAgain +@@Exit_2: + popa + ret +@@Err_3: + popa + ret + +;******************************************************* +;* ЗАПИСЬ СЕКТОРА ДАННЫХ * +;* Параметры передаются через глобальные переменные: * +;* FDD_Track - номер дорожки (0-79); * +;* FDD_Head - номер головки (0-1); * +;* FDD_Sector - номер сектора (1-18). * +;* Результат операции заносится в FDC_Status. * +;* В случае успешного выполнения операции записи * +;* содержимое FDD_DataBuffer будет занесено в сектор. * +;******************************************************* +WriteSector: +; dbgstr 'WriteSector' + pushad + call save_timer_fdd_motor +; Сбросить флаг прерывания + mov [FDD_IntFlag], 0 +; Установить скорость передачи 500 Кбайт/с + mov AX, 0 + mov DX, 03F7h + out DX, AL +; Инициализировать канал прямого доступа к памяти + mov [dmamode], 0x4A + call Init_FDC_DMA +; Подать команду "Запись данных" + mov AL, 0xC5 ;0x45 ;запись в мультитрековом режиме + call FDCDataOutput + mov AL, [FDD_Head] + shl AL, 2 + call FDCDataOutput + mov AL, [FDD_Track] + call FDCDataOutput + mov AL, [FDD_Head] + call FDCDataOutput + mov AL, [FDD_Sector] + call FDCDataOutput + mov AL, 2 ;код размера сектора (512 байт) + call FDCDataOutput + mov AL, 18; 3Fh ;число секторов на дорожке + call FDCDataOutput + mov AL, 1Bh ;значение GPL + call FDCDataOutput + 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 +@@Err_2: + mov [FDC_Status], FDC_SectorNotFound +@@Exit_3: + call save_timer_fdd_motor + popad + ret + +;******************************************************* +;* ЗАПИСЬ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) * +;* Параметры передаются через глобальные переменные: * +;* 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 + cmp [FDC_Status], 0 + je @@Exit_4 + cmp [FDC_Status], 1 + je @@Err_4 + ; Троекратное повторение чтени + inc [ReadRepCounter] + cmp [ReadRepCounter], 3 + jb @@WriteSector_1 + ; Троекратное повторение рекалибровки + call RecalibrateFDD + call SeekTrack + inc [RecalRepCounter] + cmp [RecalRepCounter], 3 + jb @@TryAgain_1 +@@Exit_4: + popa + ret +@@Err_4: + popa + ret + +;********************************************* +;* ПОЛУЧИТЬ ИНФОРМАЦИЮ О РЕЗУЛЬТАТЕ ОПЕРАЦИИ * +;********************************************* +GetStatusInfo: + push AX + call FDCDataInput + mov [FDC_ST0], AL + call FDCDataInput + mov [FDC_ST1], AL + call FDCDataInput + mov [FDC_ST2], AL + call FDCDataInput + mov [FDC_C], AL + call FDCDataInput + mov [FDC_H], AL + call FDCDataInput + mov [FDC_R], AL + call FDCDataInput + mov [FDC_N], AL + pop AX + ret + +; Interface for disk subsystem. +; Assume fixed capacity for 1.44M. +FLOPPY_CAPACITY = 2880 ; in sectors + +iglobal +align 4 +floppy_functions: + dd .size + dd 0 ; no close() function + dd 0 ; no closemedia() function + dd floppy_querymedia + dd floppy_read + dd floppy_write + dd 0 ; no flush() function + dd 0 ; no adjust_cache_size() function +.size = $ - floppy_functions +endg + +uglobal +floppy_media_flags rb 2 +n_sector dd 0 ; temporary save for sector value +flp_number db 0 ; 1- Floppy A, 2-Floppy B +old_track db 0 ; old value track +flp_label rb 15*2 ; Label and ID of inserted floppy disk +align 4 +; Hardware does not allow to work with two floppies in parallel, +; so there is one mutex guarding access to any floppy. +floppy_mutex MUTEX +endg +; Meaning of bits in floppy_media_flags +FLOPPY_MEDIA_PRESENT = 1 ; media was present when last asked +FLOPPY_MEDIA_NEED_RESCAN = 2 ; media was possibly changed, need to rescan +FLOPPY_MEDIA_LABEL_CHANGED = 4 ; temporary state + +iglobal +floppy1_name db 'fd',0 +floppy2_name db 'fd2',0 +endg + +; This function is called in boot process. +; It creates filesystems /fd and/or /fd2, if the system has one/two floppy drives. +proc floppy_init + mov ecx, floppy_mutex + call mutex_init +; First floppy is present if [DRIVE_DATA] and 0xF0 is nonzero. + test byte [DRIVE_DATA], 0xF0 + jz .no1 + stdcall disk_add, floppy_functions, floppy1_name, 1, DISK_NO_INSERT_NOTIFICATION +.no1: +; Second floppy is present if [DRIVE_DATA] and 0x0F is nonzero. + test byte [DRIVE_DATA], 0x0F + jz .no2 + stdcall disk_add, floppy_functions, floppy2_name, 2, DISK_NO_INSERT_NOTIFICATION +.no2: + ret +endp + +; Returns information about disk media. +; Floppy drives do not support insert notifications, +; DISK_NO_INSERT_NOTIFICATION is set, +; the disk subsystem calls this function before each filesystem operation. +; If the media has changed, return error for the first call as signal +; to finalize work with old media and the true geometry for the second call. +; Assume that media is (possibly) changed anytime when motor is off. +proc floppy_querymedia + virtual at esp+4 + .userdata dd ? + .info dd ? + end virtual +; 1. Acquire the global lock. + mov ecx, floppy_mutex + call mutex_lock + mov edx, [.userdata] ; 1 for /fd, 2 for /fd2 +; 2. If the media was reported and has been changed, forget it and report an error. + mov al, [floppy_media_flags+edx-1] + and al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN + cmp al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN + jnz .not_reported +.no_media: + mov [floppy_media_flags+edx-1], 0 +.return_no_media: + mov ecx, floppy_mutex + call mutex_unlock + mov eax, DISK_STATUS_NO_MEDIA + retn 8 +.not_reported: +; 3. If we are in the temporary state LABEL_CHANGED, this is the second call +; after intermediate DISK_STATUS_NO_MEDIA due to media change; +; clear the flag and return the current geometry without rereading the bootsector. + cmp [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED + jz .report_geometry +; 4. Try to read the bootsector. + mov [flp_number], dl + mov [FDC_Status], 0 + call floppy_read_bootsector +; 5. If reading bootsector failed, assume that media is not present. + mov edx, [.userdata] + cmp [FDC_Status], 0 + jnz .no_media +; 6. Check whether the previous status is "present". If not, go to 10. + push esi edi + imul edi, edx, 15 + add edi, flp_label-15 + mov esi, FDD_BUFF+39 + mov ecx, 15 + test [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT + jz .set_label +; 7. Compare the old label with the current one. + rep cmpsb +; 8. If the label has not changed, go to 11. + jz .ok +; 9. If the label has changed, store it, enter temporary state LABEL_CHANGED +; and report DISK_STATUS_NO_MEDIA. +; dbgstr 'floppy label changed' + add esi, ecx + add edi, ecx + mov ecx, 15 + sub esi, ecx + sub edi, ecx + rep movsb + mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED + pop edi esi + jmp .return_no_media +.set_label: +; 10. The previous state was "not present". Copy the label. + rep movsb +.ok: + pop edi esi +.report_geometry: +; 11. Fill DISKMEDIAINFO structure. + mov ecx, [.info] + and [ecx+DISKMEDIAINFO.Flags], 0 + mov [ecx+DISKMEDIAINFO.SectorSize], 512 + mov dword [ecx+DISKMEDIAINFO.Capacity], FLOPPY_CAPACITY + and dword [ecx+DISKMEDIAINFO.Capacity+4], 0 +; 12. Update state: media is present, data are actual. + mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT +; 13. Release the global lock and return successful status. + mov ecx, floppy_mutex + call mutex_unlock + xor eax, eax + retn 8 +endp + +proc floppy_read_bootsector + pushad + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 1; Сектор + call FDDMotorON + call RecalibrateFDD + cmp [FDC_Status], 0 + jne .nothing + call SeekTrack + cmp [FDC_Status], 0 + jne .nothing + call ReadSectWithRetr +.nothing: + popad + ret +endp + +read_chs_sector: + call calculate_chs + call ReadSectWithRetr + ret + +save_chs_sector: + call calculate_chs + call WriteSectWithRetr + ret + +calculate_chs: + mov bl, [FDD_Track] + mov [old_track], bl + mov ebx, 18 + xor edx, edx + div ebx + inc edx + mov [FDD_Sector], dl + mov edx, eax + shr eax, 1 + and edx, 1 + mov [FDD_Track], al + mov [FDD_Head], dl + mov dl, [old_track] + cmp dl, [FDD_Track] + je no_seek_track_1 + call SeekTrack +no_seek_track_1: + ret + +; Writes one or more sectors to the device. +proc floppy_write + mov dl, 1 + jmp floppy_read_write +endp + +; Reads one or more sectors from the device. +proc floppy_read + mov dl, 0 +endp + +; Common part of floppy_read and floppy_write. +proc floppy_read_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword +virtual at ebp-8 +.sectors_todo dd ? +.operation db ? +end virtual + push edx ; save operation code to [.operation] +; 1. Get number of sectors to read/write +; and zero number of sectors that were actually read/written. + mov eax, [numsectors_ptr] + push dword [eax] ; initialize [.sectors_todo] + and dword [eax], 0 + push ebx esi edi ; save used registers to be stdcall +; 2. Acquire the global lock. + mov ecx, floppy_mutex + call mutex_lock +; 3. Set floppy number for this operation. + mov edx, [userdata] + mov [flp_number], dl +; 4. Read/write sector-by-sector. +.operation_loop: +; 4a. Check that the sector is inside the media. + cmp dword [start_sector+4], 0 + jnz .end_of_media + mov eax, dword [start_sector] + cmp eax, FLOPPY_CAPACITY + jae .end_of_media +; 4b. For read operation, call read_chs_sector and then move data from FDD_BUFF to [buffer]. +; For write operation, move data from [buffer] to FDD_BUFF and then call save_chs_sector. + cmp [.operation], 0 + jz .read + mov esi, [buffer] + mov edi, FDD_BUFF + mov ecx, 512/4 + rep movsd + mov [buffer], esi + call save_chs_sector + jmp @f +.read: + call read_chs_sector + mov esi, FDD_BUFF + mov edi, [buffer] + mov ecx, 512/4 + rep movsd + mov [buffer], edi +@@: +; 4c. If there was an error, propagate it to the caller. + cmp [FDC_Status], 0 + jnz .fail +; 4d. Otherwise, increment number of sectors processed and continue the loop. + mov eax, [numsectors_ptr] + inc dword [eax] + inc dword [start_sector] + dec [.sectors_todo] + jnz .operation_loop +; 5. Release the global lock and return with the correct status. + push 0 +.return: + mov ecx, floppy_mutex + call mutex_unlock + pop eax + pop edi esi ebx ; restore used registers to be stdcall + ret ; this translates to leave/retn N and purges local variables +.fail: + push -1 + jmp .return +.end_of_media: + push DISK_STATUS_END_OF_MEDIA + jmp .return +endp diff --git a/kernel/branches/kolibri-process/blkdev/hd_drv.inc b/kernel/branches/kolibri-process/blkdev/hd_drv.inc new file mode 100644 index 000000000..5d767735b --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/hd_drv.inc @@ -0,0 +1,1185 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4420 $ + + +; Low-level driver for HDD access +; DMA support by Mario79 +; LBA48 support by Mario79 +;----------------------------------------------------------------------------- +struct HD_DATA +hdbase dd ? +hdid dd ? +hdpos dd ? +ends + +iglobal +align 4 +ide_callbacks: + dd ide_callbacks.end - ide_callbacks ; strucsize + dd 0 ; no close function + dd 0 ; no closemedia function + dd ide_querymedia + dd ide_read + dd ide_write + dd 0 ; no flush function + dd 0 ; use default cache size +.end: + +hd0_data HD_DATA ?, 0, 1 +hd1_data HD_DATA ?, 0x10, 2 +hd2_data HD_DATA ?, 0, 3 +hd3_data HD_DATA ?, 0x10, 4 + +hd_address_table: + dd 0x1f0, 0x00, 0x1f0, 0x10 + dd 0x170, 0x00, 0x170, 0x10 +endg + +uglobal +ide_mutex MUTEX +ide_channel1_mutex MUTEX +ide_channel2_mutex MUTEX +endg + +proc ide_read stdcall uses edi, \ + hd_data, buffer, startsector:qword, numsectors + ; hd_data = pointer to hd*_data + ; buffer = pointer to buffer for data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really read +locals +sectors_todo dd ? +channel_lock dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock + mov ecx, ide_channel2_mutex + mov eax, [hd_data] + push ecx + mov ecx, [hd_address_table] + cmp [eax+HD_DATA.hdbase], ecx ; 0x1F0 + pop ecx + jne .IDE_Channel_2 + mov ecx, ide_channel1_mutex +.IDE_Channel_2: + mov [channel_lock], ecx + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and edi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov ecx, [hd_data] + mov eax, [ecx+HD_DATA.hdbase] + mov [hdbase], eax + mov eax, [ecx+HD_DATA.hdid] + mov [hdid], eax + mov eax, [ecx+HD_DATA.hdpos] + mov [hdpos], eax + mov eax, dword [startsector] + mov edi, [buffer] +; 4. Worker procedures take one sectors per time, so loop over all sectors to read. +.sectors_loop: +; DMA read is permitted if [allow_dma_access]=1 or 2 + cmp [allow_dma_access], 2 + ja .nodma + cmp [dma_hdd], 1 + jnz .nodma +;-------------------------------------- + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jnz @f + + test [DRIVE_DATA+1], byte 10100000b + jnz .nodma + + jmp .dma +@@: + test [DRIVE_DATA+1], byte 1010b + jnz .nodma +.dma: +;-------------------------------------- + call hd_read_dma + jmp @f +.nodma: + call hd_read_pio +@@: + cmp [hd_error], 0 + jnz .fail + mov ecx, [numsectors] + inc dword [ecx] ; one more sector is read + dec [sectors_todo] + jz .done + inc eax + jnz .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, [channel_lock] + call mutex_unlock + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, [channel_lock] + call mutex_unlock + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +proc ide_write stdcall uses esi edi, \ + hd_data, buffer, startsector:qword, numsectors + ; hd_data = pointer to hd*_data + ; buffer = pointer to buffer with data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really written +locals +sectors_todo dd ? +channel_lock dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock + mov ecx, ide_channel2_mutex + mov eax, [hd_data] + push ecx + mov ecx, [hd_address_table] + cmp [eax+HD_DATA.hdbase], ecx ; 0x1F0 + pop ecx + jne .IDE_Channel_2 + mov ecx, ide_channel1_mutex +.IDE_Channel_2: + mov [channel_lock], ecx + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and esi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov ecx, [hd_data] + mov eax, [ecx+HD_DATA.hdbase] + mov [hdbase], eax + mov eax, [ecx+HD_DATA.hdid] + mov [hdid], eax + mov eax, [ecx+HD_DATA.hdpos] + mov [hdpos], eax + mov esi, [buffer] + lea edi, [startsector] + mov [cache_chain_ptr], edi +; 4. Worker procedures take max 16 sectors per time, +; loop until all sectors will be processed. +.sectors_loop: + mov ecx, 16 + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [cache_chain_size], cl +; DMA write is permitted only if [allow_dma_access]=1 + cmp [allow_dma_access], 2 + jae .nodma + cmp [dma_hdd], 1 + jnz .nodma +;-------------------------------------- + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jnz @f + + test [DRIVE_DATA+1], byte 10100000b + jnz .nodma + + jmp .dma +@@: + test [DRIVE_DATA+1], byte 1010b + jnz .nodma +.dma: +;-------------------------------------- + call cache_write_dma + jmp .common +.nodma: + mov [cache_chain_size], 1 + call cache_write_pio +.common: + cmp [hd_error], 0 + jnz .fail + movzx ecx, [cache_chain_size] + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .done + add [edi], ecx + jc .fail + shl ecx, 9 + add esi, ecx + jmp .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, [channel_lock] + call mutex_unlock + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, [channel_lock] + call mutex_unlock + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp + +; This is a stub. +proc ide_querymedia stdcall, hd_data, mediainfo + mov eax, [mediainfo] + mov [eax+DISKMEDIAINFO.Flags], 0 + mov [eax+DISKMEDIAINFO.SectorSize], 512 + or dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF + or dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF + xor eax, eax + ret +endp + +;----------------------------------------------------------------------------- +align 4 +; input: eax = sector, edi -> buffer +; output: edi = edi + 512 +hd_read_pio: + push eax edx + +; Select the desired drive + mov edx, [hdbase] + add edx, 6 ;адрес регистра головок + mov al, byte [hdid] + add al, 128+64+32 + out dx, al; номер головки/номер диска + + call wait_for_hd_idle + cmp [hd_error], 0 + jne hd_read_error + +; ATA with 28 or 48 bit for sector number? + mov eax, [esp+4] + cmp eax, 0x10000000 + jae .lba48 +;-------------------------------------- +.lba28: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; ATA Features регистр "особенностей" + inc edx + inc eax + out dx, al ; ATA Sector Counter счётчик секторов + inc edx + mov eax, [esp+4+4] + out dx, al ; LBA Low LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High LBA (23:16) + shr eax, 8 + inc edx + and al, 1+2+4+8 ; LBA (27:24) + add al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 20h ; READ SECTOR(S) + out dx, al ; ATACommand регистр команд + popfd + jmp .continue +;-------------------------------------- +.lba48: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; Features Previous Reserved + out dx, al ; Features Current Reserved + inc edx + out dx, al ; Sector Count Previous Sector count (15:8) + inc eax + out dx, al ; Sector Count Current Sector count (7:0) + inc edx + mov eax, [esp+4+4] + rol eax, 8 + out dx, al ; LBA Low Previous LBA (31:24) + xor eax, eax ; because only 32 bit cache + inc edx + out dx, al ; LBA Mid Previous LBA (39:32) + inc edx + out dx, al ; LBA High Previous LBA (47:40) + sub edx, 2 + mov eax, [esp+4+4] + out dx, al ; LBA Low Current LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid Current LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High Current LBA (23:16) + inc edx + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 24h ; READ SECTOR(S) EXT + out dx, al ; ATACommand регистр команд + popfd +;-------------------------------------- +.continue: + call wait_for_sector_buffer + + cmp [hd_error], 0 + jne hd_read_error + + pushfd + cli + + mov ecx, 256 + mov edx, [hdbase] + cld + rep insw + popfd + + pop edx eax + ret +;----------------------------------------------------------------------------- +align 4 +; edi -> sector, esi -> data +cache_write_pio: +; Select the desired drive + mov edx, [hdbase] + add edx, 6 ;адрес регистра головок + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + + call wait_for_hd_idle + cmp [hd_error], 0 + jne hd_write_error + +; ATA with 28 or 48 bit for sector number? + mov eax, [edi] + cmp eax, 0x10000000 + jae .lba48 +;-------------------------------------- +.lba28: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; ATA Features регистр "особенностей" + inc edx + inc eax + out dx, al ; ATA Sector Counter счётчик секторов + inc edx + mov eax, [edi] ; eax = sector to write + out dx, al ; LBA Low LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High LBA (23:16) + shr eax, 8 + inc edx + and al, 1+2+4+8 ; LBA (27:24) + add al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 30h ; WRITE SECTOR(S) + out dx, al ; ATACommand регистр команд + jmp .continue +;-------------------------------------- +.lba48: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; Features Previous Reserved + out dx, al ; Features Current Reserved + inc edx + out dx, al ; Sector Count Previous Sector count (15:8) + inc eax + out dx, al ; Sector Count Current Sector count (7:0) + inc edx + mov eax, [edi] + rol eax, 8 + out dx, al ; LBA Low Previous LBA (31:24) + xor eax, eax ; because only 32 bit cache + inc edx + out dx, al ; LBA Mid Previous LBA (39:32) + inc edx + out dx, al ; LBA High Previous LBA (47:40) + sub edx, 2 + mov eax, [edi] + out dx, al ; LBA Low Current LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid Current LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High Current LBA (23:16) + inc edx + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 34h ; WRITE SECTOR(S) EXT + out dx, al ; ATACommand регистр команд +;-------------------------------------- +.continue: + popfd + call wait_for_sector_buffer + + cmp [hd_error], 0 + jne hd_write_error + + push ecx esi + + pushfd + cli + mov ecx, 256 + mov edx, [hdbase] + cld + rep outsw + popfd + + pop esi ecx + ret +;----------------------------------------------------------------------------- +align 4 +save_hd_wait_timeout: + push eax + mov eax, [timer_ticks] + add eax, 300 ; 3 sec timeout + mov [hd_wait_timeout], eax + pop eax + ret +;----------------------------------------------------------------------------- +align 4 +check_hd_wait_timeout: + push eax + mov eax, [hd_wait_timeout] + cmp [timer_ticks], eax + jg hd_timeout_error + + pop eax + mov [hd_error], 0 + ret +;----------------------------------------------------------------------------- +hd_timeout_error: + if lang eq sp + DEBUGF 1,"K : FS - HD tiempo de espera agotado\n" + else + DEBUGF 1,"K : FS - HD timeout\n" + end if + mov [hd_error], 1 + pop eax + ret +;----------------------------------------------------------------------------- +hd_read_error: + if lang eq sp + DEBUGF 1,"K : FS - HD error de lectura\n" + else + DEBUGF 1,"K : FS - HD read error\n" + end if + pop edx eax + ret +;----------------------------------------------------------------------------- +hd_write_error_dma: + pop esi +hd_write_error: + if lang eq sp + DEBUGF 1,"K : FS - HD error de escritura\n" + else + DEBUGF 1,"K : FS - HD write error\n" + end if + ret +;----------------------------------------------------------------------------- +align 4 +wait_for_hd_idle: + push eax edx + + call save_hd_wait_timeout + + mov edx, [hdbase] + add edx, 0x7 +;-------------------------------------- +align 4 +wfhil1: + call check_hd_wait_timeout + cmp [hd_error], 0 + jne @f + + in al, dx + test al, 128 + jnz wfhil1 + +@@: + pop edx eax + ret +;----------------------------------------------------------------------------- +align 4 +wait_for_sector_buffer: + push eax edx + + mov edx, [hdbase] + add edx, 0x7 + + call save_hd_wait_timeout +;-------------------------------------- +align 4 +hdwait_sbuf: ; wait for sector buffer to be ready + call check_hd_wait_timeout + cmp [hd_error], 0 + jne @f + + in al, dx + test al, 8 + jz hdwait_sbuf + + mov [hd_error], 0 + + cmp [hd_setup], 1 ; do not mark error for setup request + je buf_wait_ok + + test al, 1 ; previous command ended up with an error + jz buf_wait_ok +@@: + mov [hd_error], 1 + +buf_wait_ok: + pop edx eax + ret +;----------------------------------------------------------------------------- +irq14_num equ byte 14 +irq15_num equ byte 15 +;----------------------------------------------------------------------------- +align 4 +wait_for_sector_dma_ide0: + push eax + push edx + call save_hd_wait_timeout +;-------------------------------------- +align 4 +.wait: + call change_task + cmp [IDE_common_irq_param], 0 + jz .done + + call check_hd_wait_timeout + cmp [hd_error], 0 + jz .wait +; clear Bus Master IDE Command register + pushfd + cli + mov [IDE_common_irq_param], 0 + mov dx, [IDEContrRegsBaseAddr] + mov al, 0 + out dx, al + popfd +;-------------------------------------- +align 4 +.done: + pop edx + pop eax + ret +;----------------------------------------------------------------------------- +align 4 +wait_for_sector_dma_ide1: + push eax + push edx + call save_hd_wait_timeout +;-------------------------------------- +align 4 +.wait: + call change_task + cmp [IDE_common_irq_param], 0 + jz .done + + call check_hd_wait_timeout + cmp [hd_error], 0 + jz .wait +; clear Bus Master IDE Command register + pushfd + cli + mov [IDE_common_irq_param], 0 + mov dx, [IDEContrRegsBaseAddr] + add dx, 8 + mov al, 0 + out dx, al + popfd +;-------------------------------------- +align 4 +.done: + pop edx + pop eax + ret +;----------------------------------------------------------------------------- +iglobal +align 4 +; note that IDE descriptor table must be 4-byte aligned and do not cross 4K boundary +IDE_descriptor_table: + dd IDE_DMA + dw 0x2000 + dw 0x8000 + +dma_cur_sector dd not 40h +dma_hdpos dd 0 +IDE_common_irq_param db 0 +endg +;----------------------------------------------------------------------------- +uglobal +; all uglobals are zeroed at boot +dma_process dd 0 +dma_slot_ptr dd 0 +cache_chain_pos dd 0 +cache_chain_ptr dd 0 +cache_chain_size db 0 +cache_chain_started db 0 +dma_task_switched db 0 +dma_hdd db 0 +allow_dma_access db 0 +endg +;----------------------------------------------------------------------------- +align 4 +IDE_irq_14_handler: + cmp [IDE_common_irq_param], irq14_num + jne .exit + + pushfd + cli + pushad + mov [IDE_common_irq_param], 0 + mov dx, [IDEContrRegsBaseAddr] +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +; clear Bus Master IDE Command register + sub edx, 2 + xor eax, eax + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: + popad + popfd +;-------------------------------------- +align 4 +.exit: + mov al, 0 + ret +;----------------------------------------------------------------------------- +align 4 +IDE_irq_15_handler: + cmp [IDE_common_irq_param], irq15_num + jne .exit + + pushfd + cli + pushad + mov [IDE_common_irq_param], 0 + mov dx, [IDEContrRegsBaseAddr] + add dx, 8 +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +; clear Bus Master IDE Command register + sub edx, 2 + mov al, 0 + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: + popad + popfd +;-------------------------------------- +align 4 +.exit: + mov al, 0 + ret +;----------------------------------------------------------------------------- +align 4 +IDE_common_irq_handler: + cmp [IDE_common_irq_param], 0 + je .exit + + pushfd + cli + pushad + xor ebx, ebx + mov dx, [IDEContrRegsBaseAddr] + mov eax, IDE_common_irq_param + cmp [eax], irq14_num + mov [eax], bl + je @f + + add dx, 8 +;-------------------------------------- +align 4 +@@: +; test whether it is our interrupt? + add edx, 2 + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +; clear Bus Master IDE Command register + sub edx, 2 + xor eax, eax + out dx, al +; read status register and remove the interrupt request + mov edx, [hdbase] + add edx, 0x7 + in al, dx + popad + popfd + mov al, 1 + ret +;-------------------------------------- +align 4 +@@: + popad + popfd +;-------------------------------------- +align 4 +.exit: + mov al, 0 + ret +;----------------------------------------------------------------------------- +align 4 +hd_read_dma: + push eax + push edx + mov edx, [dma_hdpos] + cmp edx, [hdpos] + jne .notread + mov edx, [dma_cur_sector] + cmp eax, edx + jb .notread + add edx, 15 + cmp [esp+4], edx + ja .notread + mov eax, [esp+4] + sub eax, [dma_cur_sector] + shl eax, 9 + add eax, (OS_BASE+IDE_DMA) + push ecx esi + mov esi, eax + + mov ecx, 512/4 + cld + rep movsd + pop esi ecx + pop edx + pop eax + ret +.notread: +; set data for PRD Table + mov eax, IDE_descriptor_table + mov dword [eax], IDE_DMA + mov word [eax+4], 0x2000 + sub eax, OS_BASE +; select controller Primary or Secondary + mov dx, [IDEContrRegsBaseAddr] + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jz @f + add edx, 8 +@@: + push edx +; Bus Master IDE PRD Table Address + add edx, 4 +; save IDE_descriptor_table + out dx, eax + pop edx +; clear Bus Master IDE Command register + mov al, 0 + out dx, al +; clear Bus Master IDE Status register +; clear Error bit and Interrupt bit + add edx, 2 + mov al, 6 ; 110b + out dx, al +; Select the desired drive + mov edx, [hdbase] + add edx, 6 ; адрес регистра головок + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + + call wait_for_hd_idle + cmp [hd_error], 0 + jnz hd_read_error + +; ATA with 28 or 48 bit for sector number? + mov eax, [esp+4] +; -10h because the PreCache hits the boundary between lba28 and lba48 +; 10h = 16 - size of PreCache + cmp eax, 0x10000000-10h + jae .lba48 +;-------------------------------------- +.lba28: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; ATA Features регистр "особенностей" + inc edx + mov eax, 10h ; Sector Counter = 16 ; PreCache + out dx, al ; ATA Sector Counter счётчик секторов + inc edx + mov eax, [esp+4+4] + out dx, al ; LBA Low LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High LBA (23:16) + shr eax, 8 + inc edx + and al, 0xF ; LBA (27:24) + add al, byte [hdid] + add al, 11100000b + out dx, al ; номер головки/номер диска + inc edx + mov al, 0xC8 ; READ DMA + out dx, al ; ATACommand регистр команд + jmp .continue +;-------------------------------------- +.lba48: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; Features Previous Reserved + out dx, al ; Features Current Reserved + inc edx + out dx, al ; Sector Count Previous Sector count (15:8) + mov eax, 10h ; Sector Counter = 16 PreCache + out dx, al ; Sector Count Current Sector count (7:0) + inc edx + mov eax, [esp+4+4] + rol eax, 8 + out dx, al ; LBA Low Previous LBA (31:24) + xor eax, eax ; because only 32 bit cache + inc edx + out dx, al ; LBA Mid Previous LBA (39:32) + inc edx + out dx, al ; LBA High Previous LBA (47:40) + sub edx, 2 + mov eax, [esp+4+4] + out dx, al ; LBA Low Current LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid Current LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High Current LBA (23:16) + inc edx + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 25h ; READ DMA EXT + out dx, al ; ATACommand регистр команд +;-------------------------------------- +.continue: +; select controller Primary or Secondary + mov dx, [IDEContrRegsBaseAddr] + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jz @f + add dx, 8 +@@: +; set write to memory and Start Bus Master + mov al, 9 + out dx, al + + mov eax, [CURRENT_TASK] + mov [dma_process], eax + + mov eax, [TASK_BASE] + mov [dma_slot_ptr], eax + + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jnz .ide1 + + mov [IDE_common_irq_param], irq14_num + jmp @f +.ide1: + mov [IDE_common_irq_param], irq15_num +@@: + popfd +; wait for interrupt + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jnz .wait_ide1 + call wait_for_sector_dma_ide0 + jmp @f +.wait_ide1: + call wait_for_sector_dma_ide1 +@@: + cmp [hd_error], 0 + jnz hd_read_error + mov eax, [hdpos] + mov [dma_hdpos], eax + pop edx + pop eax + mov [dma_cur_sector], eax + jmp hd_read_dma +;----------------------------------------------------------------------------- +cache_write_dma: + mov eax, [cache_chain_ptr] ; for what? + push esi +; set data for PRD Table + mov eax, IDE_descriptor_table + mov edx, eax + pusha + mov edi, (OS_BASE+IDE_DMA) + mov dword [edx], IDE_DMA + movzx ecx, [cache_chain_size] + shl ecx, 9 + mov word [edx+4], cx + shr ecx, 2 + cld + rep movsd + popa + sub eax, OS_BASE +; select controller Primary or Secondary + mov dx, [IDEContrRegsBaseAddr] + push eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + pop eax + jz @f + add edx, 8 +@@: + push edx +; Bus Master IDE PRD Table Address + add edx, 4 +; save IDE_descriptor_table + out dx, eax + pop edx +; clear Bus Master IDE Command register + mov al, 0 + out dx, al +; clear Bus Master IDE Status register +; clear Error bit and Interrupt bit + add edx, 2 + mov al, 6 + out dx, al +; Select the desired drive + mov edx, [hdbase] + add edx, 6 ; адрес регистра головок + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + + call wait_for_hd_idle + cmp [hd_error], 0 + jnz hd_write_error_dma + +; ATA with 28 or 48 bit for sector number? + mov esi, [cache_chain_ptr] + mov eax, [esi] +; -40h because the PreCache hits the boundary between lba28 and lba48 +; 40h = 64 - the maximum number of sectors to be written for one command + cmp eax, 0x10000000-40h + jae .lba48 +;-------------------------------------- +.lba28: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; ATA Features регистр "особенностей" + inc edx + mov al, [cache_chain_size] ; Sector Counter + out dx, al ; ATA Sector Counter счётчик секторов + inc edx + mov eax, [esi] + out dx, al ; LBA Low LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High LBA (23:16) + shr eax, 8 + inc edx + and al, 0xF ; LBA (27:24) + add al, byte [hdid] + add al, 11100000b + out dx, al ; номер головки/номер диска + inc edx + mov al, 0xCA ; WRITE DMA + out dx, al ; ATACommand регистр команд + jmp .continue +;-------------------------------------- +.lba48: + pushfd + cli + xor eax, eax + mov edx, [hdbase] + inc edx + out dx, al ; Features Previous Reserved + out dx, al ; Features Current Reserved + inc edx + out dx, al ; Sector Count Previous Sector count (15:8) + mov al, [cache_chain_size] ; Sector Counter + out dx, al ; Sector Count Current Sector count (7:0) + inc edx + mov eax, [esi] + rol eax, 8 + out dx, al ; LBA Low Previous LBA (31:24) + xor eax, eax ; because only 32 bit cache + inc edx + out dx, al ; LBA Mid Previous LBA (39:32) + inc edx + out dx, al ; LBA High Previous LBA (47:40) + sub edx, 2 + mov eax, [esi] + out dx, al ; LBA Low Current LBA (7:0) + shr eax, 8 + inc edx + out dx, al ; LBA Mid Current LBA (15:8) + shr eax, 8 + inc edx + out dx, al ; LBA High Current LBA (23:16) + inc edx + mov al, byte [hdid] + add al, 128+64+32 + out dx, al ; номер головки/номер диска + inc edx + mov al, 35h ; WRITE DMA EXT + out dx, al ; ATACommand регистр команд +;-------------------------------------- +.continue: +; select controller Primary or Secondary + mov dx, [IDEContrRegsBaseAddr] + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jz @f + add dx, 8 +@@: +; set write to device and Start Bus Master + mov al, 1 + out dx, al + mov eax, [CURRENT_TASK] + mov [dma_process], eax + mov eax, [TASK_BASE] + mov [dma_slot_ptr], eax + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jnz .ide1 + + mov [IDE_common_irq_param], irq14_num + jmp @f +.ide1: + mov [IDE_common_irq_param], irq15_num +@@: + popfd +; wait for interrupt + mov [dma_cur_sector], not 0x40 + mov eax, [hd_address_table] + cmp [hdbase], eax ; 0x1F0 + jnz .wait_ide1 + call wait_for_sector_dma_ide0 + jmp @f +.wait_ide1: + call wait_for_sector_dma_ide1 +@@: + cmp [hd_error], 0 + jnz hd_write_error_dma + pop esi + ret +;----------------------------------------------------------------------------- +uglobal +align 4 +IDE_Interrupt dw ? +IDEContrRegsBaseAddr dw ? +IDEContrProgrammingInterface dw ? +IDE_BAR0_val dw ? +IDE_BAR1_val dw ? +IDE_BAR2_val dw ? +IDE_BAR3_val dw ? +endg +;----------------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-process/blkdev/ide_cache.inc b/kernel/branches/kolibri-process/blkdev/ide_cache.inc new file mode 100644 index 000000000..45e01ba8f --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/ide_cache.inc @@ -0,0 +1,367 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;************************************************************************** +; +; [cache_ide[X]_pointer] +; or [cache_ide[X]_data_pointer] first entry in cache list +; +; +0 - lba sector +; +4 - state of cache sector +; 0 = empty +; 1 = used for read ( same as in hd ) +; 2 = used for write ( differs from hd ) +; +; [cache_ide[X]_system_data] +; or [cache_ide[x]_appl_data] - cache entries +; +;************************************************************************** + +$Revision: 3742 $ + +align 4 +find_empty_slot_CD_cache: +;----------------------------------------------------------- +; find empty or read slot, flush cache if next 10% is used by write +; output : edi = cache slot +;----------------------------------------------------------- +.search_again: + call cd_calculate_cache_3 +.search_for_empty: + inc edi + call cd_calculate_cache_4 + jbe .inside_cache + mov edi, 1 +.inside_cache: + call cd_calculate_cache_5 + ret +;-------------------------------------------------------------------- +clear_CD_cache: + pusha +.ide0: + xor eax, eax + cmp [cdpos], 1 + jne .ide1 + mov [cache_ide0_search_start], eax + mov ecx, [cache_ide0_system_sad_size] + mov edi, [cache_ide0_pointer] + call .clear + mov [cache_ide0_appl_search_start], eax + mov ecx, [cache_ide0_appl_sad_size] + mov edi, [cache_ide0_data_pointer] + jmp .continue +.ide1: + cmp [cdpos], 2 + jne .ide2 + mov [cache_ide1_search_start], eax + mov ecx, [cache_ide1_system_sad_size] + mov edi, [cache_ide1_pointer] + call .clear + mov [cache_ide1_appl_search_start], eax + mov ecx, [cache_ide1_appl_sad_size] + mov edi, [cache_ide1_data_pointer] + jmp .continue +.ide2: + cmp [cdpos], 3 + jne .ide3 + mov [cache_ide2_search_start], eax + mov ecx, [cache_ide2_system_sad_size] + mov edi, [cache_ide2_pointer] + call .clear + mov [cache_ide2_appl_search_start], eax + mov ecx, [cache_ide2_appl_sad_size] + mov edi, [cache_ide2_data_pointer] + jmp .continue +.ide3: + mov [cache_ide3_search_start], eax + mov ecx, [cache_ide3_system_sad_size] + mov edi, [cache_ide3_pointer] + 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 +.clear: + shl ecx, 1 + cld + rep stosd + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache: +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + mov ecx, [cache_ide0_system_sad_size] + mov esi, [cache_ide0_pointer] + ret +.ide0_appl_data: + mov ecx, [cache_ide0_appl_sad_size] + mov esi, [cache_ide0_data_pointer] + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + mov ecx, [cache_ide1_system_sad_size] + mov esi, [cache_ide1_pointer] + ret +.ide1_appl_data: + mov ecx, [cache_ide1_appl_sad_size] + mov esi, [cache_ide1_data_pointer] + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + mov ecx, [cache_ide2_system_sad_size] + mov esi, [cache_ide2_pointer] + ret +.ide2_appl_data: + mov ecx, [cache_ide2_appl_sad_size] + mov esi, [cache_ide2_data_pointer] + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + mov ecx, [cache_ide3_system_sad_size] + mov esi, [cache_ide3_pointer] + ret +.ide3_appl_data: + mov ecx, [cache_ide3_appl_sad_size] + mov esi, [cache_ide3_data_pointer] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_1: +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + mov esi, [cache_ide0_pointer] + ret +.ide0_appl_data: + mov esi, [cache_ide0_data_pointer] + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + mov esi, [cache_ide1_pointer] + ret +.ide1_appl_data: + mov esi, [cache_ide1_data_pointer] + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + mov esi, [cache_ide2_pointer] + ret +.ide2_appl_data: + mov esi, [cache_ide2_data_pointer] + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + mov esi, [cache_ide3_pointer] + ret +.ide3_appl_data: + mov esi, [cache_ide3_data_pointer] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_2: +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + mov eax, [cache_ide0_system_data] + ret +.ide0_appl_data: + mov eax, [cache_ide0_appl_data] + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + mov eax, [cache_ide1_system_data] + ret +.ide1_appl_data: + mov eax, [cache_ide1_appl_data] + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + mov eax, [cache_ide2_system_data] + ret +.ide2_appl_data: + mov eax, [cache_ide2_appl_data] + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + mov eax, [cache_ide3_system_data] + ret +.ide3_appl_data: + mov eax, [cache_ide3_appl_data] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_3: +; mov ecx,cache_max*10/100 +; mov edi,[cache_search_start] + +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + mov edi, [cache_ide0_search_start] + ret +.ide0_appl_data: + mov edi, [cache_ide0_appl_search_start] + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + mov edi, [cache_ide1_search_start] + ret +.ide1_appl_data: + mov edi, [cache_ide1_appl_search_start] + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + mov edi, [cache_ide2_search_start] + ret +.ide2_appl_data: + mov edi, [cache_ide2_appl_search_start] + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + mov edi, [cache_ide3_search_start] + ret +.ide3_appl_data: + mov edi, [cache_ide3_appl_search_start] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_4: +; cmp edi,cache_max +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + cmp edi, [cache_ide0_system_sad_size] + ret +.ide0_appl_data: + cmp edi, [cache_ide0_appl_sad_size] + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + cmp edi, [cache_ide1_system_sad_size] + ret +.ide1_appl_data: + cmp edi, [cache_ide1_appl_sad_size] + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + cmp edi, [cache_ide2_system_sad_size] + ret +.ide2_appl_data: + cmp edi, [cache_ide2_appl_sad_size] + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + cmp edi, [cache_ide3_system_sad_size] + ret +.ide3_appl_data: + cmp edi, [cache_ide3_appl_sad_size] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_5: +; mov [cache_search_start],edi +; 1 - IDE0 ... 4 - IDE3 +.ide0: + cmp [cdpos], 1 + jne .ide1 + cmp [cd_appl_data], 0 + jne .ide0_appl_data + mov [cache_ide0_search_start], edi + ret +.ide0_appl_data: + mov [cache_ide0_appl_search_start], edi + ret +.ide1: + cmp [cdpos], 2 + jne .ide2 + cmp [cd_appl_data], 0 + jne .ide1_appl_data + mov [cache_ide1_search_start], edi + ret +.ide1_appl_data: + mov [cache_ide1_appl_search_start], edi + ret +.ide2: + cmp [cdpos], 3 + jne .ide3 + cmp [cd_appl_data], 0 + jne .ide2_appl_data + mov [cache_ide2_search_start], edi + ret +.ide2_appl_data: + mov [cache_ide2_appl_search_start], edi + ret +.ide3: + cmp [cd_appl_data], 0 + jne .ide3_appl_data + mov [cache_ide3_search_start], edi + ret +.ide3_appl_data: + mov [cache_ide3_appl_search_start], edi + ret +;-------------------------------------------------------------------- +;align 4 +;calculate_linear_to_real: +; shr eax, 12 +; mov eax, [page_tabs+eax*4] +; and eax, 0xFFFFF000 +; ret diff --git a/kernel/branches/kolibri-process/blkdev/rd.inc b/kernel/branches/kolibri-process/blkdev/rd.inc new file mode 100644 index 000000000..5029ceaa6 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/rd.inc @@ -0,0 +1,195 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; RAMDISK functions ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + +iglobal +align 4 +ramdisk_functions: + dd .size + dd 0 ; no close() function + dd 0 ; no closemedia() function + dd ramdisk_querymedia + dd ramdisk_read + dd ramdisk_write + dd 0 ; no flush() function + dd ramdisk_adjust_cache_size +.size = $ - ramdisk_functions +endg + +; See memmap.inc. +; Currently size of memory allocated for the ramdisk is fixed. +; This should be revisited when/if memory map would become more dynamic. +RAMDISK_CAPACITY = 2880 ; in sectors + +iglobal +align 4 +ramdisk_actual_size dd RAMDISK_CAPACITY +endg + +; This function is called early in boot process. +; It creates filesystem /rd/1 based on raw image data loaded by somebody before +; to memory named as RAMDISK with max size RAMDISK_CAPACITY, may be less. +proc ramdisk_init +iglobal +ramdisk_name db 'rd',0 +endg + push ebx esi ; save used registers to be stdcall +; 1. Register the device and the (always inserted) media in the disk subsystem. + stdcall disk_add, ramdisk_functions, ramdisk_name, 0, 0 + test eax, eax + jz .fail + mov ebx, eax + stdcall disk_media_changed, eax, 1 +; 2. We don't know actual size of loaded image, +; so try to calculate it using partition structure, +; assuming that file systems fill the real size based on contents of the partition. +; 2a. Prepare for loop over partitions. + xor ecx, ecx + xor edx, edx +; 2b. Check that at least one partition was recognized. + cmp [ebx+DISK.NumPartitions], ecx + jz .fail +; 2c. Loop over partitions. +.partitions: +; For every partition, set edx to maximum between edx and end of partition. + mov esi, [ebx+DISK.Partitions] + mov esi, [esi+ecx*4] + mov eax, dword [esi+PARTITION.FirstSector] + add eax, dword [esi+PARTITION.Length] + cmp eax, edx + jb @f + mov edx, eax +@@: + inc ecx + cmp ecx, [ebx+DISK.NumPartitions] + jb .partitions +; 3. Reclaim unused memory, if any. + mov [ramdisk_actual_size], edx + add edx, 7 ; aligning up + shr edx, 3 ; 512-byte sectors -> 4096-byte pages + mov esi, RAMDISK_CAPACITY / 8 ; aligning down + sub esi, edx + jbe .no_reclaim + shl edx, 12 + add edx, RAMDISK - OS_BASE +@@: + mov eax, edx + call free_page + add edx, 0x1000 + dec esi + jnz @b +.no_reclaim: + pop esi ebx ; restore used registers to be stdcall + ret +.fail: + dbgstr 'Failed to initialize ramdisk' + pop esi ebx ; restore used registers to be stdcall + ret +endp + +; Returns information about disk media. +proc ramdisk_querymedia + virtual at esp+4 + .userdata dd ? + .info dd ? + end virtual +; Media is always present, sector size is always 512 bytes. + mov edx, [.userdata] + mov ecx, [.info] + mov [ecx+DISKMEDIAINFO.Flags], 0 + mov [ecx+DISKMEDIAINFO.SectorSize], 512 + mov eax, [ramdisk_actual_size] + mov dword [ecx+DISKMEDIAINFO.Capacity], eax + mov dword [ecx+DISKMEDIAINFO.Capacity+4], 0 +; Return zero as an indicator of success. + xor eax, eax + retn 8 +endp + +; Common procedure for reading and writing. +; operation = 0 for reading, operation = 1 for writing. +; Arguments of ramdisk_read and ramdisk_write are the same. +macro ramdisk_read_write operation +{ + push esi edi ; save used registers to be stdcall + mov esi, [userdata] + mov edi, [numsectors_ptr] +; 1. Determine number of sectors to be transferred. +; This is either the requested number of sectors or number of sectors +; up to the disk boundary, depending of what is less. + xor ecx, ecx +; 1a. Test whether [start_sector] is less than RAMDISK_CAPACITY. +; If so, calculate number of sectors between [start_sector] and RAMDISK_CAPACITY. +; Otherwise, the actual number of sectors is zero. + cmp dword [start_sector+4], ecx + jnz .got_number + mov eax, [ramdisk_actual_size] + sub eax, dword [start_sector] + jbe .got_number +; 1b. Get the requested number of sectors. + mov ecx, [edi] +; 1c. If it is greater than number of sectors calculated in 1a, use the value +; from 1a. + cmp ecx, eax + jb .got_number + mov ecx, eax +.got_number: +; 2. Compare the actual number of sectors with requested. If they are +; equal, set eax (it will be the returned value) to zero. Otherwise, +; use DISK_STATUS_END_OF_MEDIA. + xor eax, eax + cmp ecx, [edi] + jz @f + mov al, DISK_STATUS_END_OF_MEDIA +@@: +; 3. Store the actual number of sectors. + mov [edi], ecx +; 4. Calculate source and destination addresses. +if operation = 0 ; reading? + mov esi, dword [start_sector] + shl esi, 9 + add esi, RAMDISK + mov edi, [buffer] +else ; writing? + mov edi, dword [start_sector] + shl edi, 9 + add edi, RAMDISK + mov esi, [buffer] +end if +; 5. Calculate number of dwords to be transferred. + shl ecx, 9-2 +; 6. Copy data. + rep movsd +; 7. Return. The value in eax was calculated in step 2. + pop edi esi ; restore used registers to be stdcall +} + +; Reads one or more sectors from the device. +proc ramdisk_read userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword + ramdisk_read_write 0 + ret +endp + +; Writes one or more sectors to the device. +proc ramdisk_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword + ramdisk_read_write 1 + ret +endp + +; The kernel calls this function when initializing cache subsystem for +; the media. This call allows the driver to adjust the cache size. +proc ramdisk_adjust_cache_size + virtual at esp+4 + .userdata dd ? + .suggested_size dd ? + end virtual +; Since ramdisk does not need cache, just return 0. + xor eax, eax + retn 8 +endp diff --git a/kernel/branches/kolibri-process/blkdev/rdsave.inc b/kernel/branches/kolibri-process/blkdev/rdsave.inc new file mode 100644 index 000000000..50ff92cb4 --- /dev/null +++ b/kernel/branches/kolibri-process/blkdev/rdsave.inc @@ -0,0 +1,33 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + + +iglobal +saverd_fileinfo: + dd 2 ; subfunction: write + dd 0 ; (reserved) + dd 0 ; (reserved) +.size: + dd 0 + dd RAMDISK + db 0 +.name: + dd ? +endg +sysfn_saveramdisk: ; 18.6 = SAVE FLOPPY IMAGE (HD version only) + mov ebx, saverd_fileinfo + mov [ebx+21], ecx + mov eax, [ramdisk_actual_size] + shl eax, 9 + mov [ebx+12], eax + pushad + call file_system_lfn_protected ;in ebx + popad + mov [esp+32], eax + ret diff --git a/kernel/branches/kolibri-process/boot/ETFONT.FNT b/kernel/branches/kolibri-process/boot/ETFONT.FNT new file mode 100644 index 000000000..114f0609f Binary files /dev/null and b/kernel/branches/kolibri-process/boot/ETFONT.FNT differ diff --git a/kernel/branches/kolibri-process/boot/bootcode.inc b/kernel/branches/kolibri-process/boot/bootcode.inc new file mode 100644 index 000000000..b569590fc --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootcode.inc @@ -0,0 +1,1520 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; BOOTCODE.INC ;; +;; ;; +;; KolibriOS 16-bit loader, ;; +;; based on bootcode for MenuetOS ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4291 $ + + +;========================================================================== +; +; 16 BIT FUNCTIONS +; +;========================================================================== + + +putchar: +; in: al=character + mov ah, 0Eh + mov bh, 0 + int 10h + ret + +print: +; in: si->string + mov al, 186 + call putchar + mov al, ' ' + call putchar + +printplain: +; in: si->string + pusha + lodsb +@@: + call putchar + lodsb + test al, al + jnz @b + popa + ret + +getkey: ; Use BIOS INT 16h to read a key from the keyboard +; get number in range [bl,bh] (bl,bh in ['0'..'9']) +; in: bx=range +; out: ax=digit (1..9, 10 for 0) + mov ah, 0 ; If 'int 16h' is called with 'ah' equal to zero, the BIOS will not return control to the caller + int 16h ; until a key is available in the system type ahead buffer. On return, 'al' contains the ASCII + cmp al, 27 ; code for the key read from the buffer and 'ah' contains the keyboard scan code. (27=>ESC) + jz @f ; If ESC is pressed, return (user doesn't want to change any value). + cmp al, bl ; Compare 'al' (ASCII code of key pressed) with 'bl' (lowest accepted char from the range). + jb getkey ; ASCII code is below lowest accepted value => continue waiting for another key. + cmp al, bh ; Compare 'al' (ASCII code of key pressed) with 'bh' (highest accepted char from the range). + ja getkey ; ASCII code is above highest accepted value => continue waiting for another key. + push ax ; If the pressed key is in the accepted range, save it on the stack and echo to screen. + call putchar + pop ax + and ax, 0Fh ; Convert ASCII code to number: '1'->1, '2'->2, etc. 0Fh=1111b. + jnz @f ; ASCII code for '0' is 48 (110000b). (110000b AND 1111b) = 0 + mov al, 10 ; So if key '0' was entered, return 10 in 'ax' +@@: + ret + +setcursor: +; in: dl=column, dh=row + mov ah, 2 + mov bh, 0 + int 10h + ret + +macro _setcursor row,column +{ + mov dx, row*256 + column + call setcursor +} + +macro _ask_question question,range,variable_to_set +{ + _setcursor 16,0 + mov si, question ; Print the question + call print + mov bx, range ; range accepted for answer + call getkey + cmp al, 27 ; If ESC was pressed, do not change the value + jz .esc_pressed + mov [variable_to_set], al +} + +boot_read_floppy: + push si + xor si, si + mov ah, 2 ; read +@@: + push ax + int 0x13 + pop ax + jnc @f + inc si + cmp si, 10 + jb @b +sayerr_badsect: + mov si, badsect +sayerr_plain: + call printplain + jmp $ +@@: + pop si + ret + +; convert abs. sector number (AX) to BIOS T:H:S +; sector number = (abs.sector%BPB_SecPerTrk)+1 +; pre.track number = (abs.sector/BPB_SecPerTrk) +; head number = pre.track number%BPB_NumHeads +; track number = pre.track number/BPB_NumHeads +; Return: cl - sector number +; ch - track number +; dl - drive number (0 = a:) +; dh - head number +conv_abs_to_THS: + push bx + mov bx, word [BPB_SecPerTrk] + xor dx, dx + div bx + inc dx + mov cl, dl ; cl = sector number + mov bx, word [BPB_NumHeads] + xor dx, dx + div bx + ; !!!!!!! ax = track number, dx = head number + mov ch, al ; ch=track number + xchg dh, dl ; dh=head number + mov dl, 0 ; dl=0 (drive 0 (a:)) + pop bx + retn +; needed variables +BPB_SecPerTrk dw 0 ; sectors per track +BPB_NumHeads dw 0 ; number of heads +BPB_FATSz16 dw 0 ; size of FAT +BPB_RootEntCnt dw 0 ; count of root dir. entries +BPB_BytsPerSec dw 0 ; bytes per sector +BPB_RsvdSecCnt dw 0 ; number of reserved sectors +BPB_TotSec16 dw 0 ; count of the sectors on the volume +BPB_SecPerClus db 0 ; number of sectors per cluster +BPB_NumFATs db 0 ; number of FAT tables +abs_sector_adj dw 0 ; adjustment to make abs. sector number +end_of_FAT dw 0 ; end of FAT table +FirstDataSector dw 0 ; begin of data + +;========================================================================= +; +; 16 BIT CODE +; +;========================================================================= + +include 'bootvesa.inc' ;Include source for boot vesa +if defined extended_primary_loader +include 'parsers.inc' +end if + +start_of_code: + +if defined extended_primary_loader +; save data from primary loader + mov word [cs:bootcallback], si + mov word [cs:bootcallback+2], ds + push cs + pop ds + mov [bootdevice], ax + mov [bootfs], bx + +; set up stack + mov ax, 3000h + mov ss, ax + mov sp, 0EC00h + +; try to load configuration file + mov ax, 1 + mov di, config_file_struct + call [bootcallback] + cld + push cs + pop es +; bx=0 - ok, bx=1 - part of file loaded, assume this is ok + cmp bx, 1 + ja .config_bad +; configuration file was loaded, parse +; if length is too big, use first 0FFFFh bytes + test dx, dx + jz @f + mov ax, 0FFFFh +@@: +; ds:si will be pointer to current data, dx = limit + xchg ax, dx + push 4000h + pop ds + xor si, si +.parse_loop: +; skip spaces + cmp si, dx + jae .parse_done + lodsb + cmp al, ' ' + jbe .parse_loop + dec si +; loop over all possible configuration values + mov bx, config_file_variables +.find_variant: +; get length + mov cx, [es:bx] +; zero length = end of list + jecxz .find_newline +; skip over length + inc bx + inc bx + mov di, bx +; skip over string + add bx, cx +; test whether we have at least cx symbols left + mov ax, cx + add ax, si + jc .next_variant1 + cmp ax, dx + jae .next_variant1 +; save current position + push si +; compare strings + repz cmpsb + jnz .next_variant2 +; strings are equal; look for "=" with possible spaces before and after +@@: + cmp si, dx + jae .next_variant2 + lodsb + cmp al, ' ' + jbe @b + cmp al, '=' + jnz .next_variant2 +; ok, we found the true variant +; ignore saved position on the stack + pop ax +; call the parser + call word [es:bx] +; line parsed, find next +.find_newline: + cmp si, dx + jae .parse_done + lodsb + cmp al, 13 + jz .parse_loop + cmp al, 10 + jz .parse_loop + jmp .find_newline +.next_variant2: +; continue to the next variant, restoring current position + pop si +.next_variant1: +; continue to the next variant +; skip over the parser + inc bx + inc bx + jmp .find_variant +.parse_done: +.config_bad: + +; set up segment registers + push cs + pop ds +else + cld +; \begin{diamond}[02.12.2005] +; if bootloader sets ax = 'KL', then ds:si points to loader block + cmp ax, 'KL' + jnz @f + mov word [cs:cfgmanager.loader_block], si + mov word [cs:cfgmanager.loader_block+2], ds +@@: +; \end{diamond}[02.12.2005] + +; if bootloader sets cx = 'HA' and dx = 'RD', then bx contains identifier of source hard disk +; (see comment to bx_from_load) + cmp cx, 'HA' + jnz no_hd_load + cmp dx, 'RD' + jnz no_hd_load + mov word [cs:bx_from_load], bx ; {SPraid}[13.03.2007] +no_hd_load: + +; set up stack + mov ax, 3000h + mov ss, ax + mov sp, 0EC00h +; set up segment registers + push cs + pop ds + push cs + pop es +end if + +; set videomode + mov ax, 3 + int 0x10 + +if lang eq ru + ; Load & set russian VGA font (RU.INC) + mov bp, RU_FNT1 ; RU_FNT1 - First part + mov bx, 1000h ; 768 bytes + mov cx, 30h ; 48 symbols + mov dx, 80h ; 128 - position of first symbol + mov ax, 1100h + int 10h + + mov bp, RU_FNT2 ; RU_FNT2 -Second part + mov bx, 1000h ; 512 bytes + mov cx, 20h ; 32 symbols + mov dx, 0E0h ; 224 - position of first symbol + mov ax, 1100h + int 10h + ; End set VGA russian font +else if lang eq et + mov bp, ET_FNT ; ET_FNT1 + mov bx, 1000h ; + mov cx, 255 ; 256 symbols + xor dx, dx ; 0 - position of first symbol + mov ax, 1100h + int 10h +end if + +; draw frames + push 0xb800 + pop es + xor di, di + mov ah, 1*16+15 + +; draw top + mov si, d80x25_top + mov cx, d80x25_top_num * 80 +@@: + lodsb + stosw + loop @b +; draw spaces + mov si, space_msg + mov dx, 25 - d80x25_top_num - d80x25_bottom_num +dfl1: + push si + mov cx, 80 +@@: + lodsb + stosw + loop @b + pop si + dec dx + jnz dfl1 +; draw bottom + mov si, d80x25_bottom + mov cx, d80x25_bottom_num * 80 +@@: + lodsb + stosw + loop @b + + mov byte [space_msg+80], 0 ; now space_msg is null terminated + + _setcursor d80x25_top_num,0 + + +; TEST FOR 386+ + + mov bx, 0x4000 + pushf + pop ax + mov dx, ax + xor ax, bx + push ax + popf + pushf + pop ax + and ax, bx + and dx, bx + cmp ax, dx + jnz cpugood + mov si, not386 +sayerr: + call print + jmp $ + cpugood: + + push 0 + popf + sti + +; set up esp + movzx esp, sp + + push 0 + pop es + xor ax, ax + and word [es:BOOT_IDE_BASE_ADDR], ax ;0 + and word [es:BOOT_IDE_BAR0_16], ax ;0 + and word [es:BOOT_IDE_BAR1_16], ax ;0 + and word [es:BOOT_IDE_BAR2_16], ax ;0 + and word [es:BOOT_IDE_BAR3_16], ax ;0 +; \begin{Mario79} +; find HDD IDE DMA PCI device +; check for PCI BIOS + mov ax, 0xB101 + int 0x1A + jc .nopci + cmp edx, 'PCI ' + jnz .nopci +; find PCI class code +; class 1 = mass storage +; subclass 1 = IDE controller +; a) class 1, subclass 1, programming interface 0x80 +; This is a Parallel IDE Controller which uses IRQs 14 and 15. + mov ax, 0xB103 + mov ecx, 1*10000h + 1*100h + 0x80 + mov [es:BOOT_IDE_PI_16], cx + xor si, si ; device index = 0 + int 0x1A + jnc .found_1 ; Parallel IDE Controller +; b) class 1, subclass 1, programming interface 0x8f + mov ax, 0xB103 + mov ecx, 1*10000h + 1*100h + 0x8f + mov [es:BOOT_IDE_PI_16], cx + xor si, si ; device index = 0 + int 0x1A + jnc .found_1 +; c) class 1, subclass 1, programming interface 0x85 + mov ax, 0xB103 + mov ecx, 1*10000h + 1*100h + 0x85 + mov [es:BOOT_IDE_PI_16], cx + xor si, si ; device index = 0 + int 0x1A + jnc .found_1 +; d) class 1, subclass 1, programming interface 0x8A +; This is a Parallel IDE Controller which uses IRQs 14 and 15. + mov ax, 0xB103 + mov ecx, 1*10000h + 1*100h + 0x8A + mov [es:BOOT_IDE_PI_16], cx + xor si, si ; device index = 0 + int 0x1A + jnc .found_1 ; Parallel IDE Controller +; Controller not found! + xor ax, ax + mov [es:BOOT_IDE_PI_16], ax + jmp .nopci +;-------------------------------------- +.found_1: +; get memory base BAR4 + mov ax, 0xB10A + mov di, 0x20 ; memory base is config register at 0x20 + push cx + int 0x1A + jc .no_BAR4 ;.nopci + and cx, 0xFFFC ; clear address decode type + mov [es:BOOT_IDE_BASE_ADDR], cx +.no_BAR4: + pop cx +;-------------------------------------- +.found: +; get Interrupt Line + mov ax, 0xB10A + mov di, 0x3c ; memory base is config register at 0x3c + push cx + int 0x1A + jc .no_Interrupt ;.nopci + + mov [es:BOOT_IDE_INTERR_16], cx +.no_Interrupt: + pop cx +;-------------------------------------- +; get memory base BAR0 + mov ax, 0xB10A + mov di, 0x10 ; memory base is config register at 0x10 + push cx + int 0x1A + jc .no_BAR0 ;.nopci + + mov [es:BOOT_IDE_BAR0_16], cx +.no_BAR0: + pop cx +;-------------------------------------- +; get memory base BAR1 + mov ax, 0xB10A + mov di, 0x14 ; memory base is config register at 0x14 + push cx + int 0x1A + jc .no_BAR1 ;.nopci + + mov [es:BOOT_IDE_BAR1_16], cx +.no_BAR1: + pop cx +;-------------------------------------- +; get memory base BAR2 + mov ax, 0xB10A + mov di, 0x18 ; memory base is config register at 0x18 + push cx + int 0x1A + jc .no_BAR2 ;.nopci + + mov [es:BOOT_IDE_BAR2_16], cx +.no_BAR2: + pop cx +;-------------------------------------- +; get memory base BAR3 + mov ax, 0xB10A + mov di, 0x1C ; memory base is config register at 0x1c + push cx + int 0x1A + jc .no_BAR3 ;.nopci + + mov [es:BOOT_IDE_BAR3_16], cx +.no_BAR3: + pop cx +;-------------------------------------- +.nopci: +; \end{Mario79} + + mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование + out 0x60, al + xor cx, cx +wait_loop: ; variant 2 +; reading state of port of 8042 controller + in al, 64h + and al, 00000010b ; ready flag +; wait until 8042 controller is ready + loopnz wait_loop + +;;;/diamond today 5.02.2008 +; set keyboard typematic rate & delay + mov al, 0xf3 + out 0x60, al + xor cx, cx +@@: + in al, 64h + test al, 2 + loopnz @b + mov al, 0 + out 0x60, al + xor cx, cx +@@: + in al, 64h + test al, 2 + loopnz @b +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; --------------- APM --------------------- + and word [es:BOOT_APM_VERSION], 0 ; ver = 0.0 (APM not found) + mov ax, 0x5300 + xor bx, bx + int 0x15 + jc apm_end ; APM not found + test cx, 2 + jz apm_end ; APM 32-bit protected-mode interface not supported + mov [es:BOOT_APM_VERSION], ax ; Save APM Version + mov [es:BOOT_APM_FLAGS], cx ; Save APM flags + + ; Write APM ver ---- + and ax, 0xf0f + add ax, '00' + mov si, msg_apm + mov [si + 5], ah + mov [si + 7], al + _setcursor 0, 3 + call printplain + ; ------------------ + + mov ax, 0x5304 ; Disconnect interface + xor bx, bx + int 0x15 + mov ax, 0x5303 ; Connect 32 bit mode interface + xor bx, bx + int 0x15 + + mov [es:BOOT_APM_ENTRY], ebx + mov [es:BOOT_APM_CODE_32], ax + mov [es:BOOT_APM_CODE_16], cx + mov [es:BOOT_APM_DATA_16], dx + +apm_end: + _setcursor d80x25_top_num, 0 + +if ~ defined extended_primary_loader +;CHECK current of code + cmp [cfgmanager.loader_block], -1 + jz noloaderblock + les bx, [cfgmanager.loader_block] + cmp byte [es:bx], 1 + mov si, loader_block_error + jnz sayerr + push 0 + pop es +end if + +noloaderblock: +; DISPLAY VESA INFORMATION + call print_vesa_info + call calc_vmodes_table + call check_first_parm ;check and enable cursor_pos + +; \begin{diamond}[30.11.2005] +cfgmanager: +; settings: +; a) preboot_graph = graphical mode +; preboot_gprobe = probe this mode? +; b) preboot_biosdisk = use BIOS disks through V86 emulation? // (earlier was: preboot_dma = use DMA access?) +; c) preboot_debug = duplicates kernel debug output to the screen // (earlier was: preboot_vrrm = use VRR?) +; // VRR is an obsolete functionality, used only with CRT monitors: increase display frequency by reducing screen resolution +; d) preboot_launcher = start the first app (right now it's LAUNCHER) after kernel is loaded? +; e) preboot_device = from where to boot? + +; determine default settings +if ~ defined extended_primary_loader + mov [.bSettingsChanged], 0 +end if + +;.preboot_gr_end: + mov di, preboot_device +; if image in memory is present and [preboot_device] is uninitialized, +; set it to use this preloaded image + cmp byte [di], 0 + jnz .preboot_device_inited +if defined extended_primary_loader + inc byte [di] + cmp byte [bootdevice], 'f' ; floppy? + jz .preboot_device_inited + inc byte [di] +else + cmp [.loader_block], -1 + jz @f + les bx, [.loader_block] + test byte [es:bx+1], 1 + jz @f + mov byte [di], 3 + jmp .preboot_device_inited +@@: +; otherwise, set [preboot_device] to 1 (default value - boot from floppy) + mov byte [di], 1 +end if +.preboot_device_inited: +; following 4 lines set variables to 1 if its current value is 0 + cmp byte [di+preboot_dma-preboot_device], 1 + adc byte [di+preboot_dma-preboot_device], 0 + cmp byte [di+preboot_launcher-preboot_device], 1 ; Start LAUNCHER by default + adc byte [di+preboot_launcher-preboot_device], 0 +; cmp byte [di+preboot_biosdisk-preboot_device], 1 +; adc byte [di+preboot_biosdisk-preboot_device], 0 +;; default value for VRR is OFF +; cmp byte [di+preboot_vrrm-preboot_device], 0 +; jnz @f +; mov byte [di+preboot_vrrm-preboot_device], 2 +;@@: +; notify user + _setcursor 5,2 + + mov si, linef + call printplain + mov si, start_msg + call print + mov si, time_msg + call print +; get start time + call .gettime + mov [.starttime], eax + mov word [.timer], .newtimer + mov word [.timer+2], cs +.printcfg: + + _setcursor 9,0 + mov si, current_cfg_msg + call print + mov si, curvideo_msg + call print + + call draw_current_vmode + + mov si, usebd_msg + cmp [preboot_biosdisk], 1 + call .say_on_off +; mov si, vrrm_msg +; cmp [preboot_vrrm], 1 +; call .say_on_off + mov si, debug_mode_msg + cmp [preboot_debug], 1 + call .say_on_off + mov si, launcher_msg + cmp [preboot_launcher], 1 + call .say_on_off + mov si, preboot_device_msg + call print + mov al, [preboot_device] +if defined extended_primary_loader + and eax, 3 +else + and eax, 7 +end if + mov si, [preboot_device_msgs+eax*2] + call printplain +.show_remarks: +; show remarks in gray color + mov di, ((21-num_remarks)*80 + 2)*2 + push 0xB800 + pop es + mov cx, num_remarks + mov si, remarks +.write_remarks: + lodsw + push si + xchg ax, si + mov ah, 1*16+7 ; background: blue (1), foreground: gray (7) + push di +.write_remark: + lodsb + test al, al + jz @f + stosw + jmp .write_remark +@@: + pop di + pop si + add di, 80*2 + loop .write_remarks +.wait: + _setcursor 25,0 ; out of screen +; set timer interrupt handler + cli + push 0 + pop es + push dword [es:8*4] + pop dword [.oldtimer] + push dword [.timer] + pop dword [es:8*4] +; mov eax, [es:8*4] +; mov [.oldtimer], eax +; mov eax, [.timer] +; mov [es:8*4], eax + sti +; wait for keypressed + xor ax, ax + int 16h + push ax +; restore timer interrupt +; push 0 +; pop es + mov eax, [.oldtimer] + mov [es:8*4], eax + mov [.timer], eax + + _setcursor 7,0 + mov si, space_msg + call printplain +; clear remarks and restore normal attributes + push es + mov di, ((21-num_remarks)*80 + 2)*2 + push 0xB800 + pop es + mov cx, num_remarks + mov ax, ' ' + (1*16 + 15)*100h +@@: + push cx + mov cx, 76 + rep stosw + pop cx + add di, 4*2 + loop @b + pop es + pop ax +; switch on key + cmp al, 13 + jz .continue + or al, 20h + cmp al, 'a' ; select graphical mode + jz .change_a + cmp al, 'q' ; Trick to make 'A' key on azerty keyboard work + je .change_a + cmp al, 'b' ; use BIOS disks? // (selecting YES will make BIOS disks visible as /bd) + jz .change_b + cmp al, 'c' ; load kernel in debug mode? // (earlier was: use VRR?) + jz .change_c + cmp al, 'd' ; start launcher after kernel is loaded? + jz .change_d + cmp al, 'e' ; select boot origin + jnz .show_remarks +; e) preboot_device = from where to boot? +if defined extended_primary_loader + _ask_question bdev,'12',preboot_device ; range accepted for answer: 1-2 +else + _ask_question bdev,'14',preboot_device ; range accepted for answer: 1-4 +end if + _setcursor 14,0 + +.d: +if ~ defined extended_primary_loader + mov [.bSettingsChanged], 1 +end if +.esc_pressed: + call clear_vmodes_table ;clear vmodes_table + jmp .printcfg + +.change_a: + call clear_vmodes_table ;clear vmodes_table + + mov si, word [cursor_pos] + mov word [cursor_pos_old], si +.loops: + call draw_vmodes_table + _setcursor 25,0 ; out of screen + xor ax, ax + int 0x16 +; call clear_table_cursor ;clear current position of cursor + + mov si, word [cursor_pos] + + cmp al, 27 ; If ESC was pressed, do not change the value + jnz @f ; Just exit the resolution selection box + + mov si, word [cursor_pos_old] + mov word [cursor_pos], si + jmp .esc_pressed +@@: + cmp ah, 0x48;x,0x48E0 ; up + jne .down + cmp si, modes_table + jbe .loops + sub word [cursor_pos], size_of_step + jmp .loops + +.down: + cmp ah, 0x50;x,0x50E0 ; down + jne .pgup + cmp word[es:si+10], -1 + je .loops + add word [cursor_pos], size_of_step + jmp .loops + +.pgup: + cmp ah, 0x49 ; page up + jne .pgdn + sub si, size_of_step*long_v_table + cmp si, modes_table + jae @f + mov si, modes_table +@@: + mov word [cursor_pos], si + mov si, word [home_cursor] + sub si, size_of_step*long_v_table + cmp si, modes_table + jae @f + mov si, modes_table +@@: + mov word [home_cursor], si + jmp .loops + +.pgdn: + cmp ah, 0x51 ; page down + jne .enter + mov ax, [end_cursor] + add si, size_of_step*long_v_table + cmp si, ax + jb @f + mov si, ax + sub si, size_of_step +@@: + mov word [cursor_pos], si + mov si, word [home_cursor] + sub ax, size_of_step*long_v_table + add si, size_of_step*long_v_table + cmp si, ax + jb @f + mov si, ax +@@: + mov word [home_cursor], si + jmp .loops + +.enter: + cmp al, 0x0D;x,0x1C0D ; enter + jne .loops + push word [cursor_pos] + pop bp + push word [es:bp] + pop word [x_save] + push word [es:bp+2] + pop word [y_save] + push word [es:bp+6] + pop word [number_vm] + mov word [preboot_graph], bp ;save choose + + jmp .d + +.change_b: ; b) preboot_biosdisk = use BIOS disks through V86 emulation? +; _setcursor 16,0 +; mov si, ask_dma // (earlier was: preboot_dma = use DMA access?) +; call print +; mov bx, '13' ; range accepted for answer: 1-3 +; call getkey +; mov [preboot_dma], al + _ask_question ask_bd,'12',preboot_biosdisk ; range accepted for answer: 1-2 + _setcursor 11,0 + jmp .d +;.change_c: ; // VRR is an obsolete functionality, used only with CRT monitors +; _setcursor 16,0 +; mov si, vrrmprint +; call print +; mov bx, '12' ; range accepted for answer: 1-2 +; call getkey +; mov [preboot_vrrm], al +; _setcursor 12,0 +; jmp .d +.change_c: ; c) preboot_debug = duplicates kernel debug output to the screen + _ask_question ask_debug,'12',preboot_debug ; range accepted for answer: 1-2 + _setcursor 12,0 + jmp .d +.change_d: ; d) preboot_launcher = start the first app (right now it's LAUNCHER) after kernel is loaded? + _ask_question ask_launcher,'12',preboot_launcher ; range accepted for answer: 1-2 + _setcursor 13,0 + jmp .d +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.say_on_off: + pushf + call print + mov si, on_msg + popf + jz @f + mov si, off_msg +@@: + jmp printplain +; novesa and vervesa strings are not used at the moment of executing this code +virtual at novesa +.oldtimer dd ? +.starttime dd ? +if ~ defined extended_primary_loader +.bSettingsChanged db ? +end if +.timer dd ? +end virtual +if ~ defined extended_primary_loader +.loader_block dd -1 +end if +.gettime: + mov ah, 0 + int 1Ah + xchg ax, cx + shl eax, 10h + xchg ax, dx + ret +.newtimer: + push ds + push cs + pop ds + pushf + call [.oldtimer] + pushad + call .gettime + sub eax, [.starttime] +if defined extended_primary_loader + sub ax, [preboot_timeout] +else + sub ax, 18*5 +end if + jae .timergo + neg ax + add ax, 18-1 + mov bx, 18 + xor dx, dx + div bx +if lang eq ru +; подождите 5 секунд, 4/3/2 секунды, 1 секунду + cmp al, 5 + mov cl, ' ' + jae @f + cmp al, 1 + mov cl, 0xE3 ; 'у' in cp866 + jz @f + mov cl, 0xEB ; 'ы' in cp866 +@@: + mov [time_str+9], cl +else if lang eq et + cmp al, 1 + ja @f + mov byte [time_str+9], ' ' + mov byte [time_str+10], ' ' +@@: +else if lang eq sp +; esperar 5/4/3/2 segundos, 1 segundo + cmp al, 1 + mov cl, 's' + ja @f + mov cl, ' ' +@@: + mov [time_str+10], cl +else +; wait 5/4/3/2 seconds, 1 second + cmp al, 1 + mov cl, 's' + ja @f + mov cl, ' ' +@@: + mov [time_str+9], cl +end if + add al, '0' + mov [time_str+1], al + mov si, time_msg + _setcursor 7,0 + call print + _setcursor 25,0 + popad + pop ds + iret +.timergo: + push 0 + pop es + mov eax, [.oldtimer] + mov [es:8*4], eax + mov sp, 0EC00h +.continue: + sti + _setcursor 6,0 + mov si, space_msg + call printplain + call printplain + _setcursor 6,0 + mov si, loading_msg + call print + _setcursor 16,0 +if ~ defined extended_primary_loader + cmp [.bSettingsChanged], 0 + jz .load + cmp [.loader_block], -1 + jz .load + les bx, [.loader_block] + mov eax, [es:bx+3] + push ds + pop es + test eax, eax + jz .load + push eax + mov si, save_quest + call print +.waityn: + mov ah, 0 + int 16h + or al, 20h + cmp al, 'n' + jz .loadc + if lang eq sp + cmp al, 's' + else + cmp al, 'y' + end if + jnz .waityn + call putchar + mov byte [space_msg+80], 186 + + pop eax + push cs + push .cont + push eax + retf ;call back +.loadc: + pop eax +.cont: + push cs + pop ds + mov si, space_msg + mov byte [si+80], 0 + _setcursor 16,0 + call printplain + _setcursor 16,0 +.load: +end if +; \end{diamond}[02.12.2005] + +; ASK GRAPHICS MODE + + call set_vmode + +; GRAPHICS ACCELERATION +; force yes + mov [es:BOOT_MTRR], byte 1 + +; DMA ACCESS TO HD + + mov al, [preboot_dma] + mov [es:BOOT_DMA], al + +;; VRR_M USE +; +; mov al,[preboot_vrrm] +; mov [es:BOOT_VRR], al ;// 0x9030 + +; Set kernel DEBUG mode - if nonzero, duplicates debug output to the screen. + mov al, [preboot_debug] + mov [es:BOOT_DEBUG_PRINT], al ;// 0x901E + +; Start the first app (right now it's LAUNCHER) after kernel is loaded? + mov al, [preboot_launcher] + mov [es:BOOT_LAUNCHER_START], al ;// 0x901D + +; BOOT DEVICE + + mov al, [preboot_device] + dec al + mov [boot_dev], al + +; GET MEMORY MAP +include '../detect/biosmem.inc' + +; READ DISKETTE TO MEMORY + + cmp [boot_dev], 0 + jne no_sys_on_floppy + mov si, diskload + call print + xor ax, ax ; reset drive + xor dx, dx + int 0x13 +; do we boot from CD-ROM? + mov ah, 41h + mov bx, 55AAh + xor dx, dx + int 0x13 + jc .nocd + cmp bx, 0AA55h + jnz .nocd + mov ah, 48h + push ds + push es + pop ds + mov si, 0xa000 + mov word [si], 30 + int 0x13 + pop ds + jc .nocd + push ds + lds si, [es:si+26] + test byte [ds:si+10], 40h + pop ds + jz .nocd +; yes - read all floppy by 18 sectors + +; TODO: !!!! read only first sector and set variables !!!!! +; ... +; TODO: !!! then read flippy image track by track + + mov cx, 0x0001 ; startcyl,startsector +.a1: + push cx dx + mov al, 18 + mov bx, 0xa000 + call boot_read_floppy + mov si, movedesc + push es + push ds + pop es + mov cx, 256*18 + mov ah, 0x87 + int 0x15 + pop es + pop dx cx + test ah, ah + jnz sayerr_floppy + add dword [si+8*3+2], 512*18 + inc dh + cmp dh, 2 + jnz .a1 + mov dh, 0 + inc ch + cmp ch, 80 + jae ok_sys_on_floppy + pusha + mov al, ch + shr ch, 2 + add al, ch + aam + xchg al, ah + add ax, '00' + mov si, pros + mov [si], ax + call printplain + popa + jmp .a1 +.nocd: +; no - read only used sectors from floppy +; now load floppy image to memory +; at first load boot sector and first FAT table + +; read only first sector and fill variables + mov cx, 0x0001 ; first logical sector + xor dx, dx ; head = 0, drive = 0 (a:) + mov al, 1 ; read one sector + mov bx, 0xB000 ; es:bx -> data area + call boot_read_floppy +; fill the necessary parameters to work with a floppy + mov ax, word [es:bx+24] + mov word [BPB_SecPerTrk], ax + mov ax, word [es:bx+26] + mov word [BPB_NumHeads], ax + mov ax, word [es:bx+17] + mov word [BPB_RootEntCnt], ax + mov ax, word [es:bx+14] + mov word [BPB_RsvdSecCnt], ax + mov ax, word [es:bx+19] + mov word [BPB_TotSec16], ax + mov al, byte [es:bx+13] + mov byte [BPB_SecPerClus], al + mov al, byte [es:bx+16] + mov byte [BPB_NumFATs], al +; 18.11.2008 + mov ax, word [es:bx+22] + mov word [BPB_FATSz16], ax + mov cx, word [es:bx+11] + mov word [BPB_BytsPerSec], cx + +; count of clusters in FAT12 ((size_of_FAT*2)/3) +; mov ax, word [BPB_FATSz16] +; mov cx, word [BPB_BytsPerSec] +;end 18.11.2008 + xor dx, dx + mul cx + shl ax, 1 + mov cx, 3 + div cx ; now ax - number of clusters in FAT12 + mov word [end_of_FAT], ax + +; load first FAT table + mov cx, 0x0002 ; startcyl,startsector ; TODO!!!!! + xor dx, dx ; starthead,drive + mov al, byte [BPB_FATSz16] ; no of sectors to read + add bx, word [BPB_BytsPerSec] ; es:bx -> data area + call boot_read_floppy + mov bx, 0xB000 + +; and copy them to extended memory + mov si, movedesc + mov [si+8*2+3], bh ; from + + mov ax, word [BPB_BytsPerSec] + shr ax, 1 ; words per sector + mov cx, word [BPB_RsvdSecCnt] + add cx, word [BPB_FATSz16] + mul cx + push ax ; save to stack count of words in boot+FAT + xchg ax, cx + + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + test ah, ah + jz @f +sayerr_floppy: + mov dx, 0x3f2 + mov al, 0 + out dx, al +sayerr_memmove: + mov si, memmovefailed + jmp sayerr_plain +@@: + pop ax ; restore from stack count of words in boot+FAT + shl ax, 1 ; make bytes count from count of words + and eax, 0ffffh + add dword [si+8*3+2], eax + +; copy first FAT to second copy +; TODO: BPB_NumFATs !!!!! + add bx, word [BPB_BytsPerSec] ; !!! TODO: may be need multiply by BPB_RsvdSecCnt !!! + mov byte [si+8*2+3], bh ; bx - begin of FAT + + mov ax, word [BPB_BytsPerSec] + shr ax, 1 ; words per sector + mov cx, word [BPB_FATSz16] + mul cx + mov cx, ax ; cx - count of words in FAT + + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + test ah, ah + jnz sayerr_floppy + + mov ax, cx + shl ax, 1 + and eax, 0ffffh ; ax - count of bytes in FAT + add dword [si+8*3+2], eax + +; reading RootDir +; TODO: BPB_NumFATs + add bx, ax + add bx, 100h + and bx, 0ff00h ; bx - place in buffer to write RootDir + push bx + + mov bx, word [BPB_BytsPerSec] + shr bx, 5 ; divide bx by 32 + mov ax, word [BPB_RootEntCnt] + xor dx, dx + div bx + push ax ; ax - count of RootDir sectors + + mov ax, word [BPB_FATSz16] + xor cx, cx + mov cl, byte [BPB_NumFATs] + mul cx + add ax, word [BPB_RsvdSecCnt] ; ax - first sector of RootDir + + mov word [FirstDataSector], ax + pop bx + push bx + add word [FirstDataSector], bx ; Begin of data region of floppy + +; read RootDir + call conv_abs_to_THS + pop ax + pop bx ; place in buffer to write + push ax + call boot_read_floppy ; read RootDir into buffer +; copy RootDir + mov byte [si+8*2+3], bh ; from buffer + pop ax ; ax = count of RootDir sectors + mov cx, word [BPB_BytsPerSec] + mul cx + shr ax, 1 + mov cx, ax ; count of words to copy + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + + mov ax, cx + shl ax, 1 + and eax, 0ffffh ; ax - count of bytes in RootDir + add dword [si+8*3+2], eax ; add count of bytes copied + +; Reading data clusters from floppy + mov byte [si+8*2+3], bh + push bx + + mov di, 2 ; First data cluster +.read_loop: + mov bx, di + shr bx, 1 ; bx+di = di*1.5 + jnc .even + test word [es:bx+di+0xB200], 0xFFF0 ; TODO: may not be 0xB200 !!! + jmp @f +.even: + test word [es:bx+di+0xB200], 0xFFF ; TODO: may not be 0xB200 !!! + +@@: + jz .skip +; read cluster di +;.read: + ;conv cluster di to abs. sector ax + ; ax = (N-2) * BPB_SecPerClus + FirstDataSector + mov ax, di + sub ax, 2 + xor bx, bx + mov bl, byte [BPB_SecPerClus] + mul bx + add ax, word [FirstDataSector] + call conv_abs_to_THS + pop bx + push bx + mov al, byte [BPB_SecPerClus] ; number of sectors in cluster + call boot_read_floppy + push es + push ds + pop es + pusha +; + mov ax, word [BPB_BytsPerSec] + xor cx, cx + mov cl, byte [BPB_SecPerClus] + mul cx + shr ax, 1 ; ax = (BPB_BytsPerSec * BPB_SecPerClus)/2 + mov cx, ax ; number of words to copy (count words in cluster) +; + mov ah, 0x87 + int 0x15 ; copy data + test ah, ah + popa + pop es + jnz sayerr_floppy +; skip cluster di +.skip: + mov ax, word [BPB_BytsPerSec] + xor cx, cx + mov cl, byte [BPB_SecPerClus] + mul cx + and eax, 0ffffh ; ax - count of bytes in cluster + add dword [si+8*3+2], eax + + mov ax, word [end_of_FAT] ; max cluster number + pusha +; draw percentage +; total clusters: ax +; read clusters: di + xchg ax, di + mov cx, 100 + mul cx + div di + aam + xchg al, ah + add ax, '00' + mov si, pros + cmp [si], ax + jz @f + mov [si], ax + call printplain +@@: + popa + inc di + cmp di, word [end_of_FAT] ; max number of cluster + jnz .read_loop + pop bx ; clear stack + +ok_sys_on_floppy: + mov si, backspace2 + call printplain + mov si, okt + call printplain +no_sys_on_floppy: + xor ax, ax ; reset drive + xor dx, dx + int 0x13 + mov dx, 0x3f2 ; floppy motor off + mov al, 0 + out dx, al + +if defined extended_primary_loader + cmp [boot_dev], 1 + jne no_sys_from_primary +; load kolibri.img using callback from primary loader + and word [movedesc + 24 + 2], 0 + mov byte [movedesc + 24 + 4], 10h +; read in blocks of 64K until file is fully loaded + mov ax, 1 +.repeat: + mov di, image_file_struct + call [bootcallback] + push cs + pop ds + push cs + pop es + cmp bx, 1 + ja sayerr_badsect + push bx + mov si, movedesc + and word [si + 16 + 2], 0 + mov byte [si + 16 + 4], 4 + mov ah, 87h + mov cx, 8000h + int 15h + pop bx + test ah, ah + jnz sayerr_memmove + inc byte [si + 24 + 4] + test bx, bx + jz no_sys_from_primary + mov ax, 2 + jmp .repeat +no_sys_from_primary: +end if + +; SET GRAPHICS + + xor ax, ax + mov es, ax + + mov ax, [es:BOOT_VESA_MODE] ; vga & 320x200 + mov bx, ax + cmp ax, 0x13 + je setgr + cmp ax, 0x12 + je setgr + mov ax, 0x4f02 ; Vesa +setgr: + int 0x10 + test ah, ah + mov si, fatalsel + jnz v_mode_error +; set mode 0x12 graphics registers: + cmp bx, 0x12 + jne gmok2 + + mov al, 0x05 + mov dx, 0x03ce + push dx + out dx, al ; select GDC mode register + mov al, 0x02 + inc dx + out dx, al ; set write mode 2 + + mov al, 0x02 + mov dx, 0x03c4 + out dx, al ; select VGA sequencer map mask register + mov al, 0x0f + inc dx + out dx, al ; set mask for all planes 0-3 + + mov al, 0x08 + pop dx + out dx, al ; select GDC bit mask register + ; for writes to 0x03cf +gmok2: + push ds + pop es diff --git a/kernel/branches/kolibri-process/boot/booten.inc b/kernel/branches/kolibri-process/boot/booten.inc new file mode 100644 index 000000000..fdc56e6ff --- /dev/null +++ b/kernel/branches/kolibri-process/boot/booten.inc @@ -0,0 +1,106 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision: 2455 $ + + +d80x25_bottom: + db 186,' KolibriOS comes with ABSOLUTELY NO WARRANTY. See file COPYING for details ',186 + db 186,' If you find any bugs, please report them at: http://board.kolibrios.org ',186 + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm db " APM x.x ", 0 +novesa db "Display: EGA/CGA",13,10,0 +s_vesa db "Version of VESA: " + .ver db "?.?",13,10,0 + +gr_mode db "Select a videomode: ",13,10,0 + +ask_bd db "Add disks visible by BIOS emulated in V86-mode? [1-yes, 2-no]: ",0 + +if defined extended_primary_loader +bdev db "Load ramdisk from [1-floppy; 2-kolibri.img]: ",0 +else +bdev db "Load ramdisk from [1-floppy; 2-C:\kolibri.img (FAT32);" + db 13,10,186," " + db "3-use preloaded ram-image from kernel restart;" + db 13,10,186," " + db "4-create blank image]: ",0 +end if + +prnotfnd db "Fatal - Videomode not found.",0 + +not386 db "Fatal - CPU 386+ required.",0 +fatalsel db "Fatal - Graphics mode not supported by hardware.",0 +pres_key db "Press any key to choose a new videomode.",0 +badsect db 13,10,186," Fatal - Bad sector. Replace floppy.",0 +memmovefailed db 13,10,186," Fatal - Int 0x15 move failed.",0 +okt db " ... OK" +linef db 13,10,0 +diskload db "Loading diskette: 00 %",8,8,8,8,0 +pros db "00" +backspace2 db 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg db "Press [abcde] to change settings, press [Enter] to continue booting",13,10,0 +time_msg db " or wait " +time_str db " 5 seconds" + db " before automatical continuation",13,10,0 +current_cfg_msg db "Current settings:",13,10,0 +curvideo_msg db " [a] Videomode: ",0 + +mode0 db "320x200, EGA/CGA 256 colors",13,10,0 +mode9 db "640x480, VGA 16 colors",13,10,0 + +usebd_msg db " [b] Add disks visible by BIOS:",0 +on_msg db " on",13,10,0 +off_msg db " off",13,10,0 + +debug_mode_msg db " [c] Duplicate debug output to the screen:",0 +ask_debug db "Duplicate debug output to the screen? [1-yes, 2-no]: ",0 + +launcher_msg db " [d] Start LAUNCHER after kernel is loaded:",0 +ask_launcher db "Start first application (LAUNCHER) after kernel is loaded? [1-yes, 2-no]: ",0 + +preboot_device_msg db " [e] Floppy image: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 db "real floppy",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 db "real floppy",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +pdm3 db "use already loaded image",13,10,0 +pdm4 db "create blank image",13,10,0 +end if + +loading_msg db "Loading KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest db "Remember current settings? [y/n]: ",0 +loader_block_error db "Bootloader data invalid, I cannot continue. Stopped.",0 +end if + +_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 option [b]. If the system gets",0 +remark3 db "stuck after booting, enable option [c], disable option [d] and make photo.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 \ No newline at end of file diff --git a/kernel/branches/kolibri-process/boot/bootet.inc b/kernel/branches/kolibri-process/boot/bootet.inc new file mode 100644 index 000000000..475d55d00 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootet.inc @@ -0,0 +1,106 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision: 4135 $ + + +d80x25_bottom: + latin1 '║ KolibriOS on IGASUGUSE GARANTIITA. Vaata faili COPYING info saamiseks. Kui ║' + latin1 '║ leiate vigu, anna neist palun teada aadressil: http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +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 latin1 "Vali video resolutsioon: ",13,10,0 + +ask_bd latin1 "Lisa V86 reziimis BIOSle nähtavad kettad? [1-jah, 2-ei]: ",0 + +if defined extended_primary_loader +bdev latin1 "Paigalda mäluketas [1-diskett; 2-kolibri.img]: ",0 +else +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 latin1 "Fataalne - Video resolutsiooni ei leitud.",0 + +not386 latin1 "Fataalne - CPU 386+ on vajalik.",0 +fatalsel latin1 "Fataalne - Riistvara ei toeta graafilist resolutsiooni.",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 latin1 "Vajuta [abcde] 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] Video resolutsioon: ",0 + +mode0 latin1 "320x200, EGA/CGA 256 värvi",0 +mode9 latin1 "640x480, VGA 16 värvi",0 + +usebd_msg latin1 " [b] Lisa BIOSle nähtavad kettad:",0 +on_msg latin1 " sees",13,10,0 +off_msg latin1 " väljas",13,10,0 + +debug_mode_msg latin1 " [c] Dubleeri silumisinfo ekraanile:",0 +ask_debug latin1 "Dubleeri silumisinfo ekraanile? [1-jah, 2-ei]: ",0 + +launcher_msg latin1 " [d] Käivita LAUNCHER pärast kerneli laadimist:",0 +ask_launcher latin1 "Käivita esimese programm (LAUNCHER) peale kerneli laadimist? [1-jah, 2-ei]: ",0 + +preboot_device_msg latin1 " [e] Disketi kujutis: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 latin1 "reaalne diskett",13,10,0 +pdm2 latin1 "kolibri.img",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,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 latin1 "Laadin KolibriOS...",0 + +if ~ defined extended_primary_loader +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 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 latin1 "Vaikimisi väärtused on kasutatavad enamikes arvutites, kuid mitte kõigis.",0 +remark2 latin1 "Kui süsteem ei käivitu, proovige lülitada kirje [b] välja. Kui see läheb",0 +remark3 latin1 "kinni pärast käivitamist, võimaldama valik [c], keelake [d] ja teha foto.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 \ No newline at end of file diff --git a/kernel/branches/kolibri-process/boot/bootge.inc b/kernel/branches/kolibri-process/boot/bootge.inc new file mode 100644 index 000000000..3020f693a --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootge.inc @@ -0,0 +1,107 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision: 4135 $ + + +d80x25_bottom: + db 186,' KolibriOS wird ohne jegliche Garantie vertrieben. Details stehen in der ',186 + db 186,' Datei COPYING. Bitte melden Sie Fehler bei: http://board.kolibrios.org ',186 + line_full_bottom +d80x25_bottom_num = 3 + +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 + +gr_mode db "Wahlen Sie einen videomode: ",13,10,0 + +ask_bd db "Add-Festplatten sichtbar BIOS in V86-Modus emuliert? [1-ja, 2 nein]: ",0 + +if defined extended_primary_loader +bdev db "Lade die Ramdisk von [1-Diskette; 2-kolibri.img]: ",0 +else +bdev db "Lade die Ramdisk von [1-Diskette; 2-C:\kolibri.img (FAT32);" + db 13,10,186," " + db "3-benutze ein bereits geladenes Kernel image;" + db 13,10,186," " + db "4-create blank image]: ",0 +end if + +prnotfnd db "Fatal - Videomodus nicht gefunden.",0 + +not386 db "Fatal - CPU 386+ benoetigt.",0 +fatalsel db "Fatal - Grafikmodus nicht unterstuetzt.",0 +pres_key db "Drucken Sie eine beliebige Taste, um eine neue videomode wahlen.",0 +badsect db 13,10,186," Fatal - Sektorfehler, Andere Diskette neutzen.",0 +memmovefailed db 13,10,186," Fatal - Int 0x15 Fehler.",0 +okt db " ... OK" +linef db 13,10,0 +diskload db "Lade Diskette: 00 %",8,8,8,8,0 +pros db "00" +backspace2 db 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg db "Druecke [abcde], um die Einstellungen zu aendern, druecke [Enter] zum starten",13,10,0 +time_msg db " oder warte " +time_str db " 5 Sekunden" + db " bis zum automatischen Start",13,10,0 +current_cfg_msg db "Aktuelle Einstellungen:",13,10,0 +curvideo_msg db " [a] Videomodus: ",0 + +mode0 db "320x200, EGA/CGA 256 colors",13,10,0 +mode9 db "640x480, VGA 16 colors",13,10,0 + +usebd_msg db " [b] Add-Festplatten sichtbar durch das BIOS:",0 +on_msg db " an",13,10,0 +off_msg db " aus",13,10,0 + +debug_mode_msg db " [c] Duplizieren debuggen Ausgabe auf dem Bildschirm:",0 +ask_debug db "Duplizieren debuggen Ausgabe auf dem Bildschirm? [1-ja, 2 nein]: ",0 + +launcher_msg db " [d] Start LAUNCHER nach Kernel geladen wird:",0 +ask_launcher db "Starten erste Anwendung nach Kernel geladen wird? [1-ja, 2 nein]: ",0 + +preboot_device_msg db " [e] Diskettenimage: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 db "Echte Diskette",13,10,0 +pdm2 db "kolibri.img",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 db "Echte Diskette",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +pdm3 db "Nutze bereits geladenes Image",13,10,0 +pdm4 db "create blank image",13,10,0 +end if + +loading_msg db "Lade KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest db "Aktuelle Einstellungen speichern? [y/n]: ",0 +loader_block_error db "Bootloader Daten ungueltig, Kann nicht fortfahren. Angehalten.",0 +end if + +_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, das Option [b] deaktivieren versuchen. Wenn es",0 +remark3 db "nach dem Booten hangen bleibt, aktivieren Sie Option [c], deaktivieren [d]",0 +remark4 db "und machen Fotos.",0 +remarks dw remark1, remark2, remark3, remark4 +num_remarks = 4 \ No newline at end of file diff --git a/kernel/branches/kolibri-process/boot/bootru.inc b/kernel/branches/kolibri-process/boot/bootru.inc new file mode 100644 index 000000000..f0dfece87 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootru.inc @@ -0,0 +1,104 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;================================================================= +; +; BOOT DATA +; +;================================================================= + +$Revision: 4135 $ + + +d80x25_bottom: + cp866 '║ KolibriOS НЕ ПРЕДОСТАВЛЯЕТ НИКАКИХ ГАРAНТИЙ. Подробнее смотрите в файле ║' + cp866 '║ COPYING.TXT. О найденных ошибках сообщайте на http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +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 cp866 "Выберите видеорежим: ",13,10,0 + +ask_bd cp866 "Добавить диски, видимые через BIOS в режиме V86? [1-да, 2-нет]: ",0 + +if defined extended_primary_loader +bdev cp866 "Загрузить образ из [1-дискета; 2-kolibri.img из папки загрузки]: ",0 +else +bdev cp866 "Загрузить образ из [1-дискета; 2-C:\kolibri.img (FAT32);",13,10 + cp866 "║ 3-использовать уже загруженный образ;",13,10 + cp866 "║ 4-создать чистый образ]: ",0 +end if + +prnotfnd cp866 "Ошибка - Видеорежим не найден.",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 "Нажмите [abcde] для изменения настроек, [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 cp866 "320x200, EGA/CGA 256 цветов",13,10,0 +mode9 cp866 "640x480, VGA 16 цветов",13,10,0 + +usebd_msg cp866 " [b] Добавить диски, видимые через BIOS:",0 +on_msg cp866 " вкл",13,10,0 +off_msg cp866 " выкл",13,10,0 + +debug_mode_msg cp866 " [c] Дублировать дебаг-вывод на экран монитора:",0 +ask_debug cp866 "Дублировать дебаг-вывод на экран монитора? [1-да, 2-нет]: ",0 + +launcher_msg cp866 " [d] Запустить программу LAUNCHER после загрузки ядра:",0 +ask_launcher cp866 "Запустить первую программу (LAUNCHER) после загрузки ядра? [1-да, 2-нет]: ",0 + +preboot_device_msg cp866 " [e] Образ дискеты: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 cp866 "настоящая дискета",13,10,0 +pdm2 cp866 "kolibri.img из папки загрузки",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,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 cp866 "Идёт загрузка KolibriOS...",0 + +if ~ defined extended_primary_loader ; saving not supported in this case +save_quest cp866 "Запомнить текущие настройки? [y/n]: ",0 +loader_block_error cp866 "Ошибка в данных начального загрузчика, продолжение невозможно.",0 +end if + +_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 cp866 "Значения по умолчанию выбраны для удобства большинства, но не всех. Если у",0 +remark2 cp866 "Вас не грузится система, попробуйте отключить пункт [b]. Если она зависла",0 +remark3 cp866 "после запуска, включите пункт [c], отключите пункт [d] и сделайте фото лога.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 \ No newline at end of file diff --git a/kernel/branches/kolibri-process/boot/bootsp.inc b/kernel/branches/kolibri-process/boot/bootsp.inc new file mode 100644 index 000000000..3f9a80db6 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootsp.inc @@ -0,0 +1,108 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +; Para modificar éste archivo es necesario abrirlo con codificación CP850 + +$Revision: 2455 $ + + +d80x25_bottom: + cp850 '║ KolibriOS viene ABSOLUTAMENTE SIN GARANTíA. Lee el archivo COPYING por más ║' + cp850 '║ detalles. Por favor, informar de los errores en: http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +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 cp850 "Selecciona un modo de video: ",13,10,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 cp850 "Cargar unidad ram desde [1-disquete; 2-kolibri.img]: ",0 +else +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 cp850 "Fatal - Modo de video no encontrado.",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 cp850 "Presiona [abcde] 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 cp850 "320x200, EGA/CGA 256 colores",13,10,0 +mode9 cp850 "640x480, VGA 16 colores",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 + +debug_mode_msg cp850 " [c] Duplicar depurar salida a la pantalla:",0 +ask_debug cp850 "¿Duplicar depurar la salida a la pantalla? [1-si, 2-no]: ",0 + +launcher_msg cp850 " [d] Iniciar LAUNCHER después de cargar kernel:",0 +ask_launcher cp850 "¿Inicie la primera aplicación después de cargar el kernel? [1-si, 2-no]: ",0 + +preboot_device_msg cp850 " [e] Imagen de disquete: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,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,pdm4,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 cp850 "Cargando KolibriOS...",0 + +if ~ defined extended_primary_loader +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 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 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]. Si se bloquea",0 +remark3 cp850 "después de arrancar, habilite la opción [c], desactivar [d] y hacer fotos.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 \ No newline at end of file diff --git a/kernel/branches/kolibri-process/boot/bootstr.inc b/kernel/branches/kolibri-process/boot/bootstr.inc new file mode 100644 index 000000000..76480bd45 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootstr.inc @@ -0,0 +1,63 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; boot data: common strings (for all languages) +macro line_full_top { + db 201 + times 78 db 205 + db 187 +} +macro line_full_bottom { + db 200 + times 78 db 205 + db 188 +} +macro line_half { + db 186,' ' + times 76 db 0xc4 + db ' ',186 +} +macro line_space { + db 186 + times 78 db 32 + db 186 +} +d80x25_top: + line_full_top +cur_line_pos = 75 + store byte ' ' at d80x25_top+cur_line_pos+1 +rev_var = __REV__ +while rev_var > 0 + store byte rev_var mod 10 + '0' at d80x25_top+cur_line_pos + cur_line_pos = cur_line_pos - 1 + rev_var = rev_var / 10 +end while + store byte ' ' at d80x25_top+cur_line_pos + store dword ' SVN' at d80x25_top+cur_line_pos-4 + +space_msg: + line_space +verstr: +; line_space +; version string + db 186,32 + repeat 78 + load a byte from version+%-1 + if a = 13 + break + end if + db a + end repeat + repeat 78 - ($-verstr) + db ' ' + end repeat + db 32,186 + line_half +d80x25_top_num = 4 diff --git a/kernel/branches/kolibri-process/boot/bootvesa.inc b/kernel/branches/kolibri-process/boot/bootvesa.inc new file mode 100644 index 000000000..498b5dc59 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/bootvesa.inc @@ -0,0 +1,795 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3999 $ + +struc VBE_VGAInfo { + .VESASignature dd ? ; char + .VESAVersion dw ? ; short + .OemStringPtr dd ? ; char * + .Capabilities dd ? ; ulong + .VideoModePtr dd ? ; ulong + .TotalMemory dw ? ; short + ; VBE 2.0+ + .OemSoftwareRev db ? ; short + .OemVendorNamePtr dw ? ; char * + .OemProductNamePtr dw ? ; char * + .OemProductRevPtr dw ? ; char * + .reserved rb 222 ; char + .OemData rb 256 ; char +} + +struc VBE_ModeInfo { + .ModeAttributes dw ? ; short + .WinAAttributes db ? ; char + .WinBAttributes db ? ; char + .WinGranularity dw ? ; short + .WinSize dw ? ; short + .WinASegment dw ? ; ushort + .WinBSegment dw ? ; ushort + .WinFuncPtr dd ? ; void * + .BytesPerScanLine dw ? ; short + .XRes dw ? ; short + .YRes dw ? ; short + .XCharSize db ? ; char + .YCharSize db ? ; char + .NumberOfPlanes db ? ; char + .BitsPerPixel db ? ; char + .NumberOfBanks db ? ; char + .MemoryModel db ? ; char + .BankSize db ? ; char + .NumberOfImagePages db ? ; char + .res1 db ? ; char + .RedMaskSize db ? ; char + .RedFieldPosition db ? ; char + .GreenMaskSize db ? ; char + .GreenFieldPosition db ? ; char + .BlueMaskSize db ? ; char + .BlueFieldPosition db ? ; char + .RsvedMaskSize db ? ; char + .RsvedFieldPosition db ? ; char + .DirectColorModeInfo db ? ; char ; MISSED IN THIS TUTORIAL!! SEE ABOVE + ; VBE 2.0+ + .PhysBasePtr dd ? ; ulong + .OffScreenMemOffset dd ? ; ulong + .OffScreenMemSize dw ? ; short + ; VBE 3.0+ + .LinbytesPerScanLine dw ? ; short + .BankNumberOfImagePages db ? ; char + .LinNumberOfImagePages db ? ; char + .LinRedMaskSize db ? ; char + .LinRedFieldPosition db ? ; char + .LingreenMaskSize db ? ; char + .LinGreenFieldPosition db ? ; char + .LinBlueMaskSize db ? ; char + .LinBlueFieldPosition db ? ; char + .LinRsvdMaskSize db ? ; char + .LinRsvdFieldPosition db ? ; char + .MaxPixelClock dd ? ; ulong + .res2 rb 190 ; char +} + +virtual at $A000 + vi VBE_VGAInfo + mi VBE_ModeInfo +modes_table: +end virtual +cursor_pos dw 0 ;временное хранение курсора. +cursor_pos_old 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 +scroll_end dw 0 ;end position of scroll bar +long_v_table equ 9 ;long of visible video table +size_of_step equ 10 +scroll_area_size equ (long_v_table-2) +int2str: + dec bl + jz @f + xor edx, edx + div ecx + push edx + call int2str + pop eax + @@: + or al, 0x30 + mov [ds:di], al + inc di + ret + +int2strnz: + cmp eax, ecx + jb @f + xor edx, edx + div ecx + push edx + call int2strnz + pop eax + @@: + or al, 0x30 + mov [es:di], al + inc di + ret + +;------------------------------------------------------- +;Write message about incorrect v_mode and write message about jmp on swith v_mode +v_mode_error: + _setcursor 19,2 + mov si, fatalsel + call printplain + _setcursor 20,2 + mov si, pres_key + call printplain + xor eax, eax + int 16h + jmp cfgmanager.d +;------------------------------------------------------- +; + + + +;------------------------------------------------------- +print_vesa_info: + _setcursor 5,2 + + mov [es:vi.VESASignature], 'VBE2' + mov ax, 0x4F00 + mov di, vi ;0xa000 + int 0x10 + or ah, ah + jz @f + mov [es:vi.VESASignature], 'VESA' + mov ax, $4F00 + mov di, vi + int 0x10 + or ah, ah + jnz .exit + @@: + cmp [es:vi.VESASignature], 'VESA' + jne .exit + cmp [es:vi.VESAVersion], 0x0100 + jb .exit + jmp .vesaok2 + + .exit: + mov si, novesa + call printplain + ret + + .vesaok2: + mov ax, [es:vi.VESAVersion] + add ax, '00' + + mov [s_vesa.ver], ah + mov [s_vesa.ver+2], al + mov si, s_vesa + call printplain + + _setcursor 4,2 + mov si, word[es:vi.OemStringPtr] + mov di, si + + push ds + mov ds, word[es:vi.OemStringPtr+2] + call printplain + pop ds + + ret +;----------------------------------------------------------------------------- + +calc_vmodes_table: + pushad + +; push 0 +; pop es + + lfs si, [es:vi.VideoModePtr] + + mov bx, modes_table +;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 + + mov word [es:bx+10], 640 + mov word [es:bx+12], 480 + mov word [es:bx+16], 0x12 + add bx, 20 + .next_mode: + mov cx, word [fs:si]; mode number + cmp cx, -1 + je .modes_ok.2 + + mov ax, 0x4F01 + mov di, mi + int 0x10 + + or ah, ah + jnz .modes_ok.2;vesa_info.exit + + test [es:mi.ModeAttributes], 00000001b ;videomode support ? + jz @f + test [es:mi.ModeAttributes], 00010000b ;picture ? + jz @f + test [es:mi.ModeAttributes], 10000000b ;LFB ? + jz @f + + cmp [es:mi.BitsPerPixel], 24 ;It show only videomodes to have support 24 and 32 bpp + jb @f + +; cmp [es:mi.BitsPerPixel],16 +; jne .l0 +; cmp [es:mi.GreenMaskSize],5 +; jne .l0 +; mov [es:mi.BitsPerPixel],15 + + +.l0: + cmp [es:mi.XRes], 640 + jb @f + cmp [es:mi.YRes], 480 + jb @f +; cmp [es:mi.BitsPerPixel],8 +; jb @f + + mov ax, [es:mi.XRes] + mov [es:bx+0], ax ; +0[2] : resolution X + mov ax, [es:mi.YRes] + mov [es:bx+2], ax ; +2[2] : resolution Y + mov ax, [es:mi.ModeAttributes] + mov [es:bx+4], ax ; +4[2] : attributes + + cmp [s_vesa.ver], '2' +; jb .lp1 + jb @f ; We do not use Vesa 1.2 mode is now + + or cx, 0x4000 ; use LFB +.lp1: + 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 si, 2 + jmp .next_mode + + .modes_ok.2: + + mov word[es:bx], -1 ;end video table + mov word[end_cursor], bx ;save end cursor position +;;;;;;;;;;;;;;;;;; +;Sort array +; mov si,modes_table +;.new_mode: +; mov ax,word [es:si] +; cmp ax,-1 +; je .exxit +; add ax,word [es:si+2] +; add ax,word [es:si+8] +; mov bp,si +;.again: +; add bp,12 +; mov bx,word [es:bp] +; cmp bx,-1 +; je .exit +; add bx,word [es:bp+2] +; add bx,word [es:bp+8] +; +; cmp ax,bx +; ja .loops +; jmp .again +;.loops: +; push dword [es:si] +; push dword [es:si+4] +; push dword [es:si+8] +; push dword [es:bp] +; push dword [es:bp+4] +; push dword [es:bp+8] +; +; pop dword [es:si+8] +; pop dword [es:si+4] +; pop dword [es:si] +; pop dword [es:bp+8] +; pop dword [es:bp+4] +; pop dword [es:bp] +; jmp .new_mode +; +;.exit: add si,12 +; jmp .new_mode +;.exxit: + popad + ret + +;----------------------------------------------------------------------------- + +draw_current_vmode: + push 0 + pop es + + mov si, word [cursor_pos] + + cmp word [es:si+6], 0x12 + je .no_vesa_0x12 + + cmp word [es:si+6], 0x13 + je .no_vesa_0x13 + +if defined extended_primary_loader + mov di, config_file_variables +else + mov di, loader_block_error +end if + movzx eax, word[es:si+0] + mov ecx, 10 + call int2strnz + mov byte[es:di], 'x' + inc di + movzx eax, word[es:si+2] + call int2strnz + mov byte[es:di], 'x' + inc di + movzx eax, word[es:si+8] + call int2strnz + mov dword[es:di], 0x00000d0a +if defined extended_primary_loader + mov si, config_file_variables +else + mov si, loader_block_error +end if + push ds + push es + pop ds + call printplain + pop ds + ret +.no_vesa_0x13: + mov si, mode0 + jmp .print +.no_vesa_0x12: + mov si, mode9 +.print: + call printplain + ret +;----------------------------------------------------------------------------- +check_first_parm: +if defined extended_primary_loader + mov cx, [number_vm] + jcxz .novbemode + mov si, modes_table +.findvbemode: + cmp [es:si+6], cx + jnz @f + cmp word [es:si+8], 32 + je .ok_found_mode + cmp word [es:si+8], 24 + je .ok_found_mode +@@: + add si, size_of_step + cmp word [es:si], -1 + jnz .findvbemode +.novbemode: + mov ax, [x_save] + test ax, ax + jz .zerro + mov bx, [y_save] + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode +else + mov si, word [preboot_graph] + test si, si + jnz .no_zero ;if no zero +end if +.zerro: +; mov ax,modes_table +; mov word [cursor_pos],ax +; mov word [home_cursor],ax +; mov word [preboot_graph],ax +;SET default video of mode first probe will fined a move of work 1024x768@32 + + mov ax, 1024 + mov bx, 768 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + mov ax, 800 + mov bx, 600 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + mov ax, 640 + mov bx, 480 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + + mov si, modes_table +if ~ defined extended_primary_loader + jmp .ok_found_mode + + + +.no_zero: + mov bp, word [number_vm] + cmp bp, word [es:si+6] + jz .ok_found_mode + mov ax, word [x_save] + mov bx, word [y_save] + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + + mov si, modes_table +; cmp ax,modes_table +; jb .zerro ;check on correct if bellow +; cmp ax,word [end_cursor] +; ja .zerro ;check on correct if anymore +end if + +.ok_found_mode: + mov word [home_cursor], si +; mov word [cursor_pos],si + mov word [preboot_graph], si + mov ax, si + + mov ecx, long_v_table + +.loop: + add ax, size_of_step + cmp ax, word [end_cursor] + jae .next_step + loop .loop +.next_step: + sub ax, size_of_step*long_v_table + cmp ax, modes_table + jae @f + mov ax, modes_table +@@: + + mov word [home_cursor], ax + mov si, [preboot_graph] + mov word [cursor_pos], si + + push word [es:si] + pop word [x_save] + push word [es:si+2] + pop word [y_save] + push word [es:si+6] + pop word [number_vm] + + ret +;;;;;;;;;;;;;;;;;;;;;;;;;;; +.loops: + cmp ax, word [es:si] + jne .next + cmp bx, word [es:si+2] + jne .next + cmp word [es:si+8], 32 + je .ok + cmp word [es:si+8], 24 + je .ok +.next: + add si, size_of_step + cmp word [es:si], -1 + je .exit + jmp .loops +.ok: + xor ax, ax + ret +.exit: + or ax, -1 + ret + + +;----------------------------------------------------------------------------- + +;default_vmode: + +;----------------------------------------------------------------------------- +draw_vmodes_table: + _setcursor 9, 2 + mov si, gr_mode + call printplain + + mov si, _st + call printplain + + push word [cursor_pos] + pop ax + push word [home_cursor] + pop si + mov cx, si + + cmp ax, si + je .ok + jb .low + + + 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] + jae .ok + add si, size_of_step + push si + pop word [home_cursor] + jmp .ok + + +.low: + sub cx, size_of_step + cmp cx, modes_table + jb .ok + push cx + push cx + pop word [home_cursor] + pop si + + +.ok: +; calculate scroll position + push si + mov ax, [end_cursor] + sub ax, modes_table + mov bx, size_of_step + cwd + div bx + mov si, ax ; si = size of list + mov ax, [home_cursor] + sub ax, modes_table + cwd + div bx + mov di, ax + mov ax, scroll_area_size*long_v_table + cwd + div si + test ax, ax + jnz @f + inc ax +@@: + cmp al, scroll_area_size + jb @f + mov al, scroll_area_size +@@: + mov cx, ax +; cx = scroll height +; calculate scroll pos + xor bx, bx ; initialize scroll pos + sub al, scroll_area_size+1 + neg al + sub si, long_v_table-1 + jbe @f + mul di + div si + mov bx, ax +@@: + inc bx + imul ax, bx, size_of_step + add ax, [home_cursor] + mov [scroll_start], ax + imul cx, size_of_step + add ax, cx + mov [scroll_end], ax + pop si + mov bp, long_v_table ;show rows +.@@_next_bit: +;clear cursor + mov ax, ' ' + mov word[ds:_r1+21], ax + mov word[ds:_r1+50], ax + + mov word[ds:_r2+21], ax + mov word[ds:_r2+45], ax + + mov word[ds:_rs+21], ax + mov word[ds:_rs+46], ax +; draw string + cmp word [es:si+6], 0x12 + je .show_0x12 + cmp word [es:si+6], 0x13 + je .show_0x13 + + movzx eax, word[es:si] + cmp ax, -1 + je .@@_end + mov di, _rs+23 + mov ecx, 10 + mov bl, 4 + call int2str + movzx eax, word[es:si+2] + inc di + mov bl, 4 + call int2str + + movzx eax, word[es:si+8] + inc di + mov bl, 2 + call int2str + + cmp si, word [cursor_pos] + jne .next +;draw cursor + mov word[ds:_rs+21], '>>' + mov word[ds:_rs+46], '<<' + + + +.next: + push si + mov si, _rs +.@@_sh: +; add to the string pseudographics for scrollbar + pop bx + push bx + mov byte [si+53], ' ' + cmp bx, [scroll_start] + jb @f + cmp bx, [scroll_end] + jae @f + mov byte [si+53], 0xDB ; filled bar +@@: + push bx + add bx, size_of_step + cmp bx, [end_cursor] + jnz @f + mov byte [si+53], 31 ; 'down arrow' symbol +@@: + sub bx, [home_cursor] + cmp bx, size_of_step*long_v_table + jnz @f + mov byte [si+53], 31 ; 'down arrow' symbol +@@: + pop bx + cmp bx, [home_cursor] + jnz @f + mov byte [si+53], 30 ; 'up arrow' symbol +@@: + call printplain + pop si + add si, size_of_step + + dec bp + jnz .@@_next_bit + +.@@_end: + mov si, _bt + call printplain + ret +.show_0x13: + push si + + cmp si, word [cursor_pos] + jne @f + mov word[ds:_r1+21], '>>' + mov word[ds:_r1+50], '<<' +@@: + mov si, _r1 + jmp .@@_sh +.show_0x12: + push si + cmp si, word [cursor_pos] + jne @f + + mov word[ds:_r2+21], '>>' + mov word[ds:_r2+45], '<<' +@@: + mov si, _r2 + jmp .@@_sh + +;----------------------------------------------------------------------------- +;Clear arrea of current video page (0xb800) +clear_vmodes_table: + pusha + ; draw frames + push es + push 0xb800 + pop es + mov di, 1444 + xor ax, ax + mov ah, 1*16+15 + mov cx, 77 + mov bp, 12 +.loop_start: + rep stosw + mov cx, 77 + add di, 6 + dec bp + jns .loop_start + pop es + popa + ret + +;----------------------------------------------------------------------------- + +set_vmode: + push 0 ;0;x1000 + pop es + + 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 + + + mov word [es:BOOT_X_RES], ax ; resolution X + mov word [es:BOOT_Y_RES], bx ; resolution Y + mov word [es:BOOT_VESA_MODE], cx ; number of mode + + cmp cx, 0x12 + je .mode0x12_0x13 + cmp cx, 0x13 + je .mode0x12_0x13 + + +; cmp byte [s_vesa.ver], '2' +; jb .vesa12 + +; VESA 2 and Vesa 3 + + mov ax, 0x4f01 + and cx, 0xfff + mov di, mi;0xa000 + int 0x10 + ; LFB + mov eax, [es:mi.PhysBasePtr];di+0x28] + mov [es:BOOT_LFB], eax + ; ---- vbe voodoo + BytesPerLine equ 0x10 + mov ax, [es:di+BytesPerLine] + mov [es:BOOT_PITCH], ax + ; BPP + cmp [es:mi.BitsPerPixel], 16 + jne .l0 + cmp [es:mi.GreenMaskSize], 5 + jne .l0 + mov [es:mi.BitsPerPixel], 15 +.l0: + mov al, byte [es:di+0x19] + mov [es:BOOT_BPP], al + jmp .exit + +.mode0x12_0x13: + mov byte [es:BOOT_BPP], 32 + or dword [es:BOOT_LFB], 0xFFFFFFFF; 0x800000 + + +; VESA 1.2 PM BANK SWITCH ADDRESS + +;.vesa12: +; mov ax, 0x4f0A +; xor bx, bx +; int 0x10 +; xor eax, eax +; xor ebx, ebx +; mov ax, es +; shl eax, 4 +; mov bx, di +; add eax, ebx +; movzx ebx, word[es:di] +; add eax, ebx +; push 0x0000 +; pop es +; mov [es:0x9014], eax + .exit: + ret + +;============================================================================= +;============================================================================= +;============================================================================= + diff --git a/kernel/branches/kolibri-process/boot/et.inc b/kernel/branches/kolibri-process/boot/et.inc new file mode 100644 index 000000000..f01d7ae58 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/et.inc @@ -0,0 +1,16 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3927 $ + + +; Full ASCII code font +; only õ,ä,ü added +; Kaitz +ET_FNT: + fontfile file "ETFONT.FNT" + diff --git a/kernel/branches/kolibri-process/boot/parsers.inc b/kernel/branches/kolibri-process/boot/parsers.inc new file mode 100644 index 000000000..78c4284a8 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/parsers.inc @@ -0,0 +1,170 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2288 $ + +; All parsers are called with ds:si -> value of the variable, +; possibly with spaces before, and dx = limit of config file. + +; Three subroutines parse_char, parse_number and parse_bool set CF +; if something has failed, otherwise return the value in al/ax. + +parse_timeout: +; timeout is a number not greater than 9 + call parse_number + jc .nothing + cmp ax, 9 + jbe @f + mov ax, 9 +@@: + imul ax, 18 + mov [es:preboot_timeout], ax +.nothing: + ret + +parse_resolution: +; resolution is *, 'x' can be used instead of '*' +; parse width + call parse_number + jc .nothing +; save width + xchg ax, bx +; test for 'x' or '*' + call parse_char + cmp al, 'x' + jz @f + cmp al, '*' + jnz .nothing +@@: +; parse height + call parse_number + jc .nothing +; write width and height + mov [es:x_save], bx + mov [es:y_save], ax +.nothing: + ret + +parse_vbemode: +; vbemode is a number + call parse_number + jc .nothing + mov [es:number_vm], ax +.nothing: + ret + +;parse_vrr: +;; vrr is a boolean setting +; call parse_bool +; jc .nothing +;; convert 0 to 2, 1 to 1 +; inc ax +; xor al, 3 +; mov [es:preboot_vrrm], al +;.nothing: +; ret + +parse_biosdisks: +; using biosdisks is a boolean setting + call parse_bool + jc .nothing +; convert 0 to 2, 1 to 1 + inc ax + xor al, 3 + mov [es:preboot_biosdisk], al +.nothing: + ret + +parse_imgfrom: +; boot device (1-floppy 2-kolibri.img using primary loader) + call parse_number + jc .nothing + cmp al, 1 + jb .nothing + cmp al, 2 + ja .nothing + mov [es:preboot_device], al +.nothing: + ret + +parse_char: +; skip spaces and return the next character or CF if EOF. + cmp si, dx + jae .eof + lodsb + cmp al, ' ' + jbe parse_char + ret +.eof: + stc + ret + +parse_number: +; initialize high part of ax to zero + xor ax, ax +; skip spaces + call parse_char + jc .bad +; al should be a digit + sub al, '0' + cmp al, 9 + ja .bad +; accumulate the value in cx + xchg cx, ax +@@: + cmp si, dx + jae .eof + lodsb + sub al, '0' + cmp al, 9 + ja .end + imul cx, 10 + add cx, ax + jmp @b +; if the end is caused by non-digit, unwind the last character +.end: + dec si +.eof: + xchg cx, ax + clc + ret +.bad: + stc + ret + +parse_bool: +; skip spaces + call parse_char + jc .bad +; Boolean false can be represented as 0=no=off, +; boolean true can be represented as 1=yes=on. + cmp al, '0' + jz .false + cmp al, '1' + jz .true + mov ah, al + cmp si, dx + jae .bad + lodsb + cmp ax, 'n'*256 + 'o' + jz .false + cmp ax, 'o'*256 + 'f' + jz .false + cmp ax, 'y'*256 + 'e' + jz .true + cmp ax, 'o'*256 + 'n' + jz .true +.bad: + stc + ret +.true: + xor ax, ax + inc ax + ret +.false: + xor ax, ax + ret diff --git a/kernel/branches/kolibri-process/boot/preboot.inc b/kernel/branches/kolibri-process/boot/preboot.inc new file mode 100644 index 000000000..0c6223a51 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/preboot.inc @@ -0,0 +1,44 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3777 $ + + +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 ) + +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_debug db 0 ; load kernel in debug mode? (1-yes, 2-no) +preboot_launcher db 0 ; start launcher after kernel is loaded? (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) +if defined extended_primary_loader +preboot_timeout dw 5*18 ; timeout in 1/18th of second for config settings screen +end if + + if $>0x200 +ERROR: + prebooting parameters must fit in first sector!!! + end if +hdsysimage db 'KOLIBRI.IMG',0 ; load from +image_save db 'KOLIBRI.IMG',0 ; save to diff --git a/kernel/branches/kolibri-process/boot/rdload.inc b/kernel/branches/kolibri-process/boot/rdload.inc new file mode 100644 index 000000000..be5775ddb --- /dev/null +++ b/kernel/branches/kolibri-process/boot/rdload.inc @@ -0,0 +1,123 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3744 $ + + +read_ramdisk: +; READ RAMDISK IMAGE FROM HD + + cmp [boot_dev+OS_BASE+0x10000], 1 + jne no_sys_on_hd + + xor ebp, ebp +.hd_loop: + lea eax, [ebp+'0'] + mov [read_image_fsinfo.name_digit], al + movzx eax, byte [DRIVE_DATA+2+ebp] + test eax, eax + jz .next_hd + push eax + mov esi, 1 +.partition_loop: + mov eax, esi + push -'0' +@@: + xor edx, edx + div [_10] + push edx + test eax, eax + jnz @b + mov edi, read_image_fsinfo.partition +@@: + pop eax + add al, '0' + stosb + jnz @b + mov byte [edi-1], '/' + push esi edi + mov esi, bootpath1 + mov ecx, bootpath1.len + rep movsb + call read_image + test eax, eax + jz .yes + cmp eax, 6 + jz .yes + pop edi + push edi + mov esi, bootpath2 + mov ecx, bootpath2.len + rep movsb + call read_image + test eax, eax + jz .yes + cmp eax, 6 + jz .yes + pop edi esi + inc esi + cmp esi, [esp] + jbe .partition_loop + pop eax +.next_hd: + inc ebp + cmp ebp, 4 + jb .hd_loop + jmp no_sys_on_hd +.yes: + pop edi esi eax + jmp yes_sys_on_hd + +iglobal +align 4 +read_image_fsinfo: + dd 0 ; function: read + dq 0 ; offset: zero + dd 1474560 ; size + dd RAMDISK ; buffer + db '/hd' +.name_digit db '0' + db '/' +.partition: + rb 64 ; should be enough for '255/KOLIBRI/KOLIBRI.IMG' + +bootpath1 db 'KOLIBRI.IMG',0 +.len = $ - bootpath1 +bootpath2 db 'KOLIBRI/KOLIBRI.IMG',0 +.len = $ - bootpath2 +endg + +read_image: + mov ebx, read_image_fsinfo + pushad + call file_system_lfn_protected + popad + ret + +no_sys_on_hd: + ; test_to_format_ram_disk (need if not using ram disk) + cmp [boot_dev+OS_BASE+0x10000], 3 + jne not_format_ram_disk + ; format_ram_disk + mov edi, RAMDISK + mov ecx, 0x1080 + xor eax, eax +@@: + stosd + loop @b + + mov ecx, 0x58F7F + mov eax, 0xF6F6F6F6 +@@: + stosd + loop @b + + mov [RAMDISK+0x200], dword 0xFFFFF0 ; fat table + mov [RAMDISK+0x4200], dword 0xFFFFF0 + +not_format_ram_disk: +yes_sys_on_hd: diff --git a/kernel/branches/kolibri-process/boot/ru.inc b/kernel/branches/kolibri-process/boot/ru.inc new file mode 100644 index 000000000..c17c197b9 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/ru.inc @@ -0,0 +1,102 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3539 $ + + +; Generated by RUFNT.EXE +; By BadBugsKiller (C) +; Modifyed by BadBugsKiller 12.01.2004 17:45 +; Шрифт уменьшен в размере и теперь состоит из 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 + db 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0x66, 0x62, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xFF, 0xC3, 0x81, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xDB, 0xDB, 0x5A, 0x5A, 0x7E, 0x7E, 0x5A, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x78, 0x78, 0x6C, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x1F, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xCF, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + + db 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x7C, 0x38, 0x38, 0x7C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0xF8, 0xF0, 0xB0, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x7E, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xF3, 0xDB, 0xDB, 0xDB, 0xDB, 0xF3, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x26, 0x3E, 0x26, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xCE, 0xDB, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xDB, 0xDB, 0xCE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x3F, 0x66, 0x66, 0x66, 0x3E, 0x3E, 0x66, 0x66, 0x66, 0xE7, 0x00, 0x00, 0x00, 0x00 + + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x02, 0x06, 0x7C, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x62, 0x62, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0xFF, 0xC3, 0xC3, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xFE, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0x54, 0x7C, 0x54, 0xD6, 0xD6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x38, 0xC6, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xD6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + +RU_FNT2: + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00 + db 0x00, 0x00, 0x00, 0x3C, 0x18, 0x7E, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x18, 0x3C, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB0, 0xB0, 0x3E, 0x33, 0x33, 0x7E, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xF6, 0xDE, 0xDE, 0xF6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x3E, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xCE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC6, 0xC6, 0x7E, 0x36, 0x66, 0xE7, 0x00, 0x00, 0x00, 0x00 + + db 0x6C, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xFC, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC8, 0xF8, 0xC8, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xF8, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00 + db 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xCF, 0xCD, 0xEF, 0xEC, 0xFF, 0xDC, 0xDC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/kernel/branches/kolibri-process/boot/shutdown.inc b/kernel/branches/kolibri-process/boot/shutdown.inc new file mode 100644 index 000000000..19634ac00 --- /dev/null +++ b/kernel/branches/kolibri-process/boot/shutdown.inc @@ -0,0 +1,212 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Shutdown for Menuet ;; +;; ;; +;; Distributed under General Public License ;; +;; See file COPYING for details. ;; +;; Copyright 2003 Ville Turjanmaa ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +align 4 +pr_mode_exit: + +; setup stack + mov ax, 0x3000 + mov ss, ax + mov esp, 0x0EC00 +; setup ds + push cs + pop ds + + lidt [old_ints_h] +;remap IRQs + mov al, 0x11 + out 0x20, al + call rdelay + out 0xA0, al + call rdelay + + mov al, 0x08 + out 0x21, al + call rdelay + mov al, 0x70 + out 0xA1, al + call rdelay + + mov al, 0x04 + out 0x21, al + call rdelay + mov al, 0x02 + out 0xA1, al + call rdelay + + mov al, 0x01 + out 0x21, al + call rdelay + out 0xA1, al + call rdelay + + mov al, 0xB8 + out 0x21, al + call rdelay + mov al, 0xBD + out 0xA1, al + sti + +temp_3456: + xor ax, ax + mov es, ax + mov al, byte [es:0x9030] + cmp al, 1 + jl nbw + cmp al, 4 + jle nbw32 + +nbw: + in al, 0x60 + cmp al, 6 + jae nbw + mov bl, al +nbw2: + in al, 0x60 + cmp al, bl + je nbw2 + cmp al, 240;ax,240 + jne nbw31 + mov al, bl + dec ax + jmp nbw32 +nbw31: + add bl, 128 + cmp al, bl + jne nbw + sub al, 129 + +nbw32: + + dec ax + dec ax ; 2 = power off + jnz no_apm_off + call APM_PowerOff + jmp $ +no_apm_off: + +if ~ defined extended_primary_loader ; kernel restarting is not supported + dec ax ; 3 = reboot + jnz restart_kernel ; 4 = restart kernel +end if + push 0x40 + pop ds + mov word[0x0072], 0x1234 + jmp 0xF000:0xFFF0 + + +rdelay: + ret + +APM_PowerOff: + mov ax, 5304h + xor bx, bx + int 15h +;!!!!!!!!!!!!!!!!!!!!!!!! + mov ax, 0x5300 + xor bx, bx + int 0x15 + push ax + + mov ax, 0x5301 + xor bx, bx + int 0x15 + + mov ax, 0x5308 + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x530E + xor bx, bx + pop cx + int 0x15 + + mov ax, 0x530D + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x530F + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x5307 + mov bx, 1 + mov cx, 3 + int 0x15 +;!!!!!!!!!!!!!!!!!!!!!!!! + ret + +if ~ defined extended_primary_loader +restart_kernel: + + mov ax, 0x0003 ; set text mode for screen + int 0x10 + jmp 0x4000:0000 + +restart_kernel_4000: + cli + + push ds + pop es + mov cx, 0x8000 + push cx + push 0x7000 + pop ds + xor si, si + xor di, di + rep movsw + pop cx + mov ds, cx + push 0x2000 + pop es + rep movsw + push 0x9000 + pop ds + push 0x3000 + pop es + mov cx, 0xE000/2 + rep movsw + + wbinvd ; write and invalidate cache + + mov al, 00110100b + out 43h, al + jcxz $+2 + mov al, 0xFF + out 40h, al + jcxz $+2 + out 40h, al + jcxz $+2 + sti + +; (hint by Black_mirror) +; We must read data from keyboard port, +; because there may be situation when previous keyboard interrupt is lost +; (due to return to real mode and IRQ reprogramming) +; and next interrupt will not be generated (as keyboard waits for handling) + in al, 0x60 + +; bootloader interface + push 0x1000 + pop ds + mov si, kernel_restart_bootblock + mov ax, 'KL' + jmp 0x1000:0000 +end if + diff --git a/kernel/branches/kolibri-process/bus/pci/PCIe.inc b/kernel/branches/kolibri-process/bus/pci/PCIe.inc new file mode 100644 index 000000000..213b6b681 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/pci/PCIe.inc @@ -0,0 +1,119 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2010-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ;; +;; PCIe.INC ;; +;; ;; +;; Extended PCI express services ;; +;; ;; +;; art_zh ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 1463 $ + +;*************************************************************************** +; Function +; pci_ext_config: +; +; Description +; PCIe extended (memory-mapped) config space detection +; +; WARNINGs: +; 1) Very Experimental! +; 2) direct HT-detection (no ACPI or BIOS service used) +; 3) Only AMD/HT processors currently supported +; +;*************************************************************************** + +PCIe_CONFIG_SPACE equ 0xF0000000 ; to be moved to const.inc +mmio_pcie_cfg_addr dd 0x0 ; intel pcie space may be defined here +mmio_pcie_cfg_lim dd 0x0 ; upper pcie space address + + +align 4 + +pci_ext_config: + + mov ebx, [mmio_pcie_cfg_addr] + or ebx, ebx + jz @f + or ebx, 0x7FFFFFFF ; required by PCI-SIG standards + jnz .pcie_failed + add ebx, 0x0FFFFC + cmp ebx, [mmio_pcie_cfg_lim]; is the space limit correct? + ja .pcie_failed + jmp .pcie_cfg_mapped +@@: + mov ebx, [cpu_vendor] + cmp ebx, dword [AMD_str] + jne .pcie_failed + mov bx, 0xC184 ; dev = 24, fn = 01, reg = 84h + +.check_HT_mmio: + mov cx, bx + mov ax, 0x0002 ; bus = 0, 1dword to read + call pci_read_reg + mov bx, cx + sub bl, 4 + and al, 0x80 ; check the NP bit + jz .no_pcie_cfg + shl eax, 8 ; bus:[27..20], dev:[19:15] + or eax, 0x00007FFC ; fun:[14..12], reg:[11:2] + mov [mmio_pcie_cfg_lim], eax + mov cl, bl + mov ax, 0x0002 ; bus = 0, 1dword to read + call pci_read_reg + mov bx, cx + test al, 0x03 ; MMIO Base RW enabled? + jz .no_pcie_cfg + test al, 0x0C ; MMIO Base locked? + jnz .no_pcie_cfg + xor al, al + shl eax, 8 + test eax, 0x000F0000 ; MMIO Base must be bus0-aligned + jnz .no_pcie_cfg + mov [mmio_pcie_cfg_addr], eax + add eax, 0x000FFFFC + sub eax, [mmio_pcie_cfg_lim]; MMIO must cover at least one bus + ja .no_pcie_cfg + +; -- it looks like a true PCIe config space; + mov eax, [mmio_pcie_cfg_addr] ; physical address + or eax, (PG_SHARED + PG_LARGE + PG_USER) + mov ebx, PCIe_CONFIG_SPACE ; linear address + mov ecx, ebx + shr ebx, 20 + add ebx, sys_pgdir ; PgDir entry @ +@@: + mov dword[ebx], eax ; map 4 buses + invlpg [ecx] + cmp bl, 4 + jz .pcie_cfg_mapped ; fix it later + add bl, 4 ; next PgDir entry + add eax, 0x400000 ; eax += 4M + add ecx, 0x400000 + jmp @b + +.pcie_cfg_mapped: + +; -- glad to have the extended PCIe config field found +; mov esi, boot_pcie_ok +; call boot_log + ret ; <<<<<<<<<<< OK >>>>>>>>>>> + +.no_pcie_cfg: + + xor eax, eax + mov [mmio_pcie_cfg_addr], eax + mov [mmio_pcie_cfg_lim], eax + add bl, 12 + cmp bl, 0xC0 ; MMIO regs lay below this offset + jb .check_HT_mmio +.pcie_failed: +; mov esi, boot_pcie_fail +; call boot_log + ret ; <<<<<<<<< FAILURE >>>>>>>>> + diff --git a/kernel/branches/kolibri-process/bus/pci/pci16.inc b/kernel/branches/kolibri-process/bus/pci/pci16.inc new file mode 100644 index 000000000..b37d27421 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/pci/pci16.inc @@ -0,0 +1,51 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; PCI16.INC ;; +;; ;; +;; 16 bit PCI driver code ;; +;; ;; +;; Version 0.2 December 21st, 2002 ;; +;; ;; +;; Author: Victor Prodan, victorprodan@yahoo.com ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +init_pci_16: + + pushad + + xor ax, ax + mov es, ax + mov byte [es:0x9020], 1;default mechanism:1 + mov ax, 0xb101 + int 0x1a + or ah, ah + jnz pci16skip + + mov [es:0x9021], cl;last PCI bus in system + mov [es:0x9022], bx + mov [es:0x9024], edi + +; we have a PCI BIOS, so check which configuration mechanism(s) +; it supports +; AL = PCI hardware characteristics (bit0 => mechanism1, bit1 => mechanism2) + test al, 1 + jnz pci16skip + test al, 2 + jz pci16skip + mov byte [es:0x9020], 2; if (al&3)==2 => mechanism 2 + +pci16skip: + + mov ax, 0x1000 + mov es, ax + + popad diff --git a/kernel/branches/kolibri-process/bus/pci/pci32.inc b/kernel/branches/kolibri-process/bus/pci/pci32.inc new file mode 100644 index 000000000..b23abd635 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/pci/pci32.inc @@ -0,0 +1,724 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ;; +;; PCI32.INC ;; +;; ;; +;; 32 bit PCI driver code ;; +;; ;; +;; Version 0.3 April 9, 2007 ;; +;; Version 0.2 December 21st, 2002 ;; +;; ;; +;; Author: Victor Prodan, victorprodan@yahoo.com ;; +;; Mihailov Ilia, ghost.nsk@gmail.com ;; +;; Credits: ;; +;; Ralf Brown ;; +;; Mike Hibbett, mikeh@oceanfree.net ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4418 $ + +;*************************************************************************** +; Function +; pci_api: +; +; Description +; entry point for system PCI calls +;*************************************************************************** +;mmio_pci_addr equ 0x400 ; set actual PCI address here to activate user-MMIO + +iglobal +align 4 +f62call: + dd pci_fn_0 + dd pci_fn_1 + dd pci_fn_2 + dd pci_service_not_supported ;3 + dd pci_read_reg ;4 byte + dd pci_read_reg ;5 word + dd pci_read_reg ;6 dword + dd pci_service_not_supported ;7 + dd pci_write_reg ;8 byte + dd pci_write_reg ;9 word + dd pci_write_reg ;10 dword +if defined mmio_pci_addr + dd pci_mmio_init ;11 + dd pci_mmio_map ;12 + dd pci_mmio_unmap ;13 +end if + +endg + +align 4 + +pci_api: + +;cross + mov eax, ebx + mov ebx, ecx + mov ecx, edx + + cmp [pci_access_enabled], 1 + jne pci_service_not_supported + + movzx edx, al + +if defined mmio_pci_addr + cmp al, 13 + ja pci_service_not_supported +else + cmp al, 10 + ja pci_service_not_supported +end if + + call dword [f62call+edx*4] + mov dword [esp+32], eax + ret + + +align 4 +pci_api_drv: + + cmp [pci_access_enabled], 1 + jne .fail + + cmp eax, 2 + ja .fail + + jmp dword [f62call+eax*4] + +.fail: + or eax, -1 + ret + + +;; ============================================ + +pci_fn_0: +; PCI function 0: get pci version (AH.AL) + movzx eax, word [BOOT_VARS+0x9022] + ret + +pci_fn_1: +; PCI function 1: get last bus in AL + mov al, [BOOT_VARS+0x9021] + ret + +pci_fn_2: +; PCI function 2: get pci access mechanism + mov al, [BOOT_VARS+0x9020] + ret + +pci_service_not_supported: + or eax, -1 + mov dword [esp+32], eax + ret + +;*************************************************************************** +; Function +; pci_make_config_cmd +; +; Description +; creates a command dword for use with the PCI bus +; bus # in ah +; device+func in bh (dddddfff) +; register in bl +; +; command dword returned in eax ( 10000000 bbbbbbbb dddddfff rrrrrr00 ) +;*************************************************************************** + +align 4 + +pci_make_config_cmd: + shl eax, 8 ; move bus to bits 16-23 + mov ax, bx ; combine all + and eax, 0xffffff + or eax, 0x80000000 + ret + +;*************************************************************************** +; Function +; pci_read_reg: +; +; Description +; read a register from the PCI config space into EAX/AX/AL +; IN: ah=bus,device+func=bh,register address=bl +; number of bytes to read (1,2,4) coded into AL, bits 0-1 +; (0 - byte, 1 - word, 2 - dword) +;*************************************************************************** + +align 4 + +pci_read_reg: + push ebx esi + cmp byte [BOOT_VARS+0x9020], 2;what mechanism will we use? + je pci_read_reg_2 + + ; mechanism 1 + mov esi, eax ; save register size into ESI + and esi, 3 + + call pci_make_config_cmd + mov ebx, eax + ; get current state + mov dx, 0xcf8 + in eax, dx + push eax + ; set up addressing to config data + mov eax, ebx + and al, 0xfc; make address dword-aligned + out dx, eax + ; get requested DWORD of config data + mov dl, 0xfc + and bl, 3 + or dl, bl ; add to port address first 2 bits of register address + + or esi, esi + jz pci_read_byte1 + cmp esi, 1 + jz pci_read_word1 + cmp esi, 2 + jz pci_read_dword1 + jmp pci_fin_read1 + +pci_read_byte1: + in al, dx + jmp pci_fin_read1 +pci_read_word1: + in ax, dx + jmp pci_fin_read1 +pci_read_dword1: + in eax, dx + jmp pci_fin_read1 +pci_fin_read1: + ; restore configuration control + xchg eax, [esp] + mov dx, 0xcf8 + out dx, eax + + pop eax + pop esi ebx + ret +pci_read_reg_2: + + test bh, 128 ;mech#2 only supports 16 devices per bus + jnz pci_read_reg_err + + mov esi, eax ; save register size into ESI + and esi, 3 + + push eax + ;store current state of config space + mov dx, 0xcf8 + in al, dx + mov ah, al + mov dl, 0xfa + in al, dx + + xchg eax, [esp] + ; out 0xcfa,bus + mov al, ah + out dx, al + ; out 0xcf8,0x80 + mov dl, 0xf8 + mov al, 0x80 + out dx, al + ; compute addr + shr bh, 3; func is ignored in mechanism 2 + or bh, 0xc0 + mov dx, bx + + or esi, esi + jz pci_read_byte2 + cmp esi, 1 + jz pci_read_word2 + cmp esi, 2 + jz pci_read_dword2 + jmp pci_fin_read2 + +pci_read_byte2: + in al, dx + jmp pci_fin_read2 +pci_read_word2: + in ax, dx + jmp pci_fin_read2 +pci_read_dword2: + in eax, dx +; jmp pci_fin_read2 +pci_fin_read2: + + ; restore configuration space + xchg eax, [esp] + mov dx, 0xcfa + out dx, al + mov dl, 0xf8 + mov al, ah + out dx, al + + pop eax + pop esi ebx + ret + +pci_read_reg_err: + xor eax, eax + dec eax + pop esi ebx + ret + + +;*************************************************************************** +; Function +; pci_write_reg: +; +; Description +; write a register from ECX/CX/CL into the PCI config space +; IN: ah=bus,device+func=bh,register address (dword aligned)=bl, +; value to write in ecx +; number of bytes to write (1,2,4) coded into AL, bits 0-1 +; (0 - byte, 1 - word, 2 - dword) +;*************************************************************************** + +align 4 + +pci_write_reg: + push esi ebx + cmp byte [BOOT_VARS+0x9020], 2;what mechanism will we use? + je pci_write_reg_2 + + ; mechanism 1 + mov esi, eax ; save register size into ESI + and esi, 3 + + call pci_make_config_cmd + mov ebx, eax + ; get current state into ecx + mov dx, 0xcf8 + in eax, dx + push eax + ; set up addressing to config data + mov eax, ebx + and al, 0xfc; make address dword-aligned + out dx, eax + ; write DWORD of config data + mov dl, 0xfc + and bl, 3 + or dl, bl + mov eax, ecx + + or esi, esi + jz pci_write_byte1 + cmp esi, 1 + jz pci_write_word1 + cmp esi, 2 + jz pci_write_dword1 + jmp pci_fin_write1 + +pci_write_byte1: + out dx, al + jmp pci_fin_write1 +pci_write_word1: + out dx, ax + jmp pci_fin_write1 +pci_write_dword1: + out dx, eax + jmp pci_fin_write1 +pci_fin_write1: + + ; restore configuration control + pop eax + mov dl, 0xf8 + out dx, eax + + xor eax, eax + pop ebx esi + + ret +pci_write_reg_2: + + test bh, 128 ;mech#2 only supports 16 devices per bus + jnz pci_write_reg_err + + + mov esi, eax ; save register size into ESI + and esi, 3 + + push eax + ;store current state of config space + mov dx, 0xcf8 + in al, dx + mov ah, al + mov dl, 0xfa + in al, dx + xchg eax, [esp] + ; out 0xcfa,bus + mov al, ah + out dx, al + ; out 0xcf8,0x80 + mov dl, 0xf8 + mov al, 0x80 + out dx, al + ; compute addr + shr bh, 3; func is ignored in mechanism 2 + or bh, 0xc0 + mov dx, bx + ; write register + mov eax, ecx + + or esi, esi + jz pci_write_byte2 + cmp esi, 1 + jz pci_write_word2 + cmp esi, 2 + jz pci_write_dword2 + jmp pci_fin_write2 + +pci_write_byte2: + out dx, al + jmp pci_fin_write2 +pci_write_word2: + out dx, ax + jmp pci_fin_write2 +pci_write_dword2: + out dx, eax + jmp pci_fin_write2 +pci_fin_write2: + ; restore configuration space + pop eax + mov dx, 0xcfa + out dx, al + mov dl, 0xf8 + mov al, ah + out dx, al + + xor eax, eax + pop ebx esi + ret + +pci_write_reg_err: + xor eax, eax + dec eax + pop ebx esi + ret + +if defined mmio_pci_addr ; must be set above +;*************************************************************************** +; Function +; pci_mmio_init +; +; Description +; IN: bx = device's PCI bus address (bbbbbbbbdddddfff) +; Returns eax = user heap space available (bytes) +; Error codes +; eax = -1 : PCI user access blocked, +; eax = -2 : device not registered for uMMIO service +; eax = -3 : user heap initialization failure +;*************************************************************************** +pci_mmio_init: + cmp bx, mmio_pci_addr + jz @f + mov eax, -2 + ret +@@: + call init_heap ; (if not initialized yet) + or eax, eax + jz @f + ret +@@: + mov eax, -3 + ret + + +;*************************************************************************** +; Function +; pci_mmio_map +; +; Description +; maps a block of PCI memory to user-accessible linear address +; +; WARNING! This VERY EXPERIMENTAL service is for one chosen PCI device only! +; The target device address should be set in kernel var mmio_pci_addr +; +; IN: ah = BAR#; +; IN: ebx = block size (bytes); +; IN: ecx = offset in MMIO block (in 4K-pages, to avoid misaligned pages); +; +; Returns eax = MMIO block's linear address in the userspace (if no error) +; +; +; Error codes +; eax = -1 : user access to PCI blocked, +; eax = -2 : an invalid BAR register referred +; eax = -3 : no i/o space on that BAR +; eax = -4 : a port i/o BAR register referred +; eax = -5 : dynamic userspace allocation problem +;*************************************************************************** + +pci_mmio_map: + and edx, 0x0ffff + cmp ah, 6 + jc .bar_0_5 + jz .bar_rom + mov eax, -2 + ret +.bar_rom: + mov ah, 8 ; bar6 = Expansion ROM base address +.bar_0_5: + push ecx + add ebx, 4095 + and ebx, -4096 + push ebx + mov bl, ah ; bl = BAR# (0..5), however bl=8 for BAR6 + shl bl, 1 + shl bl, 1 + add bl, 0x10; now bl = BAR offset in PCI config. space + mov ax, mmio_pci_addr + mov bh, al ; bh = dddddfff + mov al, 2 ; al : DW to read + call pci_read_reg + or eax, eax + jnz @f + mov eax, -3 ; empty I/O space + jmp mmio_ret_fail +@@: + test eax, 1 + jz @f + mov eax, -4 ; damned ports (not MMIO space) + jmp mmio_ret_fail +@@: + pop ecx ; ecx = block size, bytes (expanded to whole page) + mov ebx, ecx; user_alloc destroys eax, ecx, edx, but saves ebx + and eax, 0xFFFFFFF0 + push eax ; store MMIO physical address + keep 2DWords in the stack + stdcall user_alloc, ecx + or eax, eax + jnz mmio_map_over + mov eax, -5 ; problem with page allocation + +mmio_ret_fail: + pop ecx + pop edx + ret + +mmio_map_over: + mov ecx, ebx; ecx = size (bytes, expanded to whole page) + shr ecx, 12 ; ecx = number of pages + mov ebx, eax; ebx = linear address + pop eax ; eax = MMIO start + pop edx ; edx = MMIO shift (pages) + shl edx, 12 ; edx = MMIO shift (bytes) + add eax, edx; eax = uMMIO physical address + or eax, PG_SHARED + or eax, PG_UW + or eax, PG_NOCACHE + mov edi, ebx + call commit_pages + mov eax, edi + ret + +;*************************************************************************** +; Function +; pci_mmio_unmap_page +; +; Description +; unmaps the linear space previously tied to a PCI memory block +; +; IN: ebx = linear address of space previously allocated by pci_mmio_map +; returns eax = 1 if successfully unmapped +; +; Error codes +; eax = -1 if no user PCI access allowed, +; eax = 0 if unmapping failed +;*************************************************************************** + +pci_mmio_unmap: + stdcall user_free, ebx + ret + +end if + +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +uglobal +align 4 +; VendID (2), DevID (2), Revision = 0 (1), Class Code (3), FNum (1), Bus (1) +pci_emu_dat: + times 30*10 db 0 +endg +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +align 4 +sys_pcibios: + cmp [pci_access_enabled], 1 + jne .unsupported_func + cmp [pci_bios_entry], 0 + jz .emulate_bios + + push ds + mov ax, pci_data_sel + mov ds, ax + mov eax, ebp + mov ah, 0B1h + call pword [cs:pci_bios_entry] + pop ds + + jmp .return + ;-=-=-=-=-=-=-=-= +.emulate_bios: + cmp ebp, 1 ; PCI_FUNCTION_ID + jnz .not_PCI_BIOS_PRESENT + mov edx, 'PCI ' + mov al, [BOOT_VARS + 0x9020] + mov bx, [BOOT_VARS + 0x9022] + mov cl, [BOOT_VARS + 0x9021] + xor ah, ah + jmp .return_abcd + +.not_PCI_BIOS_PRESENT: + cmp ebp, 2 ; FIND_PCI_DEVICE + jne .not_FIND_PCI_DEVICE + mov ebx, pci_emu_dat +..nxt: + cmp [ebx], dx + jne ..no + cmp [ebx + 2], cx + jne ..no + dec si + jns ..no + mov bx, [ebx + 4] + xor ah, ah + jmp .return_ab +..no: + cmp word[ebx], 0 + je ..dev_not_found + add ebx, 10 + jmp ..nxt +..dev_not_found: + mov ah, 0x86 ; DEVICE_NOT_FOUND + jmp .return_a + +.not_FIND_PCI_DEVICE: + cmp ebp, 3 ; FIND_PCI_CLASS_CODE + jne .not_FIND_PCI_CLASS_CODE + mov esi, pci_emu_dat + shl ecx, 8 +..nxt2: + cmp [esi], ecx + jne ..no2 + mov bx, [esi] + xor ah, ah + jmp .return_ab +..no2: + cmp dword[esi], 0 + je ..dev_not_found + add esi, 10 + jmp ..nxt2 + +.not_FIND_PCI_CLASS_CODE: + cmp ebp, 8 ; READ_CONFIG_* + jb .not_READ_CONFIG + cmp ebp, 0x0A + ja .not_READ_CONFIG + mov eax, ebp + mov ah, bh + mov edx, edi + mov bh, bl + mov bl, dl + call pci_read_reg + mov ecx, eax + xor ah, ah ; SUCCESSFUL + jmp .return_abc +.not_READ_CONFIG: + cmp ebp, 0x0B ; WRITE_CONFIG_* + jb .not_WRITE_CONFIG + cmp ebp, 0x0D + ja .not_WRITE_CONFIG + lea eax, [ebp+1] + mov ah, bh + mov edx, edi + mov bh, bl + mov bl, dl + call pci_write_reg + xor ah, ah ; SUCCESSFUL + jmp .return_abc +.not_WRITE_CONFIG: +.unsupported_func: + mov ah, 0x81 ; FUNC_NOT_SUPPORTED +.return: + mov dword[esp + 4 ], edi + mov dword[esp + 8], esi +.return_abcd: + mov dword[esp + 24], edx +.return_abc: + mov dword[esp + 28], ecx +.return_ab: + mov dword[esp + 20], ebx +.return_a: + mov dword[esp + 32], eax + ret + +proc pci_enum + push ebp + mov ebp, esp + push 0 +virtual at ebp-4 +.devfn db ? +.bus db ? +end virtual +.loop: + mov ah, [.bus] + mov al, 2 + mov bh, [.devfn] + mov bl, 0 + call pci_read_reg + cmp eax, 0xFFFFFFFF + jnz .has_device + test byte [.devfn], 7 + jnz .next_func + jmp .no_device +.has_device: + push eax + movi eax, sizeof.PCIDEV + call malloc + pop ecx + test eax, eax + jz .nomemory + mov edi, eax + mov [edi+PCIDEV.vendor_device_id], ecx + mov eax, pcidev_list + mov ecx, [eax+PCIDEV.bk] + mov [edi+PCIDEV.bk], ecx + mov [edi+PCIDEV.fd], eax + mov [ecx+PCIDEV.fd], edi + mov [eax+PCIDEV.bk], edi + mov eax, dword [.devfn] + mov dword [edi+PCIDEV.devfn], eax + mov dword [edi+PCIDEV.owner], 0 + mov bh, al + mov al, 2 + mov bl, 8 + call pci_read_reg + shr eax, 8 + mov [edi+PCIDEV.class], eax + test byte [.devfn], 7 + jnz .next_func + mov ah, [.bus] + mov al, 0 + mov bh, [.devfn] + mov bl, 0Eh + call pci_read_reg + test al, al + js .next_func +.no_device: + or byte [.devfn], 7 +.next_func: + inc dword [.devfn] + mov ah, [.bus] + cmp ah, [BOOT_VARS+0x9021] + jbe .loop +.nomemory: + leave + ret +endp diff --git a/kernel/branches/kolibri-process/bus/usb/common.inc b/kernel/branches/kolibri-process/bus/usb/common.inc new file mode 100644 index 000000000..d77e687a4 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/common.inc @@ -0,0 +1,446 @@ +; Constants and structures that are shared between different parts of +; USB subsystem and *HCI drivers. + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Version of all structures related to host controllers. +; Must be the same in kernel and *hci-drivers. +USBHC_VERSION = 1 + +; 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 + +; 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 + +; Possible speeds of USB devices +USB_SPEED_FS = 0 ; full-speed +USB_SPEED_LS = 1 ; low-speed +USB_SPEED_HS = 2 ; high-speed + +; 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 ================================= +; ============================================================================= + +; Description of controller-specific data and functions. +struct usb_hardware_func +Version dd ? ; must be USBHC_VERSION +ID dd ? ; '*HCI' +DataSize dd ? ; sizeof(*hci_controller) +BeforeInit dd ? +; Early initialization: take ownership from BIOS. +; in: [ebp-4] = (bus shl 8) + devfn +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 + +; pointers to kernel API functions that are called from *HCI-drivers +struct usbhc_func +usb_process_gtd dd ? +usb_init_static_endpoint dd ? +usb_wakeup_if_needed dd ? +usb_subscribe_control dd ? +usb_subscription_done dd ? +usb_allocate_common dd ? +usb_free_common dd ? +usb_td_to_virt dd ? +usb_init_transfer dd ? +usb_undo_tds dd ? +usb_test_pending_port dd ? +usb_get_tt dd ? +usb_get_tt_think_time dd ? +usb_new_device dd ? +usb_disconnect_stage2 dd ? +usb_process_wait_lists dd ? +usb_unlink_td dd ? +usb_is_final_packet dd ? +usb_find_ehci_companion dd ? +ends + +; 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. +PCICoordinates dd ? +; Device:function and bus number from PCI. +; +; 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. +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. +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 + +; 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 + +; 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. +TTHub dd ? +; Pointer to usb_hub for (the) hub with Transaction Translator for the device, +; NULL if the device operates in the same speed as the controller. +Port db ? +; Port on the hub, zero-based. +TTPort db ? +; Port on the TTHub, zero-based. +DeviceDescrSize db ? +; Size of device descriptor. +Speed db ? +; Device speed, one of USB_SPEED_*. +NumInterfaces dd ? +; Number of interfaces. +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 diff --git a/kernel/branches/kolibri-process/bus/usb/hccommon.inc b/kernel/branches/kolibri-process/bus/usb/hccommon.inc new file mode 100644 index 000000000..4a066859a --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/hccommon.inc @@ -0,0 +1,322 @@ +; USB Host Controller support code: hardware-independent part, +; common for all controller types. + +iglobal +; USB HC support: some functions interesting only for *HCI-drivers. +align 4 +usb_hc_func: + dd usb_process_gtd + dd usb_init_static_endpoint + dd usb_wakeup_if_needed + dd usb_subscribe_control + dd usb_subscription_done + dd usb_allocate_common + dd usb_free_common + dd usb_td_to_virt + dd usb_init_transfer + dd usb_undo_tds + dd usb_test_pending_port + dd usb_get_tt + dd usb_get_tt_think_time + dd usb_new_device + dd usb_disconnect_stage2 + dd usb_process_wait_lists + dd usb_unlink_td + dd usb_is_final_packet + dd usb_find_ehci_companion +endg + +; Initializes one controller, called by usb_init for every controller. +; 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 + mov edi, [eax+PCIDEV.owner] + test edi, edi + jz .nothing + mov edi, [edi+USBSRV.usb_func] +; 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 + mov eax, [ebp-4] + mov [edi+usb_controller.PCICoordinates-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 +@@: + movi edx, 4 + 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 + +; Called from USB1 controller-specific initialization. +; Finds EHCI companion controller for given USB1 controller. +; in: bl = PCI device:function for USB1 controller, bh = PCI bus +; out: eax -> usb_controller for EHCI companion +proc usb_find_ehci_companion +; 1. Loop over all registered controllers. + mov eax, usb_controllers_list +.next: + mov eax, [eax+usb_controller.Next] + cmp eax, usb_controllers_list + jz .notfound +; 2. For every controller, check the type, ignore everything that is not EHCI. + mov edx, [eax+usb_controller.HardwareFunc] + cmp [edx+usb_hardware_func.ID], 'EHCI' + jnz .next +; 3. For EHCI controller, compare PCI coordinates with input data: +; bus and device must be the same, function can be different. + mov edx, [eax+usb_controller.PCICoordinates] + xor edx, ebx + cmp dx, 8 + jae .next + ret +.notfound: + xor eax, eax + ret +endp + +; Find Transaction Translator hub and port for the given device. +; in: edx = parent hub for the device, ecx = port for the device +; out: edx = TT hub for the device, ecx = TT port for the device. +proc usb_get_tt +; If the parent hub is high-speed, it is TT for the device. +; Otherwise, the parent hub itself is behind TT, and the device +; has the same TT hub+port as the parent hub. + mov eax, [edx+usb_hub.ConfigPipe] + mov eax, [eax+usb_pipe.DeviceData] + cmp [eax+usb_device_data.Speed], USB_SPEED_HS + jz @f + movzx ecx, [eax+usb_device_data.TTPort] + mov edx, [eax+usb_device_data.TTHub] +@@: + mov edx, [edx+usb_hub.ConfigPipe] + ret +endp diff --git a/kernel/branches/kolibri-process/bus/usb/hub.inc b/kernel/branches/kolibri-process/bus/usb/hub.inc new file mode 100644 index 000000000..604807684 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/hub.inc @@ -0,0 +1,1275 @@ +; 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. +MaxPacketSize dd ? +; Maximum packet size for interrupt endpoint. +; Usually equals ceil((1+NumPorts)/8), but some hubs give additional bytes. +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 +; 1. Check that the maximal nesting is not exceeded: +; 5 non-root hubs is the maximum according to the spec. + mov ebx, [.pipe] + push 5 + mov eax, ebx +.count_parents: + mov eax, [eax+usb_pipe.DeviceData] + mov eax, [eax+usb_device_data.Hub] + test eax, eax + jz .depth_ok + mov eax, [eax+usb_hub.ConfigPipe] + dec dword [esp] + jnz .count_parents + pop eax + dbgstr 'Hub chain is too long' + jmp .return0 +.depth_ok: + pop eax +; Hubs use one IN interrupt endpoint for polling the device +; 2. Locate the descriptor of the interrupt endpoint. +; Loop over all descriptors owned by this interface. +.lookep: +; 2a. Skip the current descriptor. + movzx eax, [edx+usb_descr.bLength] + add edx, eax + sub ecx, eax + jb .errorep +; 2b. Length of data left must be at least sizeof.usb_endpoint_descr. + cmp ecx, sizeof.usb_endpoint_descr + jb .errorep +; 2c. 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 +; 2d. Ignore all interface-related descriptors except endpoint descriptor. + cmp [edx+usb_endpoint_descr.bDescriptorType], USB_ENDPOINT_DESCR + jnz .lookep +; 2e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr. + cmp [edx+usb_endpoint_descr.bLength], sizeof.usb_endpoint_descr + jb .errorep +; 2f. 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. +; 3. Allocate memory for the hub descriptor. +; Maximum length (assuming 255 downstream ports) is 40 bytes. +; Allocate 4 extra bytes to keep wMaxPacketSize. +; 3a. Save registers. + push edx +; 3b. Call the allocator. + movi eax, 44 + call malloc +; 3c. Restore registers. + pop ecx +; 3d. If failed, say something to the debug board and return error. + test eax, eax + jz .nomemory +; 3e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov. + xchg esi, eax +; 4. Open a pipe for the status endpoint with descriptor found in step 1. + movzx eax, [ecx+usb_endpoint_descr.bEndpointAddress] + movzx edx, [ecx+usb_endpoint_descr.bInterval] + movzx ecx, [ecx+usb_endpoint_descr.wMaxPacketSize] + test ecx, (1 shl 11) - 1 + jz .free + push ecx + stdcall usb_open_pipe, ebx, eax, ecx, INTERRUPT_PIPE, edx + pop ecx +; If failed, free the memory allocated in step 3, +; say something to the debug board and return error. + test eax, eax + jz .free +; 5. Send control query for the hub descriptor, +; pass status pipe as a callback parameter, +; allow short packets. + and ecx, (1 shl 11) - 1 + mov [esi+40], ecx + 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 +; 6. If failed, free the memory allocated in step 3, +; 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(floor(NumPorts/8)+1+MaxPacketSize,4)+8*NumPorts. + add edx, [eax+40] + add edx, sizeof.usb_hub - sizeof.usb_hub_descr + 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] + mov eax, [esi+40] + mov [ebx+usb_hub.MaxPacketSize], eax +; 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 + stdcall usb_normal_transfer_async, [eax+usb_hub.StatusPipe], \ + [eax+usb_hub.StatusChangePtr], [eax+usb_hub.MaxPacketSize], 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] + jbe .restart + 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 + +; Helper function for USB2 scheduler. +; in: eax -> usb_hub +; out: ecx = TT think time for the hub in FS-bytes +proc usb_get_tt_think_time + movzx ecx, [eax+usb_hub.HubCharacteristics] + shr ecx, 5 + and ecx, 3 + inc ecx + ret +endp diff --git a/kernel/branches/kolibri-process/bus/usb/init.inc b/kernel/branches/kolibri-process/bus/usb/init.inc new file mode 100644 index 000000000..5cc3bdaf0 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/init.inc @@ -0,0 +1,258 @@ +; 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. + +iglobal +uhci_service_name: + db 'UHCI',0 +ohci_service_name: + db 'OHCI',0 +ehci_service_name: + db 'EHCI',0 +endg + +; Initializes the USB subsystem. +proc usb_init +; 1. Initialize all locks. + mov ecx, usb_controllers_list_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 ebx, uhci_service_name + cmp byte [esi+PCIDEV.class], 0x00 + jz .do_kickoff + mov ebx, ohci_service_name + cmp byte [esi+PCIDEV.class], 0x10 + jz .do_kickoff + mov ebx, ehci_service_name + cmp byte [esi+PCIDEV.class], 0x20 + jnz .kickoff +.do_kickoff: + inc dword [esp] + push ebx esi + stdcall get_service, ebx + pop esi ebx + test eax, eax + jz .driver_fail + mov edx, [eax+USBSRV.usb_func] + cmp [edx+usb_hardware_func.Version], USBHC_VERSION + jnz .driver_invalid + mov [esi+PCIDEV.owner], eax + call [edx+usb_hardware_func.BeforeInit] + jmp .kickoff +.driver_fail: + DEBUGF 1,'K : failed to load driver %s\n',ebx + jmp .kickoff +.driver_invalid: + DEBUGF 1,'K : driver %s has wrong version\n',ebx + 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 + 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 + cmp [eax+PCIDEV.class], 0x0C0300 + jz @f + 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 + movi ebx, 1 + 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 "common.inc" +include "hccommon.inc" +include "pipe.inc" +include "protocol.inc" +include "hub.inc" diff --git a/kernel/branches/kolibri-process/bus/usb/memory.inc b/kernel/branches/kolibri-process/bus/usb/memory.inc new file mode 100644 index 000000000..8a24b656b --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/memory.inc @@ -0,0 +1,144 @@ +; 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. + +; 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: translate physical address in ecx +; of some transfer descriptor to linear address. +; in: eax = address of first page +proc usb_td_to_virt +; Traverse all pages used for transfer descriptors, looking for the one +; with physical address as in ecx. +@@: + 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-process/bus/usb/pipe.inc b/kernel/branches/kolibri-process/bus/usb/pipe.inc new file mode 100644 index 000000000..ab249d967 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/pipe.inc @@ -0,0 +1,675 @@ +; Functions for USB pipe manipulation: opening/closing, sending data etc. +; +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 +tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list +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 + mov 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 + +; Part of API for drivers, see documentation for USBGetParam. +proc usb_get_param +virtual at esp + dd ? ; return address +.pipe dd ? +.param dd ? +end virtual + mov edx, [.param] + mov ecx, [.pipe] + mov eax, [ecx+usb_pipe.DeviceData] + test edx, edx + jz .get_device_descriptor + dec edx + jz .get_config_descriptor + dec edx + jz .get_speed + or eax, -1 + ret 8 +.get_device_descriptor: + add eax, usb_device_data.DeviceDescriptor + ret 8 +.get_config_descriptor: + movzx ecx, [eax+usb_device_data.DeviceDescrSize] + lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] + ret 8 +.get_speed: + movzx eax, [eax+usb_device_data.Speed] + ret 8 +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 + +; One part of transfer is completed, run the associated callback +; or update total length in the next part of transfer. +; in: ebx -> usb_gtd, ecx = status, edx = length +proc usb_process_gtd +; 1. 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 +; 2. 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] + ret +.nocallback: +; 3. 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 + 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-process/bus/usb/protocol.inc b/kernel/branches/kolibri-process/bus/usb/protocol.inc new file mode 100644 index 000000000..6d720e380 --- /dev/null +++ b/kernel/branches/kolibri-process/bus/usb/protocol.inc @@ -0,0 +1,935 @@ +; 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 + +; 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. + movi eax, sizeof.usb_device_data + 8 + 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.TTHub], edi + mov [eax+usb_device_data.TTPort], 0 + mov [eax+usb_device_data.NumInterfaces], edi + mov [eax+usb_device_data.DeviceDescrSize], 0 + mov dl, [esi+usb_controller.ResettingSpeed] + mov [eax+usb_device_data.Speed], dl + mov [eax+usb_device_data.NumPipes], 1 + push ebx + cmp dl, USB_SPEED_HS + jz .nott + mov ebx, [esi+usb_controller.ResettingHub] + test ebx, ebx + jz .nott + mov cl, [esi+usb_controller.ResettingPort] + mov edx, [ebx+usb_hub.ConfigPipe] + mov edx, [edx+usb_pipe.DeviceData] + cmp [edx+usb_device_data.TTHub], 0 + jz @f + mov cl, [edx+usb_device_data.TTPort] + mov ebx, [edx+usb_device_data.TTHub] + jmp .has_tt +@@: + cmp [edx+usb_device_data.Speed], USB_SPEED_HS + jnz .nott +.has_tt: + mov [eax+usb_device_data.TTHub], ebx + mov [eax+usb_device_data.TTPort], cl +.nott: + pop ebx + mov [eax+usb_device_data.ConfigDataSize], edi + mov [eax+usb_device_data.Interfaces], edi + movzx ecx, [esi+usb_controller.ResettingPort] + mov [eax+usb_device_data.Port], cl + 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 + call malloc +; 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 + mov ecx, sizeof.usb_device_data / 4 + rep movsd + pop edi esi + call usb_reinit_pipe_list +; 1d. Free the old memory. + call free + 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], edx +; 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 + mov 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 edx + cmp edx, [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-process/const.inc b/kernel/branches/kolibri-process/const.inc new file mode 100644 index 000000000..6b202e11f --- /dev/null +++ b/kernel/branches/kolibri-process/const.inc @@ -0,0 +1,637 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4418 $ + + +dpl0 equ 10010000b ; data read dpl0 +drw0 equ 10010010b ; data read/write dpl0 +drw3 equ 11110010b ; data read/write dpl3 +cpl0 equ 10011010b ; code read dpl0 +cpl3 equ 11111010b ; code read dpl3 + +D32 equ 01000000b ; 32bit segment +G32 equ 10000000b ; page gran + + +;;;;;;;;;;;;cpu_caps flags;;;;;;;;;;;;;;;; + +CPU_386 equ 3 +CPU_486 equ 4 +CPU_PENTIUM equ 5 +CPU_P6 equ 6 +CPU_PENTIUM4 equ 0x0F + +CAPS_FPU equ 00 ;on-chip x87 floating point unit +CAPS_VME equ 01 ;virtual-mode enhancements +CAPS_DE equ 02 ;debugging extensions +CAPS_PSE equ 03 ;page-size extensions +CAPS_TSC equ 04 ;time stamp counter +CAPS_MSR equ 05 ;model-specific registers +CAPS_PAE equ 06 ;physical-address extensions +CAPS_MCE equ 07 ;machine check exception +CAPS_CX8 equ 08 ;CMPXCHG8B instruction +CAPS_APIC equ 09 ;on-chip advanced programmable + ; interrupt controller +; 10 ;unused +CAPS_SEP equ 11 ;SYSENTER and SYSEXIT instructions +CAPS_MTRR equ 12 ;memory-type range registers +CAPS_PGE equ 13 ;page global extension +CAPS_MCA equ 14 ;machine check architecture +CAPS_CMOV equ 15 ;conditional move instructions +CAPS_PAT equ 16 ;page attribute table + +CAPS_PSE36 equ 17 ;page-size extensions +CAPS_PSN equ 18 ;processor serial number +CAPS_CLFLUSH equ 19 ;CLFUSH instruction + +CAPS_DS equ 21 ;debug store +CAPS_ACPI equ 22 ;thermal monitor and software + ;controlled clock supported +CAPS_MMX equ 23 ;MMX instructions +CAPS_FXSR equ 24 ;FXSAVE and FXRSTOR instructions +CAPS_SSE equ 25 ;SSE instructions +CAPS_SSE2 equ 26 ;SSE2 instructions +CAPS_SS equ 27 ;self-snoop +CAPS_HTT equ 28 ;hyper-threading technology +CAPS_TM equ 29 ;thermal monitor supported +CAPS_IA64 equ 30 ;IA64 capabilities +CAPS_PBE equ 31 ;pending break enable + +;ecx +CAPS_SSE3 equ 32 ;SSE3 instructions +; 33 +; 34 +CAPS_MONITOR equ 35 ;MONITOR/MWAIT instructions +CAPS_DS_CPL equ 36 ; +CAPS_VMX equ 37 ;virtual mode extensions +; 38 ; +CAPS_EST equ 39 ;enhansed speed step +CAPS_TM2 equ 40 ;thermal monitor2 supported +; 41 +CAPS_CID equ 42 ; +; 43 +; 44 +CAPS_CX16 equ 45 ;CMPXCHG16B instruction +CAPS_xTPR equ 46 ; +; +;reserved +; +;ext edx /ecx +CAPS_SYSCAL equ 64 ; +CAPS_XD equ 65 ;execution disable +CAPS_FFXSR equ 66 ; +CAPS_RDTSCP equ 67 ; +CAPS_X64 equ 68 ; +CAPS_3DNOW equ 69 ; +CAPS_3DNOWEXT equ 70 ; +CAPS_LAHF equ 71 ; +CAPS_CMP_LEG equ 72 ; +CAPS_SVM equ 73 ;secure virual machine +CAPS_ALTMOVCR8 equ 74 ; + +; CPU MSR names +MSR_SYSENTER_CS equ 0x174 +MSR_SYSENTER_ESP equ 0x175 +MSR_SYSENTER_EIP equ 0x176 +MSR_AMD_EFER equ 0xC0000080 ; Extended Feature Enable Register +MSR_AMD_STAR equ 0xC0000081 ; SYSCALL/SYSRET Target Address Register + +CR0_PE equ 0x00000001 ;protected mode +CR0_MP equ 0x00000002 ;monitor fpu +CR0_EM equ 0x00000004 ;fpu emulation +CR0_TS equ 0x00000008 ;task switch +CR0_ET equ 0x00000010 ;extension type hardcoded to 1 +CR0_NE equ 0x00000020 ;numeric error +CR0_WP equ 0x00010000 ;write protect +CR0_AM equ 0x00040000 ;alignment check +CR0_NW equ 0x20000000 ;not write-through +CR0_CD equ 0x40000000 ;cache disable +CR0_PG equ 0x80000000 ;paging + + +CR4_VME equ 0x0001 +CR4_PVI equ 0x0002 +CR4_TSD equ 0x0004 +CR4_DE equ 0x0008 +CR4_PSE equ 0x0010 +CR4_PAE equ 0x0020 +CR4_MCE equ 0x0040 +CR4_PGE equ 0x0080 +CR4_PCE equ 0x0100 +CR4_OSFXSR equ 0x0200 +CR4_OSXMMEXPT equ 0x0400 + +SSE_IE equ 0x0001 +SSE_DE equ 0x0002 +SSE_ZE equ 0x0004 +SSE_OE equ 0x0008 +SSE_UE equ 0x0010 +SSE_PE equ 0x0020 +SSE_DAZ equ 0x0040 +SSE_IM equ 0x0080 +SSE_DM equ 0x0100 +SSE_ZM equ 0x0200 +SSE_OM equ 0x0400 +SSE_UM equ 0x0800 +SSE_PM equ 0x1000 +SSE_FZ equ 0x8000 + +SSE_INIT equ (SSE_IM+SSE_DM+SSE_ZM+SSE_OM+SSE_UM+SSE_PM) + +IRQ_PIC equ 0 +IRQ_APIC equ 1 + +struct TSS + _back rw 2 + _esp0 rd 1 + _ss0 rw 2 + _esp1 rd 1 + _ss1 rw 2 + _esp2 rd 1 + _ss2 rw 2 + _cr3 rd 1 + _eip rd 1 + _eflags rd 1 + _eax rd 1 + _ecx rd 1 + _edx rd 1 + _ebx rd 1 + _esp rd 1 + _ebp rd 1 + _esi rd 1 + _edi rd 1 + _es rw 2 + _cs rw 2 + _ss rw 2 + _ds rw 2 + _fs rw 2 + _gs rw 2 + _ldt rw 2 + _trap rw 1 + _io rw 1 + rb 24 + _io_map_0 rb 4096 + _io_map_1 rb 4096 +ends + +PARTITION_COUNT equ 64 +DRIVE_DATA_SIZE equ (16+PARTITION_COUNT*100) + +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) + +CDDataBuf equ (OS_BASE+0x0005000) + +;unused 0x6000 - 0x8fff + +BOOT_VARS equ (OS_BASE) ;0x9000 + +idts equ (OS_BASE+0x000B100) +WIN_STACK equ (OS_BASE+0x000C000) +WIN_POS equ (OS_BASE+0x000C400) +FDD_BUFF equ (OS_BASE+0x000D000) ;512 + +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) + + +BTN_ADDR equ (OS_BASE+0x000FE88) +MEM_AMOUNT equ (OS_BASE+0x000FE8C) + +SYS_SHUTDOWN equ (OS_BASE+0x000FF00) +TASK_ACTIVATE equ (OS_BASE+0x000FF01) + + +TMP_STACK_TOP equ 0x006CC00 + +sys_proc equ (OS_BASE+0x006F000) + +SLOT_BASE equ (OS_BASE+0x0080000) + +VGABasePtr equ (OS_BASE+0x00A0000) + +CLEAN_ZONE equ (_CLEAN_ZONE-OS_BASE) +IDE_DMA equ (_IDE_DMA-OS_BASE) + +; unused? +SB16Buffer equ (OS_BASE+0x02A0000) +SB16_Status equ (OS_BASE+0x02B0000) + +UPPER_KERNEL_PAGES equ (OS_BASE+0x0400000) + +virtual at (OS_BASE+0x05FFF80) + tss TSS +end virtual + +HEAP_BASE equ (OS_BASE+0x0800000) +HEAP_MIN_SIZE equ 0x01000000 + +page_tabs equ 0xFDC00000 +app_page_tabs equ 0xFDC00000 +kernel_tabs equ (page_tabs+ (OS_BASE shr 10)) ;0xFDE00000 +master_tab equ (page_tabs+ (page_tabs shr 10)) ;0xFDFF70000 + +LFB_BASE equ 0xFE000000 + + +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 + +REG_SS equ (RING0_STACK_SIZE-4) +REG_APP_ESP equ (RING0_STACK_SIZE-8) +REG_EFLAGS equ (RING0_STACK_SIZE-12) +REG_CS equ (RING0_STACK_SIZE-16) +REG_EIP equ (RING0_STACK_SIZE-20) +REG_EAX equ (RING0_STACK_SIZE-24) +REG_ECX equ (RING0_STACK_SIZE-28) +REG_EDX equ (RING0_STACK_SIZE-32) +REG_EBX equ (RING0_STACK_SIZE-36) +REG_ESP equ (RING0_STACK_SIZE-40) ;RING0_STACK_SIZE-20 +REG_EBP equ (RING0_STACK_SIZE-44) +REG_ESI equ (RING0_STACK_SIZE-48) +REG_EDI equ (RING0_STACK_SIZE-52) +REG_RET equ (RING0_STACK_SIZE-56) ;irq0.return + + +PG_UNMAP equ 0x000 +PG_MAP equ 0x001 +PG_WRITE equ 0x002 +PG_SW equ 0x003 +PG_USER equ 0x005 +PG_UW equ 0x007 +PG_NOCACHE equ 0x018 +PG_LARGE equ 0x080 +PG_GLOBAL equ 0x100 + +PG_SHARED equ 0x200 + +;;;;;;;;;;;boot time variables + +BOOT_BPP equ 0x9000 ;byte bits per pixel +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_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 +;BOOT_LOG equ 0x901D ;byte not used anymore (0 or 1 : enable system log display) +BOOT_LAUNCHER_START equ 0x901D ;byte (0 or 1) start the first app (right now it's LAUNCHER) after kernel is loaded? +;BOOT_DIRECT_LFB equ 0x901E ;byte 0 or 1 : enable direct lfb write, paging disabled +BOOT_DEBUG_PRINT equ 0x901E ;byte If nonzero, duplicates debug output to the screen. +BOOT_DMA equ 0x901F ; +BOOT_PCI_DATA equ 0x9020 ;8bytes pci data +BOOT_VRR equ 0x9030 ;byte VRR start enabled 1, 2-no +BOOT_IDE_BASE_ADDR equ 0x9031 ;word IDEContrRegsBaseAddr +BOOT_MEM_AMOUNT equ 0x9034 ;dword memory amount + +BOOT_APM_ENTRY equ 0x9040 +BOOT_APM_VERSION equ 0x9044 +BOOT_APM_FLAGS equ 0x9046 ;unused +BOOT_APM_CODE_32 equ 0x9050 +BOOT_APM_CODE_16 equ 0x9052 +BOOT_APM_DATA_16 equ 0x9054 +BOOT_IDE_BAR0_16 equ 0x9056 +BOOT_IDE_BAR1_16 equ 0x9058 +BOOT_IDE_BAR2_16 equ 0x905A +BOOT_IDE_BAR3_16 equ 0x905C +BOOT_IDE_PI_16 equ 0x905E +BOOT_IDE_INTERR_16 equ 0x9060 + +TMP_FILE_NAME equ 0 +TMP_CMD_LINE equ 1024 +TMP_ICON_OFFS equ 1280 + + +EVENT_REDRAW equ 0x00000001 +EVENT_KEY equ 0x00000002 +EVENT_BUTTON equ 0x00000004 +EVENT_BACKGROUND equ 0x00000010 +EVENT_MOUSE equ 0x00000020 +EVENT_IPC equ 0x00000040 +EVENT_NETWORK equ 0x00000080 +EVENT_DEBUG equ 0x00000100 +EVENT_NETWORK2 equ 0x00000200 +EVENT_EXTENDED equ 0x00000400 + +EV_INTR equ 1 + +struct THR_DATA + rb (8192-512) +; pl0_stack + fpu_state rb 512 + tls_page rb 4096 + pdbr rb 4096 +ends + +virtual at (OS_BASE-sizeof.THR_DATA) + thr_data THR_DATA +end virtual + +struct SYS_VARS + bpp dd ? + scanline dd ? + vesa_mode dd ? + x_res dd ? + y_res dd ? +ends + +struct APPOBJ ; common object header + magic dd ? ; + destroy dd ? ; internal destructor + fd dd ? ; next object in list + bk dd ? ; prev object in list + pid dd ? ; owner id +ends + +APP_OBJ_OFFSET equ 48 +APP_EV_OFFSET equ 40 + +struct CURSOR APPOBJ + base dd ? ;allocated memory + hot_x dd ? ;hotspot coords + hot_y dd ? + + list_next dd ? ;next cursor in cursor list + list_prev dd ? ;prev cursor in cursor list + dev_obj dd ? ;device depended data +ends + + +struct EVENT APPOBJ + id dd ? ;event uid + state dd ? ;internal flags + code dd ? + rd 5 +ends + + +struct SMEM + bk dd ? + fd dd ? ;+4 + base dd ? ;+8 + size dd ? ;+12 + access dd ? ;+16 + refcount dd ? ;+20 + name rb 32 ;+24 +ends + +struct SMAP APPOBJ + base dd ? ;mapped base + parent dd ? ;SMEM +ends + +struct DLLDESCR + bk dd ? + fd dd ? ;+4 + data dd ? ;+8 + size dd ? ;+12 + timestamp dq ? + refcount dd ? + defaultbase dd ? + coff_hdr dd ? + symbols_ptr dd ? + symbols_num dd ? + symbols_lim dd ? + exports dd ? ;export table + name rb 260 +ends + +struct HDLL + fd dd ? ;next object in list + bk dd ? ;prev object in list + pid dd ? ;owner id + + base dd ? ;mapped base + size dd ? ;mapped size + refcount dd ? ;reference counter for this process and this lib + parent dd ? ;DLLDESCR +ends + +struct display_t + x dd ? + y dd ? + width dd ? + height dd ? + bpp dd ? + vrefresh dd ? + pitch dd ? + lfb dd ? + + modes dd ? + ddev dd ? + connector dd ? + crtc dd ? + + cr_list.next dd ? + cr_list.prev dd ? + + cursor dd ? + + init_cursor dd ? + select_cursor dd ? + show_cursor dd ? + move_cursor dd ? + restore_cursor dd ? + disable_mouse dd ? + mask_seqno dd ? + check_mouse dd ? + check_m_pixel dd ? +ends + +struct BOOT_DATA + bpp dd ? + scanline dd ? + vesa_mode dd ? + x_res dd ? + y_res dd ? + mouse_port dd ? + bank_switch dd ? + lfb dd ? + vesa_mem dd ? + log dd ? + direct_lfb dd ? + pci_data dd ? + dd ? + vrr dd ? + ide_base dd ? + mem_amount dd ? + pages_count dd ? + pagemap_size dd ? + kernel_max dd ? + kernel_pages dd ? + kernel_tables dd ? + + cpu_vendor dd ? + dd ? + dd ? + cpu_sign dd ? + cpu_info dd ? + cpu_caps dd ? + dd ? + dd ? +ends + +struct LHEAD + next dd ? ;next object in list + prev dd ? ;prev object in list +ends + +struct MUTEX + lhead LHEAD + count dd ? +ends + +struct PCIDEV + bk dd ? + fd dd ? + vendor_device_id dd ? + class dd ? + devfn db ? + bus db ? + rb 2 + owner dd ? ; pointer to SRV or 0 +ends + +; The following macro assume that we are on uniprocessor machine. +; Serious work is needed for multiprocessor machines. +macro spin_lock_irqsave spinlock +{ + pushf + cli +} +macro spin_unlock_irqrestore spinlock +{ + popf +} +macro spin_lock_irq spinlock +{ + cli +} +macro spin_unlock_irq spinlock +{ + sti +} + +struct MEM_STATE + mutex MUTEX + smallmap dd ? + treemap dd ? + topsize dd ? + top dd ? + smallbins rd 4*32 + treebins rd 32 +ends + +struct PG_DATA + mem_amount dd ? + vesa_mem dd ? + pages_count dd ? + pages_free dd ? + pages_faults dd ? + pagemap_size dd ? + kernel_pages dd ? + kernel_tables dd ? + sys_page_dir dd ? + mutex MUTEX +ends + +struct SRV + srv_name rb 16 ;ASCIIZ string + magic dd ? ;+0x10 ;'SRV ' + size dd ? ;+0x14 ;size of structure SRV + fd dd ? ;+0x18 ;next SRV descriptor + bk dd ? ;+0x1C ;prev SRV descriptor + base dd ? ;+0x20 ;service base address + entry dd ? ;+0x24 ;service START function + srv_proc dd ? ;+0x28 ;user mode service handler + 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 + +struct COFF_HEADER + machine dw ? + nSections dw ? + DataTime dd ? + pSymTable dd ? + nSymbols dd ? + optHeader dw ? + flags dw ? +ends + +struct COFF_SECTION + Name rb 8 + VirtualSize dd ? + VirtualAddress dd ? + SizeOfRawData dd ? + PtrRawData dd ? + PtrReloc dd ? + PtrLinenumbers dd ? + NumReloc dw ? + NumLinenum dw ? + Characteristics dd ? +ends + +struct COFF_RELOC + VirtualAddress dd ? + SymIndex dd ? + Type dw ? +ends + +struct COFF_SYM + Name rb 8 + Value dd ? + SectionNumber dw ? + Type dw ? + StorageClass db ? + NumAuxSymbols db ? +ends + +struct IOCTL + handle dd ? + io_code dd ? + input dd ? + inp_size dd ? + output dd ? + out_size dd ? +ends + +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-process/core/apic.inc b/kernel/branches/kolibri-process/core/apic.inc new file mode 100644 index 000000000..9aeed4150 --- /dev/null +++ b/kernel/branches/kolibri-process/core/apic.inc @@ -0,0 +1,443 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +iglobal +IRQ_COUNT dd 24 +endg + +uglobal +irq_mode rd 1 +IOAPIC_base rd 1 +LAPIC_BASE rd 1 +endg + +APIC_ID equ 0x20 +APIC_TPR equ 0x80 +APIC_EOI equ 0xb0 +APIC_LDR equ 0xd0 +APIC_DFR equ 0xe0 +APIC_SVR equ 0xf0 +APIC_ISR equ 0x100 +APIC_ESR equ 0x280 +APIC_ICRL equ 0x300 +APIC_ICRH equ 0x310 +APIC_LVT_LINT0 equ 0x350 +APIC_LVT_LINT1 equ 0x360 +APIC_LVT_err equ 0x370 + +; APIC timer +APIC_LVT_timer equ 0x320 +APIC_timer_div equ 0x3e0 +APIC_timer_init equ 0x380 +APIC_timer_cur equ 0x390 +; IOAPIC +IOAPIC_ID equ 0x0 +IOAPIC_VER equ 0x1 +IOAPIC_ARB equ 0x2 +IOAPIC_REDTBL equ 0x10 + +align 4 +APIC_init: + mov [irq_mode], IRQ_PIC + + cmp [acpi_ioapic_base], 0 + jz .no_apic + + cmp [acpi_lapic_base], 0 + jz .no_apic + + stdcall load_file, dev_data_path + test eax, eax + jz .no_apic + + mov [acpi_dev_data], eax + mov [acpi_dev_size], ebx + + call IRQ_mask_all + +; IOAPIC init + stdcall map_io_mem, [acpi_ioapic_base], 0x20, PG_SW+PG_NOCACHE + mov [IOAPIC_base], eax + + mov eax, IOAPIC_VER + call IOAPIC_read + shr eax, 16 + inc al + movzx eax, al + cmp al, IRQ_RESERVED + jbe @f + + mov al, IRQ_RESERVED +@@: + mov [IRQ_COUNT], eax + + ; Reroute IOAPIC & mask all interrupts + xor ecx, ecx + mov eax, IOAPIC_REDTBL +@@: + mov ebx, eax + call IOAPIC_read + mov ah, 0x08; Delivery Mode: Fixed, Destination Mode: Logical + mov al, cl + add al, 0x20; vector + or eax, 0x10000; Mask Interrupt + cmp ecx, 16 + jb .set + + or eax, 0xa000;<<< level-triggered active-low for IRQ16+ +.set: + xchg eax, ebx + call IOAPIC_write + inc eax + mov ebx, eax + call IOAPIC_read + or eax, 0xff000000; Destination Field + xchg eax, ebx + call IOAPIC_write + inc eax + inc ecx + cmp ecx, [IRQ_COUNT] + jb @b + + call LAPIC_init + + mov [irq_mode], IRQ_APIC + + mov al, 0x70 + out 0x22, al + mov al, 1 + out 0x23, al + + call pci_irq_fixup +.no_apic: + + ret + +;=========================================================== +align 4 +LAPIC_init: + + cmp [LAPIC_BASE], 0 + jne .done + + stdcall map_io_mem, [acpi_lapic_base], 0x1000, PG_SW+PG_NOCACHE + mov [LAPIC_BASE], eax + mov esi, eax + + ; Program Destination Format Register for Flat mode. + mov eax, [esi + APIC_DFR] + or eax, 0xf0000000 + mov [esi + APIC_DFR], eax + + ; Program Logical Destination Register. + mov eax, [esi + APIC_LDR] + ;and eax, 0xff000000 + and eax, 0x00ffffff + or eax, 0x01000000;!!!!!!!!!!!! + mov [esi + APIC_LDR], eax + + ; Task Priority Register initialization. + mov eax, [esi + APIC_TPR] + and eax, 0xffffff00 + mov [esi + APIC_TPR], eax + + ; Flush the queue + mov edx, 0 +.nxt2: + mov ecx, 32 + mov eax, [esi + APIC_ISR + edx] +.nxt: + shr eax, 1 + jnc @f + mov dword [esi + APIC_EOI], 0; EOI +@@: + loop .nxt + + add edx, 0x10 + cmp edx, 0x170 + jbe .nxt2 + + ; Spurious-Interrupt Vector Register initialization. + mov eax, [esi + APIC_SVR] + or eax, 0x1ff + and eax, 0xfffffdff + mov [esi + APIC_SVR], eax + + ; Initialize LVT LINT0 register. (INTR) + mov eax, 0x00700 + ; mov eax, 0x10700 + mov [esi + APIC_LVT_LINT0], eax + + ; Initialize LVT LINT1 register. (NMI) + mov eax, 0x00400 + mov [esi + APIC_LVT_LINT1], eax + + ; Initialize LVT Error register. + mov eax, [esi + APIC_LVT_err] + or eax, 0x10000; bit 16 + mov [esi + APIC_LVT_err], eax + + ; LAPIC timer + ; pre init + mov dword[esi + APIC_timer_div], 1011b; 1 + mov dword[esi + APIC_timer_init], 0xffffffff; max val + push esi + mov esi, 640 ; wait 0.64 sec + call delay_ms + pop esi + mov eax, [esi + APIC_timer_cur]; read current tick couner + xor eax, 0xffffffff ; eax = 0xffffffff - eax + shr eax, 6 ; eax /= 64; APIC ticks per 0.01 sec + + ; Start (every 0.01 sec) + mov dword[esi + APIC_LVT_timer], 0x30020; periodic int 0x20 + mov dword[esi + APIC_timer_init], eax + +.done: + ret + +;=========================================================== +; IOAPIC implementation +align 4 +IOAPIC_read: +; in : EAX - IOAPIC register +; out: EAX - readed value + push esi + mov esi, [IOAPIC_base] + mov [esi], eax + mov eax, [esi + 0x10] + pop esi + ret + +align 4 +IOAPIC_write: +; in : EAX - IOAPIC register +; EBX - value +; out: none + push esi + mov esi, [IOAPIC_base] + mov [esi], eax + mov [esi + 0x10], ebx + pop esi + ret +;=========================================================== +; Remap all IRQ to 0x20+ Vectors +; IRQ0 to vector 0x20, IRQ1 to vector 0x21.... +align 4 +PIC_init: + cli + mov al, 0x11 ; icw4, edge triggered + out 0x20, al + out 0xA0, al + + mov al, 0x20 ; generate 0x20 + + out 0x21, al + mov al, 0x28 ; generate 0x28 + + out 0xA1, al + + mov al, 0x04 ; slave at irq2 + out 0x21, al + mov al, 0x02 ; at irq9 + out 0xA1, al + + mov al, 0x01 ; 8086 mode + out 0x21, al + out 0xA1, al + + call IRQ_mask_all + ; mov dword[irq_type_to_set], IRQ_TYPE_PIC + ret + +; ----------------------------------------- +; TIMER SET TO 1/100 S +align 4 +PIT_init: + mov al, 0x34 ; set to 100Hz + out 0x43, al + mov al, 0x9b ; lsb 1193180 / 1193 + out 0x40, al + mov al, 0x2e ; msb + out 0x40, al + ret + +; ----------------------------------------- +align 4 +unmask_timer: + cmp [irq_mode], IRQ_APIC + je @f + + stdcall enable_irq, 0 + ret +@@: + ; use PIT + ; in some systems PIT no connected to IOAPIC + ; mov eax, 0x14 + ; call IOAPIC_read + ; mov ah, 0x09 ; Delivery Mode: Lowest Priority, Destination Mode: Logical + ; mov al, 0x20 + ; or eax, 0x10000 ; Mask Interrupt + ; mov ebx, eax + ; mov eax, 0x14 + ; call IOAPIC_write + ; stdcall enable_irq, 2 + ; ret + + ; use LAPIC timer + mov esi, [LAPIC_BASE] + mov eax, [esi + APIC_LVT_timer] + and eax, 0xfffeffff + mov [esi + APIC_LVT_timer], eax + ret + +; ----------------------------------------- +; Disable all IRQ +align 4 +IRQ_mask_all: + cmp [irq_mode], IRQ_APIC + je .APIC + + mov al, 0xFF + out 0x21, al + out 0xA1, al + mov ecx, 0x1000 + ret +.APIC: + mov ecx, [IRQ_COUNT] + mov eax, 0x10 +@@: + mov ebx, eax + call IOAPIC_read + or eax, 0x10000; bit 16 + xchg eax, ebx + call IOAPIC_write + inc eax + inc eax + loop @b + ret + +; ----------------------------------------- +; End Of Interrupt +; cl - IRQ number +align 4 +irq_eoi: ; __fastcall + cmp [irq_mode], IRQ_APIC + je .APIC + + cmp cl, 8 + mov al, 0x20 + jb @f + out 0xa0, al +@@: + out 0x20, al + ret + +.APIC: + mov eax, [LAPIC_BASE] + mov dword [eax + APIC_EOI], 0; EOI + ret + +; ----------------------------------------- +; from dll.inc +align 4 +proc enable_irq stdcall, irq_line:dword + mov ebx, [irq_line] + cmp [irq_mode], IRQ_APIC + je .APIC + + mov edx, 0x21 + cmp ebx, 8 + jb @F + + mov edx, 0xA1 + sub ebx, 8 +@@: + in al, dx + btr eax, ebx + out dx, al + ret +.APIC: + shl ebx, 1 + add ebx, 0x10 + mov eax, ebx + call IOAPIC_read + and eax, 0xfffeffff; bit 16 + xchg eax, ebx + call IOAPIC_write + ret +endp + +proc disable_irq stdcall, irq_line:dword + mov ebx, [irq_line] + cmp [irq_mode], IRQ_APIC + je .APIC + + mov edx, 0x21 + cmp ebx, 8 + jb @F + + mov edx, 0xA1 + sub ebx, 8 +@@: + in al, dx + bts eax, ebx + out dx, al + ret +.APIC: + shl ebx, 1 + add ebx, 0x10 + mov eax, ebx + call IOAPIC_read + or eax, 0x10000; bit 16 + xchg eax, ebx + call IOAPIC_write + ret +endp + +align 4 +pci_irq_fixup: + + push ebp + + mov esi, [acpi_dev_data] + mov ebx, [acpi_dev_size] + + lea edi, [esi+ebx] + +.iterate: + + cmp esi, edi + jae .done + + mov eax, [esi] + + cmp eax, -1 + je .done + + movzx ebx, al + movzx ebp, ah + + stdcall pci_read32, ebp, ebx, 0 + + cmp eax, [esi+4] + jne .skip + + mov eax, [esi+8] + stdcall pci_write8, ebp, ebx, 0x3C, eax +.skip: + add esi, 16 + jmp .iterate + +.done: +.fail: + pop ebp + ret + + + + + diff --git a/kernel/branches/kolibri-process/core/clipboard.inc b/kernel/branches/kolibri-process/core/clipboard.inc new file mode 100644 index 000000000..689064024 --- /dev/null +++ b/kernel/branches/kolibri-process/core/clipboard.inc @@ -0,0 +1,148 @@ +;------------------------------------------------------------------------------ +align 4 +sys_clipboard: + xor eax, eax + dec eax +; check availability of main list + cmp [clipboard_main_list], eax + je .exit_1 ; main list area not found + + test ebx, ebx ; 0 - Get the number of slots in the clipboard + jnz .1 +; get the number of slots + mov eax, [clipboard_slots] + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.1: + dec ebx ; 1 - Read the data from the clipboard + jnz .2 +; verify the existence of slot + cmp ecx, [clipboard_slots] + jae .exit_2 +; get a pointer to the data of slot + shl ecx, 2 + add ecx, [clipboard_main_list] + mov esi, [ecx] + mov ecx, [esi] +; allocate memory for application for copy the data of slots + push ecx + stdcall user_alloc, ecx + pop ecx +; copying data of slots + mov edi, eax + cld + rep movsb + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.2: + dec ebx ; 2 - Write the data to the clipboard + jnz .3 +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + jne .exit_2 +; lock last slot + inc eax + mov [ebx], eax +; check the overflow pointer of slots + cmp [clipboard_slots], 1024 + jae .exit_3 +; get memory for new slot + push ebx ecx edx + stdcall kernel_alloc, ecx + pop edx ecx ebx + test eax, eax + jz .exit_3 +; create a new slot + mov edi, eax + mov eax, [clipboard_slots] + shl eax, 2 + add eax, [clipboard_main_list] + mov [eax], edi +; copy the data into the slot + mov esi, edx + mov eax, ecx + cld + stosd ; store size of slot + sub ecx, 4 + add esi, 4 + rep movsb ; store slot data +; increase the counter of slots + inc [clipboard_slots] +; unlock last slot + xor eax, eax + mov [ebx], eax + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.3: + dec ebx ; 3 - Delete the last slot in the clipboard + jnz .4 +; check the availability of slots + mov eax, [clipboard_slots] + test eax, eax + jz .exit_2 +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + jne .exit_2 +; lock last slot + inc eax + mov [ebx], eax +; decrease the counter of slots + mov eax, clipboard_slots + dec dword [eax] +; free of kernel memory allocated for the slot + mov eax, [eax] + shl eax, 2 + add eax, [clipboard_main_list] + mov eax, [eax] + push ebx + stdcall kernel_free, eax + pop ebx +; unlock last slot + xor eax, eax + mov [ebx], eax + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.4: + dec ebx ; 4 - Emergency discharge of clipboard + jnz .exit +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + je .exit_2 + +; there should be a procedure for checking the integrity of the slots +; and I will do so in the future + +; unlock last slot + mov [ebx], eax + jmp .exit +;------------------------------------------------------------------------------ +align 4 +.exit_3: +; unlock last slot + xor eax, eax + mov [ebx], eax +.exit_2: + xor eax, eax + inc eax ; error +.exit_1: + mov [esp + 32], eax +.exit: + ret +;------------------------------------------------------------------------------ +uglobal +align 4 +clipboard_slots dd ? +clipboard_main_list dd ? +clipboard_write_lock dd ? +endg +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-process/core/conf_lib-sp.inc b/kernel/branches/kolibri-process/core/conf_lib-sp.inc new file mode 100644 index 000000000..abf7cf5e3 --- /dev/null +++ b/kernel/branches/kolibri-process/core/conf_lib-sp.inc @@ -0,0 +1,11 @@ +; Éste archivo debe ser editado con codificación CP866 + +ugui_mouse_speed cp850 'velocidad del ratón',0 +ugui_mouse_delay cp850 'demora del ratón',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-process/core/conf_lib.inc b/kernel/branches/kolibri-process/core/conf_lib.inc new file mode 100644 index 000000000..37b7c1a1d --- /dev/null +++ b/kernel/branches/kolibri-process/core/conf_lib.inc @@ -0,0 +1,254 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------------------------------------------------------------------------- +;Loading configuration from ini file +; {SPraid.simba} +;------------------------------------------------------------------------- + +$Revision: 3545 $ + +iglobal +conf_path_sect: + db 'path',0 + +conf_fname db '/sys/sys.conf',0 +endg +; set soke kernel configuration +proc set_kernel_conf + locals + par db 30 dup(?) + endl + + pushad +;[gui] +;mouse_speed + + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, ugui, ugui_mouse_speed, \ + eax,30, ugui_mouse_speed_def + pop eax + stdcall strtoint, eax + mov [mouse_speed_factor], ax + +;mouse_delay + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, ugui, ugui_mouse_delay, \ + eax,30, ugui_mouse_delay_def + pop eax + stdcall strtoint, eax + mov [mouse_delay], eax + + +;midibase + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, udev, udev_midibase, eax, 30, udev_midibase_def + pop eax + stdcall strtoint, eax + + cmp eax, 0x100 + jb @f + cmp eax, 0x10000 + jae @f + mov [midi_base], ax + mov [mididp], eax + inc eax + mov [midisp], eax +@@: + popad + ret +endp +iglobal +ugui db 'gui',0 +ugui_mouse_speed_def db '2',0 +ugui_mouse_delay_def db '0x00A',0 +udev_midibase db 'midibase',0 +udev_midibase_def db '0x320',0 +endg + +iglobal +if lang eq sp + include 'core/conf_lib-sp.inc' +else + ugui_mouse_speed db 'mouse_speed',0 + ugui_mouse_delay db 'mouse_delay',0 + udev db 'dev',0 + unet db 'net',0 + unet_active db 'active',0 + unet_addr db 'addr',0 + unet_mask db 'mask',0 + unet_gate db 'gate',0 +end if +unet_def db 0 +endg +; convert string to DWord +proc strtoint stdcall,strs + pushad + + mov eax, [strs] + inc eax + mov bl, [eax] + cmp bl, 'x' + je .hex + cmp bl, 'X' + je .hex + jmp .dec +.hex: + inc eax + stdcall strtoint_hex, eax + jmp .exit +.dec: + dec eax + stdcall strtoint_dec, eax +.exit: + mov [esp+28], eax + popad + ret +endp + +; convert string to DWord for decimal value +proc strtoint_dec stdcall,strs + pushad + xor edx, edx + ; поиск конца + mov esi, [strs] +@@: + lodsb + or al, al + jnz @b + mov ebx, esi + mov esi, [strs] + dec ebx + sub ebx, esi + mov ecx, 1 + +@@: + dec ebx + or ebx, ebx + jz @f + imul ecx, ecx, 10; порядок + jmp @b +@@: + + xchg ebx, ecx + + + xor ecx, ecx + + +@@: + xor eax, eax + lodsb + cmp al, 0 + je .eend + + sub al, 30h + imul ebx + add ecx, eax + push ecx + xchg eax, ebx + mov ecx, 10 + div ecx + xchg eax, ebx + pop ecx + jmp @b + +.eend: + mov [esp+28], ecx + popad + ret +endp + +;convert string to DWord for hex value +proc strtoint_hex stdcall,strs + pushad + xor edx, edx + + mov esi, [strs] + mov ebx, 1 + add esi, 1 + +@@: + lodsb + or al, al + jz @f + shl ebx, 4 + jmp @b +@@: + xor ecx, ecx + mov esi, [strs] + +@@: + xor eax, eax + lodsb + cmp al, 0 + je .eend + + cmp al, 'a' + jae .bm + cmp al, 'A' + jae .bb + jmp .cc +.bm: ; 57h + sub al, 57h + jmp .do + +.bb: ; 37h + sub al, 37h + jmp .do + +.cc: ; 30h + sub al, 30h + +.do: + imul ebx + add ecx, eax + shr ebx, 4 + + jmp @b + +.eend: + mov [esp+28], ecx + popad + ret +endp + + +; convert string to DWord for IP addres +proc do_inet_adr stdcall,strs + pushad + + mov esi, [strs] + mov ebx, 0 +.next: + push esi +@@: + lodsb + or al, al + jz @f + cmp al, '.' + jz @f + jmp @b +@@: + mov cl, al + mov [esi-1], byte 0 + ;pop eax + call strtoint_dec + rol eax, 24 + ror ebx, 8 + add ebx, eax + or cl, cl + jz @f + jmp .next +@@: + mov [esp+28], ebx + popad + ret +endp diff --git a/kernel/branches/kolibri-process/core/debug.inc b/kernel/branches/kolibri-process/core/debug.inc new file mode 100644 index 000000000..359dbcbd0 --- /dev/null +++ b/kernel/branches/kolibri-process/core/debug.inc @@ -0,0 +1,435 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; diamond, 2006 +sys_debug_services: + cmp ebx, 9 + ja @f + jmp dword [sys_debug_services_table+ebx*4] +@@: + ret +iglobal +align 4 +sys_debug_services_table: + dd debug_set_event_data + dd debug_getcontext + dd debug_setcontext + dd debug_detach + dd debug_suspend + dd debug_resume + dd debug_read_process_memory + dd debug_write_process_memory + dd debug_terminate + dd debug_set_drx +endg +debug_set_event_data: +; in: ecx = pointer +; destroys eax + mov eax, [current_slot] + mov [eax+APPDATA.dbg_event_mem], ecx + ret + +get_debuggee_slot: +; in: ecx=PID +; out: CF=1 if error +; CF=0 and eax=slot*0x20 if ok +; out: interrupts disabled + cli + mov eax, ecx + call pid_to_slot + test eax, eax + jz .ret_bad + shl eax, 5 + push ebx + mov ebx, [CURRENT_TASK] + cmp [SLOT_BASE+eax*8+APPDATA.debugger_slot], ebx + pop ebx + jnz .ret_bad +; clc ; automatically + ret +.ret_bad: + stc + ret + +debug_detach: +; in: ecx=pid +; destroys eax,ebx + call get_debuggee_slot + jc .ret + and dword [eax*8+SLOT_BASE+APPDATA.debugger_slot], 0 + call do_resume +.ret: + sti + ret + +debug_terminate: +; in: ecx=pid + call get_debuggee_slot + jc debug_detach.ret + mov ecx, eax + shr ecx, 5 +; push 2 +; pop ebx + mov edx, esi + jmp sysfn_terminate + +debug_suspend: +; in: ecx=pid +; destroys eax,ecx + cli + mov eax, ecx + call pid_to_slot + shl eax, 5 + jz .ret + mov cl, [CURRENT_TASK+eax+TASKDATA.state] ; process state + test cl, cl + jz .1 + cmp cl, 5 + jnz .ret + mov cl, 2 +.2: + mov [CURRENT_TASK+eax+TASKDATA.state], cl +.ret: + sti + ret +.1: + inc ecx + jmp .2 + +do_resume: + mov cl, [CURRENT_TASK+eax+TASKDATA.state] + cmp cl, 1 + jz .1 + cmp cl, 2 + jnz .ret + mov cl, 5 +.2: + mov [CURRENT_TASK+eax+TASKDATA.state], cl +.ret: + ret +.1: + dec ecx + jmp .2 + +debug_resume: +; in: ecx=pid +; destroys eax,ebx + cli + mov eax, ecx + call pid_to_slot + shl eax, 5 + jz .ret + call do_resume +.ret: + sti + ret + +debug_getcontext: +; in: +; ecx=pid +; edx=sizeof(CONTEXT) +; esi->CONTEXT +; destroys eax,ecx,edx,esi,edi + cmp edx, 28h + jnz .ret +; push ecx +; mov ecx, esi + call check_region +; pop ecx + dec eax + jnz .ret + call get_debuggee_slot + jc .ret + mov edi, esi + mov eax, [eax*8+SLOT_BASE+APPDATA.pl0_stack] + lea esi, [eax+RING0_STACK_SIZE] + +.ring0: +; note that following code assumes that all interrupt/exception handlers +; saves ring-3 context by pushad in this order +; top of ring0 stack: ring3 stack ptr (ss+esp), iret data (cs+eip+eflags), pushad + sub esi, 8+12+20h + lodsd ;edi + mov [edi+24h], eax + lodsd ;esi + mov [edi+20h], eax + lodsd ; ebp + mov [edi+1Ch], eax + lodsd ;esp + lodsd ;ebx + mov [edi+14h], eax + lodsd ;edx + mov [edi+10h], eax + lodsd ;ecx + mov [edi+0Ch], eax + lodsd ;eax + mov [edi+8], eax + lodsd ;eip + mov [edi], eax + lodsd ;cs + lodsd ;eflags + mov [edi+4], eax + lodsd ;esp + mov [edi+18h], eax +.ret: + sti + ret + +debug_setcontext: +; in: +; ecx=pid +; edx=sizeof(CONTEXT) +; esi->CONTEXT +; destroys eax,ecx,edx,esi,edi + cmp edx, 28h + jnz .ret +; push ebx +; mov ebx, edx + call check_region +; pop ebx + dec eax + jnz .ret + call get_debuggee_slot + jc .stiret +; mov esi, edx + mov eax, [eax*8+SLOT_BASE+APPDATA.pl0_stack] + lea edi, [eax+RING0_STACK_SIZE] + +.ring0: + sub edi, 8+12+20h + mov eax, [esi+24h] ;edi + stosd + mov eax, [esi+20h] ;esi + stosd + mov eax, [esi+1Ch] ;ebp + stosd + scasd + mov eax, [esi+14h] ;ebx + stosd + mov eax, [esi+10h] ;edx + stosd + mov eax, [esi+0Ch] ;ecx + stosd + mov eax, [esi+8] ;eax + stosd + mov eax, [esi] ;eip + stosd + scasd + mov eax, [esi+4] ;eflags + stosd + mov eax, [esi+18h] ;esp + stosd +.stiret: + sti +.ret: + ret + +debug_set_drx: + call get_debuggee_slot + jc .errret + mov ebp, eax + lea eax, [eax*8+SLOT_BASE+APPDATA.dbg_regs] +; [eax]=dr0, [eax+4]=dr1, [eax+8]=dr2, [eax+C]=dr3 +; [eax+10]=dr7 + cmp esi, OS_BASE + jae .errret + cmp dl, 3 + ja .errret + mov ecx, dr7 +;fix me + xchg ecx, edx + shr edx, cl + shr edx, cl + xchg ecx, edx + + test ecx, 2 ; bit 1+2*index = G0..G3, global break enable + jnz .errret2 + test dh, dh + jns .new +; clear breakpoint + movzx edx, dl + add edx, edx + and dword [eax+edx*2], 0 ; clear DR + btr dword [eax+10h], edx ; clear L bit + test byte [eax+10h], 55h + jnz .okret +; imul eax, ebp, tss_step/32 +; and byte [eax + tss_data + TSS._trap], not 1 + and [ebp*8 + SLOT_BASE+APPDATA.dbg_state], not 1 +.okret: + and dword [esp+32], 0 + sti + ret +.errret: + sti + mov dword [esp+32], 1 + ret +.errret2: + sti + mov dword [esp+32], 2 + ret +.new: +; add new breakpoint +; dl=index; dh=flags; esi=address + test dh, 0xF0 + jnz .errret + mov cl, dh + and cl, 3 + cmp cl, 2 + jz .errret + mov cl, dh + shr cl, 2 + cmp cl, 2 + jz .errret + + mov ebx, esi + test bl, dl + + jnz .errret + or byte [eax+10h+1], 3 ; set GE and LE flags + + movzx edx, dh + movzx ecx, dl + add ecx, ecx + bts dword [eax+10h], ecx ; set L flag + add ecx, ecx + mov [eax+ecx], ebx;esi ; set DR + shl edx, cl + mov ebx, 0xF + shl ebx, cl + not ebx + and [eax+10h+2], bx + or [eax+10h+2], dx ; set R/W and LEN fields +; imul eax, ebp, tss_step/32 +; or byte [eax + tss_data + TSS._trap], 1 + or [ebp*8 + SLOT_BASE+APPDATA.dbg_state], 1 + jmp .okret + +debug_read_process_memory: +; in: +; ecx=pid +; edx=length +; edi->buffer in debugger +; esi=address in debuggee +; out: [esp+36]=sizeof(read) +; destroys all +; push ebx +; mov ebx, esi + call check_region +; pop ebx + dec eax + jnz .err + call get_debuggee_slot + jc .err + shr eax, 5 + mov ecx, edi + call read_process_memory + sti + mov dword [esp+32], eax + ret +.err: + or dword [esp+32], -1 + ret + +debug_write_process_memory: +; in: +; ecx=pid +; edx=length +; edi->buffer in debugger +; esi=address in debuggee +; out: [esp+36]=sizeof(write) +; destroys all +; push ebx +; mov ebx, esi + call check_region +; pop ebx + dec eax + jnz debug_read_process_memory.err + call get_debuggee_slot + jc debug_read_process_memory.err + shr eax, 5 + mov ecx, edi + call write_process_memory + sti + mov [esp+32], eax + ret + +debugger_notify: +; in: eax=debugger slot +; ecx=size of debug message +; [esp+4]..[esp+4+ecx]=message +; interrupts must be disabled! +; destroys all general registers +; interrupts remain disabled + xchg ebp, eax + mov edi, [timer_ticks] + add edi, 500 ; 5 sec timeout +.1: + mov eax, ebp + shl eax, 8 + mov esi, [SLOT_BASE+eax+APPDATA.dbg_event_mem] + test esi, esi + jz .ret +; read buffer header + push ecx + push eax + push eax + mov eax, ebp + mov ecx, esp + mov edx, 8 + call read_process_memory + cmp eax, edx + jz @f + add esp, 12 + jmp .ret +@@: + cmp dword [ecx], 0 + jg @f +.2: + pop ecx + pop ecx + pop ecx + cmp dword [CURRENT_TASK], 1 + jnz .notos + cmp [timer_ticks], edi + jae .ret +.notos: + sti + call change_task + cli + jmp .1 +@@: + mov edx, [ecx+8] + add edx, [ecx+4] + cmp edx, [ecx] + ja .2 +; advance buffer position + push edx + mov edx, 4 + sub ecx, edx + mov eax, ebp + add esi, edx + call write_process_memory + pop eax +; write message + mov eax, ebp + add esi, edx + add esi, [ecx+8] + add ecx, 20 + pop edx + pop edx + pop edx + call write_process_memory +; new debug event + mov eax, ebp + shl eax, 8 + or byte [SLOT_BASE+eax+APPDATA.event_mask+1], 1 ; set flag 100h +.ret: + ret diff --git a/kernel/branches/kolibri-process/core/dll.inc b/kernel/branches/kolibri-process/core/dll.inc new file mode 100644 index 000000000..75fa70161 --- /dev/null +++ b/kernel/branches/kolibri-process/core/dll.inc @@ -0,0 +1,1662 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4418 $ + + +DRV_COMPAT equ 5 ;minimal required drivers version +DRV_CURRENT equ 6 ;current drivers model version + +DRV_VERSION equ (DRV_COMPAT shl 16) or DRV_CURRENT +PID_KERNEL equ 1 ;os_idle thread + + + +align 4 +proc get_notify stdcall, p_ev:dword + +.wait: + mov ebx, [current_slot] + test dword [ebx+APPDATA.event_mask], EVENT_NOTIFY + jz @f + and dword [ebx+APPDATA.event_mask], not EVENT_NOTIFY + mov edi, [p_ev] + mov dword [edi], EV_INTR + mov eax, [ebx+APPDATA.event] + mov dword [edi+4], eax + ret +@@: + call change_task + jmp .wait +endp + +align 4 +proc pci_read32 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 6 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_read16 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 5 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_read8 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 4 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_write8 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 8 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +align 4 +proc pci_write16 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 9 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +align 4 +proc pci_write32 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 10 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +handle equ IOCTL.handle +io_code equ IOCTL.io_code +input equ IOCTL.input +inp_size equ IOCTL.inp_size +output equ IOCTL.output +out_size equ IOCTL.out_size + + +align 4 +proc srv_handler stdcall, ioctl:dword + mov esi, [ioctl] + test esi, esi + jz .err + + mov edi, [esi+handle] + cmp [edi+SRV.magic], ' SRV' + jne .fail + + cmp [edi+SRV.size], sizeof.SRV + jne .fail + +; 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 + not eax + mov [esi+output], eax + mov [esi+out_size], 4 + ret +.err: + xor eax, eax + not eax + ret +endp + +; param +; ecx= io_control +; +; retval +; eax= error code + +align 4 +srv_handlerEx: + cmp ecx, OS_BASE + jae .fail + + mov eax, [ecx+handle] + cmp [eax+SRV.magic], ' SRV' + jne .fail + + cmp [eax+SRV.size], sizeof.SRV + jne .fail + +; 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 + ret + +restore handle +restore io_code +restore input +restore inp_size +restore output +restore out_size + +align 4 +proc get_service stdcall, sz_name:dword + mov eax, [sz_name] + test eax, eax + jnz @F + ret +@@: + mov edx, [srv.fd] +@@: + cmp edx, srv.fd-SRV.fd + je .not_load + + stdcall strncmp, edx, [sz_name], 16 + test eax, eax + je .ok + + mov edx, [edx+SRV.fd] + jmp @B +.not_load: + mov eax, [sz_name] +; Try to load .dll driver first. If not, fallback to .obj. + push edi + sub esp, 36 + mov edi, esp + mov dword [edi], '/sys' + mov dword [edi+4], '/dri' + mov dword [edi+8], 'vers' + mov byte [edi+12], '/' +@@: + mov dl, [eax] + mov [edi+13], dl + inc eax + inc edi + test dl, dl + jnz @b + mov dword [edi+12], '.sys' + mov byte [edi+16], 0 + mov edi, esp + stdcall load_pe_driver, edi, 0 + add esp, 36 + pop edi + test eax, eax + jnz .nothing + pop ebp + jmp load_driver +.ok: + mov eax, edx +.nothing: + ret +endp + +reg_service: + xor eax, eax + mov ecx, [esp+8] + jecxz .nothing + push sizeof.SRV + push ecx + pushd [esp+12] + call reg_service_ex +.nothing: + ret 8 + +reg_usb_driver: + push sizeof.USBSRV + pushd [esp+12] + pushd [esp+12] + call reg_service_ex + test eax, eax + jz .nothing + mov ecx, [esp+12] + mov [eax+USBSRV.usb_func], ecx +.nothing: + ret 12 + +proc reg_service_ex stdcall, name:dword, handler:dword, srvsize:dword + + push ebx + + xor eax, eax + + cmp [name], eax + je .fail + +; cmp [handler], eax +; je .fail + + mov eax, [srvsize] + call malloc + test eax, eax + jz .fail + + push esi + push edi + mov edi, eax + mov esi, [name] + movsd + movsd + movsd + movsd + pop edi + pop esi + + mov [eax+SRV.magic], ' SRV' + mov [eax+SRV.size], sizeof.SRV + + mov ebx, srv.fd-SRV.fd + mov edx, [ebx+SRV.fd] + mov [eax+SRV.fd], edx + mov [eax+SRV.bk], ebx + mov [ebx+SRV.fd], eax + mov [edx+SRV.bk], eax + + mov ecx, [handler] + mov [eax+SRV.srv_proc], ecx + pop ebx + ret +.fail: + xor eax, eax + pop ebx + ret +endp + +align 4 +proc get_proc stdcall, exp:dword, sz_name:dword + + mov edx, [exp] +.next: + mov eax, [edx] + test eax, eax + jz .end + + push edx + stdcall strncmp, eax, [sz_name], 16 + pop edx + test eax, eax + jz .ok + + add edx, 8 + jmp .next +.ok: + mov eax, [edx+4] +.end: + ret +endp + +align 4 +proc get_coff_sym stdcall, pSym:dword,count:dword, sz_sym:dword + +@@: + stdcall strncmp, [pSym], [sz_sym], 8 + test eax, eax + jz .ok + add [pSym], 18 + dec [count] + jnz @b + xor eax, eax + ret +.ok: + mov eax, [pSym] + mov eax, [eax+8] + ret +endp + +align 4 +proc get_curr_task + mov eax, [CURRENT_TASK] + shl eax, 8 + ret +endp + +align 4 +proc get_fileinfo stdcall, file_name:dword, info:dword + locals + cmd dd ? + offset dd ? + dd ? + count dd ? + buff dd ? + db ? + name dd ? + endl + + xor eax, eax + mov ebx, [file_name] + mov ecx, [info] + + mov [cmd], 5 + mov [offset], eax + mov [offset+4], eax + mov [count], eax + mov [buff], ecx + mov byte [buff+4], al + mov [name], ebx + + mov eax, 70 + lea ebx, [cmd] + int 0x40 + ret +endp + +align 4 +proc read_file stdcall,file_name:dword, buffer:dword, off:dword,\ + bytes:dword + locals + cmd dd ? + offset dd ? + dd ? + count dd ? + buff dd ? + db ? + name dd ? + endl + + xor eax, eax + mov ebx, [file_name] + mov ecx, [off] + mov edx, [bytes] + mov esi, [buffer] + + mov [cmd], eax + mov [offset], ecx + mov [offset+4], eax + mov [count], edx + mov [buff], esi + mov byte [buff+4], al + mov [name], ebx + + pushad + lea ebx, [cmd] + call file_system_lfn_protected + popad + ret +endp + +; description +; allocate kernel memory and loads the specified file +; +; param +; file_name= path to file +; +; retval +; eax= file image in kernel memory +; ebx= size of file +; +; warging +; You mast call kernel_free() to delete each file +; loaded by the load_file() function + +align 4 +proc load_file stdcall, file_name:dword + locals + attr dd ? + flags dd ? + cr_time dd ? + cr_date dd ? + acc_time dd ? + acc_date dd ? + mod_time dd ? + mod_date dd ? + file_size dd ? + + file dd ? + file2 dd ? + endl + + push esi + push edi + + lea eax, [attr] + stdcall get_fileinfo, [file_name], eax + test eax, eax + jnz .fail + + mov eax, [file_size] + cmp eax, 1024*1024*16 + ja .fail + + stdcall kernel_alloc, [file_size] + mov [file], eax + test eax, eax + jz .fail + + stdcall read_file, [file_name], eax, dword 0, [file_size] + cmp ebx, [file_size] + jne .cleanup + + mov eax, [file] + cmp dword [eax], 0x4B43504B + jne .exit + mov ebx, [eax+4] + mov [file_size], ebx + stdcall kernel_alloc, ebx + + test eax, eax + jz .cleanup + + mov [file2], eax + + pushad + mov ecx, unpack_mutex + call mutex_lock + popad + + stdcall unpack, [file], eax + + pushad + mov ecx, unpack_mutex + call mutex_unlock + popad + + stdcall kernel_free, [file] + mov eax, [file2] + mov ebx, [file_size] +.exit: + push eax + lea edi, [eax+ebx] ;cleanup remain space + mov ecx, 4096 ;from file end + and ebx, 4095 + jz @f + sub ecx, ebx + xor eax, eax + cld + rep stosb +@@: + mov ebx, [file_size] + pop eax + pop edi + pop esi + ret +.cleanup: + stdcall kernel_free, [file] +.fail: + xor eax, eax + xor ebx, ebx + pop edi + pop esi + ret +endp + +; description +; allocate user memory and loads the specified file +; +; param +; file_name= path to file +; +; retval +; eax= file image in user memory +; ebx= size of file +; +; warging +; You mast call kernel_free() to delete each file +; loaded by the load_file() function + +align 4 +proc load_file_umode stdcall, file_name:dword + locals + attr dd ? + flags dd ? + cr_time dd ? + cr_date dd ? + acc_time dd ? + acc_date dd ? + mod_time dd ? + mod_date dd ? + file_size dd ? + + km_file dd ? + um_file dd ? + endl + + push esi + push edi + push ebx + + lea eax, [attr] + stdcall get_fileinfo, [file_name], eax ;find file and get info + test eax, eax + jnz .err_1 + + mov eax, [file_size] + cmp eax, 1024*1024*16 ;to be enough for anybody (c) + ja .err_1 + ;it is very likely that the file is packed + stdcall kernel_alloc, [file_size] ;with kpack, so allocate memory from kernel heap + mov [km_file], eax + test eax, eax + jz .err_1 + + stdcall read_file, [file_name], eax, dword 0, [file_size] + cmp ebx, [file_size] + + jne .err_2 + + mov eax, [km_file] + cmp dword [eax], 0x4B43504B ; check kpack signature + jne .raw_file + + mov ebx, [eax+4] ;get real size of file + mov [file_size], ebx + stdcall user_alloc, ebx ;and allocate space from user heap + mov [um_file], eax + test eax, eax + jz .err_2 + + mov edx, [file_size] ;preallocate page memory + shr eax, 10 + lea edi, [page_tabs+eax] + add edx, 4095 + shr edx, 12 +@@: + call alloc_page + test eax, eax + jz .err_3 + + or eax, PG_UW + stosd + dec edx + jnz @B + + pushad + mov ecx, unpack_mutex + call mutex_lock + + stdcall unpack, [km_file], [um_file] + + mov ecx, unpack_mutex + call mutex_unlock + popad + + stdcall kernel_free, [km_file] ;we don't need packed file anymore +.exit: + + mov edi, [um_file] + mov esi, [um_file] + mov eax, [file_size] + mov edx, eax + + add edi, eax ;cleanup remain space + mov ecx, 4096 ;from file end + and eax, 4095 + jz @f + sub ecx, eax + xor eax, eax + cld + rep stosb +@@: + mov eax, [um_file] + + pop ebx + pop edi + pop esi + ret + +.raw_file: ; sometimes we load unpacked file + stdcall user_alloc, ebx ; allocate space from user heap + mov [um_file], eax + + test eax, eax + jz .err_2 + + shr eax, 10 ; and remap pages. + + mov ecx, [file_size] + add ecx, 4095 + shr ecx, 12 + + mov esi, [km_file] + shr esi, 10 + add esi, page_tabs + + lea edi, [page_tabs+eax] + + cld +@@: + lodsd + and eax, 0xFFFFF000 + or eax, PG_UW + stosd + loop @B + + stdcall free_kernel_space, [km_file] ; release allocated kernel space + jmp .exit ; physical pages still in use +.err_3: + stdcall user_free, [um_file] +.err_2: + stdcall kernel_free, [km_file] +.err_1: + xor eax, eax + xor edx, edx + + pop ebx + pop edi + pop esi + ret +endp + + +uglobal +align 4 +unpack_mutex MUTEX +endg + +align 4 +proc get_proc_ex stdcall uses ebx esi, proc_name:dword, imports:dword + mov ebx, [imports] + test ebx, ebx + jz .end + xor esi, esi +.look_up: + + mov eax, [ebx+32] + mov eax, [OS_BASE+eax+esi*4] + add eax, OS_BASE + stdcall strncmp, eax, [proc_name], 256 + test eax, eax + jz .ok + + inc esi + cmp esi, [ebx+24] + jb .look_up +.end: + xor eax, eax + ret +.ok: + mov eax, [ebx+28] + mov eax, [OS_BASE+eax+esi*4] + add eax, OS_BASE + ret +endp + +align 4 +proc fix_coff_symbols stdcall uses ebx esi, sec:dword, symbols:dword,\ + sym_count:dword, strings:dword, imports:dword + locals + retval dd ? + endl + + mov edi, [symbols] + mov [retval], 1 +.fix: + movzx ebx, [edi+COFF_SYM.SectionNumber] + test ebx, ebx + jnz .internal + mov eax, dword [edi+COFF_SYM.Name] + test eax, eax + jnz @F + + mov edi, [edi+4] + add edi, [strings] +@@: + push edi + stdcall get_proc_ex, edi, [imports] + pop edi + + xor ebx, ebx + test eax, eax + jnz @F + + mov esi, msg_unresolved + call sys_msg_board_str + mov esi, edi + call sys_msg_board_str + mov esi, msg_CR + call sys_msg_board_str + + mov [retval], 0 +@@: + mov edi, [symbols] + mov [edi+COFF_SYM.Value], eax + jmp .next +.internal: + cmp bx, -1 + je .next + cmp bx, -2 + je .next + + dec ebx + shl ebx, 3 + lea ebx, [ebx+ebx*4] + add ebx, [sec] + + mov eax, [ebx+COFF_SECTION.VirtualAddress] + add [edi+COFF_SYM.Value], eax +.next: + add edi, sizeof.COFF_SYM + mov [symbols], edi + dec [sym_count] + jnz .fix + mov eax, [retval] + ret +endp + +align 4 +proc fix_coff_relocs stdcall uses ebx esi, coff:dword, sym:dword, \ + delta:dword + locals + n_sec dd ? + endl + + mov eax, [coff] + movzx ebx, [eax+COFF_HEADER.nSections] + mov [n_sec], ebx + lea esi, [eax+20] +.fix_sec: + mov edi, [esi+COFF_SECTION.PtrReloc] + add edi, [coff] + + movzx ecx, [esi+COFF_SECTION.NumReloc] + test ecx, ecx + jz .next +.reloc_loop: + mov ebx, [edi+COFF_RELOC.SymIndex] + add ebx, ebx + lea ebx, [ebx+ebx*8] + add ebx, [sym] + + mov edx, [ebx+COFF_SYM.Value] + + cmp [edi+COFF_RELOC.Type], 6 + je .dir_32 + + cmp [edi+COFF_RELOC.Type], 20 + jne .next_reloc +.rel_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] + sub edx, eax + sub edx, 4 + jmp .fix +.dir_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] +.fix: + add eax, [delta] + add [eax], edx +.next_reloc: + add edi, 10 + dec ecx + jnz .reloc_loop +.next: + add esi, sizeof.COFF_SECTION + dec [n_sec] + jnz .fix_sec +.exit: + ret +endp + +align 4 +proc rebase_coff stdcall uses ebx esi, coff:dword, sym:dword, \ + delta:dword + locals + n_sec dd ? + endl + + mov eax, [coff] + movzx ebx, [eax+COFF_HEADER.nSections] + mov [n_sec], ebx + lea esi, [eax+20] + mov edx, [delta] +.fix_sec: + mov edi, [esi+COFF_SECTION.PtrReloc] + add edi, [coff] + + movzx ecx, [esi+COFF_SECTION.NumReloc] + test ecx, ecx + jz .next +.reloc_loop: + cmp [edi+COFF_RELOC.Type], 6 + jne .next_reloc +.dir_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] + add [eax+edx], edx +.next_reloc: + add edi, 10 + dec ecx + jnz .reloc_loop +.next: + add esi, sizeof.COFF_SECTION + dec [n_sec] + jnz .fix_sec +.exit: + ret +endp + +align 4 +proc load_driver stdcall, driver_name:dword + locals + coff dd ? + sym dd ? + strings dd ? + img_size dd ? + img_base dd ? + start dd ? + + file_name rb 13+16+4+1 ; '/sys/drivers/.obj' + endl + + lea edx, [file_name] + mov dword [edx], '/sys' + mov dword [edx+4], '/dri' + mov dword [edx+8], 'vers' + mov byte [edx+12], '/' + mov esi, [driver_name] +.redo: + lea edx, [file_name] + lea edi, [edx+13] + mov ecx, 16 +@@: + lodsb + test al, al + jz @f + stosb + loop @b +@@: + mov dword [edi], '.obj' + mov byte [edi+4], 0 + stdcall load_file, edx + + test eax, eax + jz .exit + + mov [coff], eax + + movzx ecx, [eax+COFF_HEADER.nSections] + xor ebx, ebx + + lea edx, [eax+20] +@@: + add ebx, [edx+COFF_SECTION.SizeOfRawData] + add ebx, 15 + and ebx, not 15 + add edx, sizeof.COFF_SECTION + dec ecx + jnz @B + mov [img_size], ebx + + stdcall kernel_alloc, ebx + test eax, eax + jz .fail + mov [img_base], eax + + mov edi, eax + xor eax, eax + mov ecx, [img_size] + add ecx, 4095 + and ecx, not 4095 + shr ecx, 2 + cld + rep stosd + + mov edx, [coff] + movzx ebx, [edx+COFF_HEADER.nSections] + mov edi, [img_base] + lea eax, [edx+20] +@@: + mov [eax+COFF_SECTION.VirtualAddress], edi + mov esi, [eax+COFF_SECTION.PtrRawData] + test esi, esi + jnz .copy + add edi, [eax+COFF_SECTION.SizeOfRawData] + jmp .next +.copy: + add esi, edx + mov ecx, [eax+COFF_SECTION.SizeOfRawData] + cld + rep movsb +.next: + add edi, 15 + and edi, not 15 + add eax, sizeof.COFF_SECTION + dec ebx + jnz @B + + mov ebx, [edx+COFF_HEADER.pSymTable] + add ebx, edx + mov [sym], ebx + mov ecx, [edx+COFF_HEADER.nSymbols] + add ecx, ecx + lea ecx, [ecx+ecx*8];ecx*=18 = nSymbols*CSYM_SIZE + add ecx, [sym] + mov [strings], ecx + + lea eax, [edx+20] + + stdcall fix_coff_symbols, eax, [sym], [edx+COFF_HEADER.nSymbols], \ + [strings], __exports + test eax, eax + jz .link_fail + + mov ebx, [coff] + stdcall fix_coff_relocs, ebx, [sym], 0 + + stdcall get_coff_sym, [sym], [ebx+COFF_HEADER.nSymbols], szVersion + test eax, eax + jz .link_fail + + mov eax, [eax] + shr eax, 16 + cmp eax, DRV_COMPAT + jb .ver_fail + + cmp eax, DRV_CURRENT + ja .ver_fail + + mov ebx, [coff] + stdcall get_coff_sym, [sym], [ebx+COFF_HEADER.nSymbols], szSTART + mov [start], eax + + stdcall kernel_free, [coff] + + mov ebx, [start] + stdcall ebx, DRV_ENTRY + test eax, eax + jnz .ok + + stdcall kernel_free, [img_base] + + xor eax, eax + ret +.ok: + mov ebx, [img_base] + mov [eax+SRV.base], ebx + mov ecx, [start] + mov [eax+SRV.entry], ecx + ret + +.ver_fail: + mov esi, msg_CR + call sys_msg_board_str + mov esi, [driver_name] + call sys_msg_board_str + mov esi, msg_CR + call sys_msg_board_str + mov esi, msg_version + call sys_msg_board_str + mov esi, msg_www + call sys_msg_board_str + jmp .cleanup + +.link_fail: + mov esi, msg_module + call sys_msg_board_str + mov esi, [driver_name] + call sys_msg_board_str + mov esi, msg_CR + call sys_msg_board_str +.cleanup: + stdcall kernel_free, [img_base] +.fail: + stdcall kernel_free, [coff] +.exit: + xor eax, eax + ret +endp + +; in: edx -> COFF_SECTION struct +; out: eax = alignment as mask for bits to drop +coff_get_align: +; Rules: +; - if alignment is not given, use default = 4K; +; - if alignment is given and is no more than 4K, use it; +; - if alignment is more than 4K, revert to 4K. + push ecx + mov cl, byte [edx+COFF_SECTION.Characteristics+2] + mov eax, 1 + shr cl, 4 + dec cl + js .default + cmp cl, 12 + jbe @f +.default: + mov cl, 12 +@@: + shl eax, cl + pop ecx + dec eax + ret + +align 4 +proc load_library stdcall, file_name:dword + locals + fullname rb 260 + fileinfo rb 40 + coff dd ? + img_base dd ? + endl + +; resolve file name + mov ebx, [file_name] + lea edi, [fullname+1] + mov byte [edi-1], '/' + stdcall get_full_file_name, edi, 259 + test al, al + jz .fail + +; scan for required DLL in list of already loaded for this process, +; ignore timestamp + cli + + mov esi, [CURRENT_TASK] + shl esi, 8 + lea edi, [fullname] + mov ebx, [esi+SLOT_BASE+APPDATA.dlls_list_ptr] + test ebx, ebx + jz .not_in_process + mov esi, [ebx+HDLL.fd] +.scan_in_process: + cmp esi, ebx + jz .not_in_process + mov eax, [esi+HDLL.parent] + add eax, DLLDESCR.name + stdcall strncmp, eax, edi, -1 + test eax, eax + jnz .next_in_process +; simple variant: load DLL which is already loaded in this process +; just increment reference counters and return address of exports table + inc [esi+HDLL.refcount] + mov ecx, [esi+HDLL.parent] + inc [ecx+DLLDESCR.refcount] + mov eax, [ecx+DLLDESCR.exports] + sub eax, [ecx+DLLDESCR.defaultbase] + add eax, [esi+HDLL.base] + sti + ret +.next_in_process: + mov esi, [esi+HDLL.fd] + jmp .scan_in_process +.not_in_process: + +; scan in full list, compare timestamp + sti + lea eax, [fileinfo] + stdcall get_fileinfo, edi, eax + test eax, eax + jnz .fail + cli + mov esi, [dll_list.fd] +.scan_for_dlls: + cmp esi, dll_list + jz .load_new + lea eax, [esi+DLLDESCR.name] + stdcall strncmp, eax, edi, -1 + test eax, eax + jnz .continue_scan +.test_prev_dll: + mov eax, dword [fileinfo+24]; last modified time + mov edx, dword [fileinfo+28]; last modified date + cmp dword [esi+DLLDESCR.timestamp], eax + jnz .continue_scan + cmp dword [esi+DLLDESCR.timestamp+4], edx + jz .dll_already_loaded +.continue_scan: + mov esi, [esi+DLLDESCR.fd] + jmp .scan_for_dlls + +; new DLL +.load_new: + sti +; load file + stdcall load_file, edi + test eax, eax + jz .fail + mov [coff], eax + mov dword [fileinfo+32], ebx + +; allocate DLLDESCR struct; size is DLLDESCR.sizeof plus size of DLL name + mov esi, edi + mov ecx, -1 + xor eax, eax + repnz scasb + not ecx + lea eax, [ecx+sizeof.DLLDESCR] + push ecx + call malloc + pop ecx + test eax, eax + jz .fail_and_free_coff +; save timestamp + lea edi, [eax+DLLDESCR.name] + rep movsb + mov esi, eax + mov eax, dword [fileinfo+24] + mov dword [esi+DLLDESCR.timestamp], eax + mov eax, dword [fileinfo+28] + mov dword [esi+DLLDESCR.timestamp+4], eax + +; calculate size of loaded DLL + mov edx, [coff] + movzx ecx, [edx+COFF_HEADER.nSections] + xor ebx, ebx + + add edx, 20 +@@: + call coff_get_align + add ebx, eax + not eax + and ebx, eax + add ebx, [edx+COFF_SECTION.SizeOfRawData] + add edx, sizeof.COFF_SECTION + dec ecx + jnz @B +; it must be nonzero and not too big + mov [esi+DLLDESCR.size], ebx + test ebx, ebx + jz .fail_and_free_dll + cmp ebx, MAX_DEFAULT_DLL_ADDR-MIN_DEFAULT_DLL_ADDR + ja .fail_and_free_dll +; allocate memory for kernel-side image + stdcall kernel_alloc, ebx + test eax, eax + jz .fail_and_free_dll + mov [esi+DLLDESCR.data], eax +; calculate preferred base address + add ebx, 0x1FFF + and ebx, not 0xFFF + mov ecx, [dll_cur_addr] + lea edx, [ecx+ebx] + cmp edx, MAX_DEFAULT_DLL_ADDR + jb @f + mov ecx, MIN_DEFAULT_DLL_ADDR + lea edx, [ecx+ebx] +@@: + mov [esi+DLLDESCR.defaultbase], ecx + mov [dll_cur_addr], edx + +; copy sections and set correct values for VirtualAddress'es in headers + push esi + mov edx, [coff] + movzx ebx, [edx+COFF_HEADER.nSections] + mov edi, eax + add edx, 20 + cld +@@: + call coff_get_align + add ecx, eax + add edi, eax + not eax + and ecx, eax + and edi, eax + mov [edx+COFF_SECTION.VirtualAddress], ecx + add ecx, [edx+COFF_SECTION.SizeOfRawData] + mov esi, [edx+COFF_SECTION.PtrRawData] + push ecx + mov ecx, [edx+COFF_SECTION.SizeOfRawData] + test esi, esi + jnz .copy + xor eax, eax + rep stosb + jmp .next +.copy: + add esi, [coff] + rep movsb +.next: + pop ecx + add edx, sizeof.COFF_SECTION + dec ebx + jnz @B + pop esi + +; save some additional data from COFF file +; later we will use COFF header, headers for sections and symbol table +; and also relocations table for all sections + mov edx, [coff] + mov ebx, [edx+COFF_HEADER.pSymTable] + mov edi, dword [fileinfo+32] + sub edi, ebx + jc .fail_and_free_data + mov [esi+DLLDESCR.symbols_lim], edi + add ebx, edx + movzx ecx, [edx+COFF_HEADER.nSections] + lea ecx, [ecx*5] + lea edi, [edi+ecx*8+20] + add edx, 20 +@@: + movzx eax, [edx+COFF_SECTION.NumReloc] + lea eax, [eax*5] + lea edi, [edi+eax*2] + add edx, sizeof.COFF_SECTION + sub ecx, 5 + jnz @b + stdcall kernel_alloc, edi + test eax, eax + jz .fail_and_free_data + mov edx, [coff] + movzx ecx, [edx+COFF_HEADER.nSections] + lea ecx, [ecx*5] + lea ecx, [ecx*2+5] + mov [esi+DLLDESCR.coff_hdr], eax + push esi + mov esi, edx + mov edi, eax + rep movsd + pop esi + mov [esi+DLLDESCR.symbols_ptr], edi + push esi + mov ecx, [edx+COFF_HEADER.nSymbols] + mov [esi+DLLDESCR.symbols_num], ecx + mov ecx, [esi+DLLDESCR.symbols_lim] + mov esi, ebx + rep movsb + pop esi + mov ebx, [esi+DLLDESCR.coff_hdr] + push esi + movzx eax, [edx+COFF_HEADER.nSections] + lea edx, [ebx+20] +@@: + movzx ecx, [edx+COFF_SECTION.NumReloc] + lea ecx, [ecx*5] + mov esi, [edx+COFF_SECTION.PtrReloc] + mov [edx+COFF_SECTION.PtrReloc], edi + sub [edx+COFF_SECTION.PtrReloc], ebx + add esi, [coff] + shr ecx, 1 + rep movsd + adc ecx, ecx + rep movsw + add edx, sizeof.COFF_SECTION + dec eax + jnz @b + pop esi + +; fixup symbols + mov edx, ebx + mov eax, [ebx+COFF_HEADER.nSymbols] + add edx, 20 + mov ecx, [esi+DLLDESCR.symbols_num] + lea ecx, [ecx*9] + add ecx, ecx + add ecx, [esi+DLLDESCR.symbols_ptr] + + stdcall fix_coff_symbols, edx, [esi+DLLDESCR.symbols_ptr], eax, \ + ecx, 0 +; test eax, eax +; jnz @F +; +;@@: + + stdcall get_coff_sym, [esi+DLLDESCR.symbols_ptr], [ebx+COFF_HEADER.nSymbols], szEXPORTS + test eax, eax + jnz @F + + stdcall get_coff_sym, [esi+DLLDESCR.symbols_ptr], [ebx+COFF_HEADER.nSymbols], sz_EXPORTS +@@: + mov [esi+DLLDESCR.exports], eax + +; fix relocs in the hidden copy in kernel memory to default address +; it is first fix; usually this will be enough, but second fix +; can be necessary if real load address will not equal assumption + mov eax, [esi+DLLDESCR.data] + sub eax, [esi+DLLDESCR.defaultbase] + stdcall fix_coff_relocs, ebx, [esi+DLLDESCR.symbols_ptr], eax + + stdcall kernel_free, [coff] + + cli +; initialize DLLDESCR struct + and dword [esi+DLLDESCR.refcount], 0; no HDLLs yet; later it will be incremented + mov [esi+DLLDESCR.fd], dll_list + mov eax, [dll_list.bk] + mov [dll_list.bk], esi + mov [esi+DLLDESCR.bk], eax + mov [eax+DLLDESCR.fd], esi +.dll_already_loaded: + inc [esi+DLLDESCR.refcount] + push esi + call init_heap + pop esi + + mov edi, [esi+DLLDESCR.size] + stdcall user_alloc_at, [esi+DLLDESCR.defaultbase], edi + test eax, eax + jnz @f + stdcall user_alloc, edi + test eax, eax + jz .fail_and_dereference +@@: + mov [img_base], eax + mov eax, sizeof.HDLL + call malloc + test eax, eax + jz .fail_and_free_user + mov ebx, [CURRENT_TASK] + shl ebx, 5 + mov edx, [CURRENT_TASK+ebx+TASKDATA.pid] + mov [eax+HDLL.pid], edx + push eax + call init_dlls_in_thread + pop ebx + test eax, eax + jz .fail_and_free_user + mov edx, [eax+HDLL.fd] + mov [ebx+HDLL.fd], edx + mov [ebx+HDLL.bk], eax + mov [eax+HDLL.fd], ebx + mov [edx+HDLL.bk], ebx + mov eax, ebx + mov ebx, [img_base] + mov [eax+HDLL.base], ebx + mov [eax+HDLL.size], edi + mov [eax+HDLL.refcount], 1 + mov [eax+HDLL.parent], esi + mov edx, ebx + shr edx, 12 + or dword [page_tabs+(edx-1)*4], DONT_FREE_BLOCK +; copy entries of page table from kernel-side image to usermode +; use copy-on-write for user-mode image, so map as readonly + xor edi, edi + mov ecx, [esi+DLLDESCR.data] + shr ecx, 12 +.map_pages_loop: + mov eax, [page_tabs+ecx*4] + and eax, not 0xFFF + or al, PG_USER + xchg eax, [page_tabs+edx*4] + test al, 1 + jz @f + call free_page +@@: + invlpg [ebx+edi] + inc ecx + inc edx + add edi, 0x1000 + cmp edi, [esi+DLLDESCR.size] + jb .map_pages_loop + +; if real user-mode base is not equal to preferred base, relocate image + sub ebx, [esi+DLLDESCR.defaultbase] + jz @f + stdcall rebase_coff, [esi+DLLDESCR.coff_hdr], [esi+DLLDESCR.symbols_ptr], ebx +@@: + + mov eax, [esi+DLLDESCR.exports] + sub eax, [esi+DLLDESCR.defaultbase] + add eax, [img_base] + sti + ret +.fail_and_free_data: + stdcall kernel_free, [esi+DLLDESCR.data] +.fail_and_free_dll: + mov eax, esi + call free +.fail_and_free_coff: + stdcall kernel_free, [coff] +.fail: + xor eax, eax + ret +.fail_and_free_user: + stdcall user_free, [img_base] +.fail_and_dereference: + mov eax, 1 ; delete 1 reference + call dereference_dll + sti + xor eax, eax + ret +endp + +; initialize [APPDATA.dlls_list_ptr] for given thread +; DLL is per-process object, so APPDATA.dlls_list_ptr must be +; kept in sync for all threads of one process. +; out: eax = APPDATA.dlls_list_ptr if all is OK, +; NULL if memory allocation failed +init_dlls_in_thread: + mov ebx, [current_slot] + mov eax, [ebx+APPDATA.dlls_list_ptr] + test eax, eax + jnz .ret + push [ebx+APPDATA.process] + mov eax, 8 + call malloc + pop edx + test eax, eax + jz .ret + mov [eax], eax + mov [eax+4], eax + mov ecx, [TASK_COUNT] + mov ebx, SLOT_BASE+256 +.set: + cmp [ebx+APPDATA.process], edx + jnz @f + mov [ebx+APPDATA.dlls_list_ptr], eax +@@: + add ebx, 256 + dec ecx + jnz .set +.ret: + ret + +; in: eax = number of references to delete, esi -> DLLDESCR struc +dereference_dll: + sub [esi+DLLDESCR.refcount], eax + jnz .ret + mov eax, [esi+DLLDESCR.fd] + mov edx, [esi+DLLDESCR.bk] + mov [eax+DLLDESCR.bk], edx + mov [edx+DLLDESCR.fd], eax + stdcall kernel_free, [esi+DLLDESCR.coff_hdr] + stdcall kernel_free, [esi+DLLDESCR.data] + mov eax, esi + call free +.ret: + ret + +destroy_hdll: + push ebx ecx esi edi + push eax + mov ebx, [eax+HDLL.base] + mov esi, [eax+HDLL.parent] + mov edx, [esi+DLLDESCR.size] +; The following actions require the context of application where HDLL is mapped. +; However, destroy_hdll can be called in the context of OS thread when +; cleaning up objects created by the application which is destroyed. +; So remember current cr3 and set it to page table of target. + mov eax, [ecx+APPDATA.process] +; Because we cheat with cr3, disable interrupts: task switch would restore +; page table from APPDATA of current thread. +; Also set [current_slot] because it is used by user_free. + pushf + cli + push [current_slot] + mov [current_slot], ecx + mov ecx, cr3 + push ecx + mov cr3, eax + push ebx ; argument for user_free + mov eax, ebx + shr ebx, 12 + push ebx + mov esi, [esi+DLLDESCR.data] + shr esi, 12 +.unmap_loop: + push eax + mov eax, 2 + xchg eax, [page_tabs+ebx*4] + mov ecx, [page_tabs+esi*4] + and eax, not 0xFFF + and ecx, not 0xFFF + cmp eax, ecx + jz @f + call free_page +@@: + pop eax + invlpg [eax] + add eax, 0x1000 + inc ebx + inc esi + sub edx, 0x1000 + ja .unmap_loop + pop ebx + and dword [page_tabs+(ebx-1)*4], not DONT_FREE_BLOCK + call user_free +; Restore context. + pop eax + mov cr3, eax + pop [current_slot] + popf +; Ok, cheating is done. + pop eax + push eax + mov esi, [eax+HDLL.parent] + mov eax, [eax+HDLL.refcount] + call dereference_dll + pop eax + mov edx, [eax+HDLL.bk] + mov ebx, [eax+HDLL.fd] + mov [ebx+HDLL.bk], edx + mov [edx+HDLL.fd], ebx + call free + pop edi esi ecx ebx + ret + +; ecx -> APPDATA for slot, esi = dlls_list_ptr +destroy_all_hdlls: + test esi, esi + jz .ret +.loop: + mov eax, [esi+HDLL.fd] + cmp eax, esi + jz free + call destroy_hdll + jmp .loop +.ret: + ret + +align 4 +stop_all_services: + push ebp + mov edx, [srv.fd] +.next: + cmp edx, srv.fd-SRV.fd + je .done + cmp [edx+SRV.magic], ' SRV' + jne .next + cmp [edx+SRV.size], sizeof.SRV + jne .next + + mov ebx, [edx+SRV.entry] + mov edx, [edx+SRV.fd] + test ebx, ebx + jz .next + + push edx + mov ebp, esp + push 0 + push -1 + call ebx + mov esp, ebp + pop edx + jmp .next +.done: + pop ebp + ret + +; param +; eax= size +; ebx= pid + +align 4 +create_kernel_object: + + push ebx + call malloc + pop ebx + test eax, eax + jz .fail + + mov ecx, [current_slot] + add ecx, APP_OBJ_OFFSET + + pushfd + cli + mov edx, [ecx+APPOBJ.fd] + mov [eax+APPOBJ.fd], edx + mov [eax+APPOBJ.bk], ecx + mov [eax+APPOBJ.pid], ebx + + mov [ecx+APPOBJ.fd], eax + mov [edx+APPOBJ.bk], eax + popfd +.fail: + ret + +; param +; eax= object + +align 4 +destroy_kernel_object: + + pushfd + cli + mov ebx, [eax+APPOBJ.fd] + mov ecx, [eax+APPOBJ.bk] + mov [ebx+APPOBJ.bk], ecx + mov [ecx+APPOBJ.fd], ebx + popfd + + xor edx, edx ;clear common header + mov [eax], edx + mov [eax+4], edx + mov [eax+8], edx + mov [eax+12], edx + mov [eax+16], edx + + call free ;release object memory + ret diff --git a/kernel/branches/kolibri-process/core/export.inc b/kernel/branches/kolibri-process/core/export.inc new file mode 100644 index 000000000..1bb24e6f4 --- /dev/null +++ b/kernel/branches/kolibri-process/core/export.inc @@ -0,0 +1,40 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + +; Macroinstruction for making export section + + +macro export dllname,[label,string] + { common + local module,addresses,names,ordinal,count + count = 0 + forward + count = count+1 + common + dd 0,0,0, (module-OS_BASE) , 1 + dd count,count,(addresses-OS_BASE),(names-OS_BASE),(ordinal-OS_BASE) + addresses: + forward + dd (label-OS_BASE) + common + names: + forward + local name + dd (name-OS_BASE) + common + ordinal: + count = 0 + forward + dw count + count = count+1 + common + module db dllname,0 + forward + name db string,0 + } diff --git a/kernel/branches/kolibri-process/core/exports.inc b/kernel/branches/kolibri-process/core/exports.inc new file mode 100644 index 000000000..2d80a3368 --- /dev/null +++ b/kernel/branches/kolibri-process/core/exports.inc @@ -0,0 +1,128 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4418 $ + +iglobal + szKernel db 'KERNEL', 0 + szVersion db 'version',0 +endg + + align 4 +__exports: + export 'KERNEL', \ + alloc_kernel_space, 'AllocKernelSpace', \ ; stdcall + alloc_page, 'AllocPage', \ ; gcc ABI + alloc_pages, 'AllocPages', \ ; stdcall + commit_pages, 'CommitPages', \ ; eax, ebx, ecx +\ + disk_add, 'DiskAdd', \ ;stdcall + disk_del, 'DiskDel', \ + disk_media_changed, 'DiskMediaChanged', \ ;stdcall +\ + create_event, 'CreateEvent', \ ; ecx, esi + destroy_event, 'DestroyEvent', \ ; + raise_event, 'RaiseEvent', \ ; eax, ebx, edx, esi + wait_event, 'WaitEvent', \ ; eax, ebx + wait_event_timeout, 'WaitEventTimeout', \ ; eax, ebx, ecx + get_event_ex, 'GetEvent', \ ; edi + clear_event, 'ClearEvent', \ ;see EVENT.inc for specification + send_event, 'SendEvent', \ ;see EVENT.inc for specification +\ + create_kernel_object, 'CreateObject', \ + create_ring_buffer, 'CreateRingBuffer', \ ; stdcall + destroy_kernel_object, 'DestroyObject', \ + free_kernel_space, 'FreeKernelSpace', \ ; stdcall + free_page, 'FreePage', \ ; eax + kernel_alloc, 'KernelAlloc', \ ; stdcall + kernel_free, 'KernelFree', \ ; stdcall + malloc, 'Kmalloc', \ + free, 'Kfree', \ + map_io_mem, 'MapIoMem', \ ; stdcall + map_page, 'MapPage', \ ; stdcall + get_pg_addr, 'GetPgAddr', \ ; eax + get_phys_addr, 'GetPhysAddr', \ ; eax + map_space, 'MapSpace', \ + release_pages, 'ReleasePages', \ +\ + mutex_init, 'MutexInit', \ ; gcc fastcall + mutex_lock, 'MutexLock', \ ; gcc fastcall + mutex_unlock, 'MutexUnlock', \ ; gcc fastcall +\ + get_display, 'GetDisplay', \ + set_screen, 'SetScreen', \ + window._.get_rect, 'GetWindowRect', \ ; gcc fastcall + pci_api_drv, 'PciApi', \ + pci_read8, 'PciRead8', \ ; stdcall + pci_read16, 'PciRead16', \ ; stdcall + pci_read32, 'PciRead32', \ ; stdcall + pci_write8, 'PciWrite8', \ ; stdcall + pci_write16, 'PciWrite16', \ ; stdcall + pci_write32, 'PciWrite32', \ ; stdcall +\ + get_pid, 'GetPid', \ + get_service, 'GetService', \ ; + reg_service, 'RegService', \ ; stdcall + attach_int_handler, 'AttachIntHandler', \ ; stdcall + user_alloc, 'UserAlloc', \ ; stdcall + user_alloc_at, 'UserAllocAt', \ ; stdcall + user_free, 'UserFree', \ ; stdcall + unmap_pages, 'UnmapPages', \ ; eax, ecx + sys_msg_board_str, 'SysMsgBoardStr', \ + sys_msg_board, 'SysMsgBoard', \ + get_timer_ticks, 'GetTimerTicks', \ + get_stack_base, 'GetStackBase', \ + delay_hs, 'Delay', \ ; ebx + set_mouse_data, 'SetMouseData', \ ; + set_keyboard_data, 'SetKeyboardData', \ ; gcc fastcall + register_keyboard, 'RegKeyboard', \ + delete_keyboard, 'DelKeyboard', \ + get_cpu_freq, 'GetCpuFreq', \ +\ + new_sys_threads, 'CreateThread', \ ; ebx, ecx, edx +\ + srv_handler, 'ServiceHandler', \ + fpu_save, 'FpuSave', \ + fpu_restore, 'FpuRestore', \ + r_f_port_area, 'ReservePortArea', \ + boot_log, 'Boot_Log', \ +\ + load_cursor, 'LoadCursor', \ ;stdcall +\ + get_curr_task, 'GetCurrentTask', \ + load_file, 'LoadFile', \ ;retval eax, ebx + delay_ms, 'Sleep', \ +\ + strncat, 'strncat', \ + strncpy, 'strncpy', \ + strncmp, 'strncmp', \ + strnlen, 'strnlen', \ + strchr, 'strchr', \ + strrchr, 'strrchr', \ +\ + timer_hs, 'TimerHS', \ + timer_hs, 'TimerHs', \ ; shit happens + cancel_timer_hs, 'CancelTimerHS', \ +\ + reg_usb_driver, 'RegUSBDriver', \ + usb_open_pipe, 'USBOpenPipe', \ + usb_close_pipe, 'USBClosePipe', \ + usb_normal_transfer_async, 'USBNormalTransferAsync', \ + usb_control_async, 'USBControlTransferAsync', \ + usb_get_param, 'USBGetParam', \ + usb_hc_func, 'USBHCFunc', \ +\ + NET_add_device, 'NetRegDev', \ + NET_remove_device, 'NetUnRegDev', \ + NET_ptr_to_num, 'NetPtrToNum', \ + NET_link_changed, 'NetLinkChanged', \ + ETH_input, 'Eth_input', \ +\ + 0, 'LFBAddress' ; must be the last one +load kernel_exports_count dword from __exports + 24 +load kernel_exports_addresses dword from __exports + 28 +exp_lfb = OS_BASE + kernel_exports_addresses + (kernel_exports_count - 1) * 4 - 4 diff --git a/kernel/branches/kolibri-process/core/ext_lib.inc b/kernel/branches/kolibri-process/core/ext_lib.inc new file mode 100644 index 000000000..31dd7dbb8 --- /dev/null +++ b/kernel/branches/kolibri-process/core/ext_lib.inc @@ -0,0 +1,333 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;============================================================================ +; +; External kernel dependencies (libraries) loading +; +;============================================================================ + +$Revision: 2455 $ + +if 0 +; The code currently does not work. Kill "if 0/end if" only after correcting +; to current kernel (dll.inc). +macro library [name,fname] +{ + forward + dd __#name#_library_table__,__#name#_library_name__ + common + dd 0 + forward + __#name#_library_name__ db fname,0 +} + +macro import lname,[name,sname] +{ + common + align 4 + __#lname#_library_table__: + forward + name dd __#name#_import_name__ + common + dd 0 + forward + __#name#_import_name__ db sname,0 +} + +macro export [name,sname] +{ +align 4 + forward + dd __#name#_export_name__,name + common + dd 0 + forward + __#name#_export_name__ db sname,0 +} + + + +align 4 ; loading library (use kernel functions) +proc load_k_library stdcall, file_name:dword + locals + coff dd ? + sym dd ? + strings dd ? + img_size dd ? + img_base dd ? + exports dd ? + endl + + cli + + stdcall load_file, [file_name] + test eax, eax + jz .fail + + mov [coff], eax + movzx ecx, [eax+CFH.nSections] + xor ebx, ebx + + lea edx, [eax+20] +@@: + add ebx, [edx+CFS.SizeOfRawData] + add ebx, 15 + and ebx, not 15 + add edx, COFF_SECTION_SIZE + dec ecx + jnz @B + mov [img_size], ebx + + stdcall kernel_alloc, [img_size] + + test eax, eax + jz .fail + mov [img_base], eax + + mov edx, [coff] + movzx ebx, [edx+CFH.nSections] + mov edi, [img_base] + lea eax, [edx+20] +@@: + mov [eax+CFS.VirtualAddress], edi + mov esi, [eax+CFS.PtrRawData] + test esi, esi + jnz .copy + add edi, [eax+CFS.SizeOfRawData] + jmp .next +.copy: + add esi, edx + mov ecx, [eax+CFS.SizeOfRawData] + cld + rep movsb +.next: + add edi, 15 + and edi, not 15 + add eax, COFF_SECTION_SIZE + dec ebx + jnz @B + + mov ebx, [edx+CFH.pSymTable] + add ebx, edx + mov [sym], ebx + mov ecx, [edx+CFH.nSymbols] + add ecx, ecx + lea ecx, [ecx+ecx*8];ecx*=18 = nSymbols*CSYM_SIZE + add ecx, [sym] + mov [strings], ecx + + lea eax, [edx+20] + + stdcall fix_coff_symbols, eax, [sym], [edx+CFH.nSymbols], \ + [strings], dword 0 + test eax, eax + jnz @F + +@@: + mov edx, [coff] + movzx ebx, [edx+CFH.nSections] + mov edi, 0 + lea eax, [edx+20] +@@: + add [eax+CFS.VirtualAddress], edi ;patch user space offset + add eax, COFF_SECTION_SIZE + dec ebx + jnz @B + + add edx, 20 + stdcall fix_coff_relocs, [coff], edx, [sym] + + mov ebx, [coff] + stdcall get_coff_sym, [sym], [ebx+CFH.nSymbols], szEXPORTS + mov [exports], eax + + stdcall kernel_free, [coff] + + mov eax, [exports] + ret +.fail: + xor eax, eax + ret +endp + + +proc dll.Load, import_table:dword + mov esi, [import_table] + .next_lib: + mov edx, [esi] + or edx, edx + jz .exit + push esi + + mov edi, s_libname + + mov al, '/' + stosb + mov esi, sysdir_path + @@: + lodsb + stosb + or al, al + jnz @b + dec edi + mov [edi], dword '/lib' + mov [edi+4], byte '/' + add edi, 5 + pop esi + push esi + mov esi, [esi+4] + @@: + lodsb + stosb + or al, al + jnz @b + + pushad + stdcall load_k_library, s_libname + mov [esp+28], eax + popad + or eax, eax + jz .fail + stdcall dll.Link, eax, edx + stdcall dll.Init, [eax+4] + pop esi + add esi, 8 + jmp .next_lib + .exit: + xor eax, eax + ret + .fail: + add esp, 4 + xor eax, eax + inc eax + ret +endp + +proc dll.Link, exp:dword,imp:dword + push eax + mov esi, [imp] + test esi, esi + jz .done + .next: + lodsd + test eax, eax + jz .done + stdcall dll.GetProcAddress, [exp], eax + or eax, eax + jz @f + mov [esi-4], eax + jmp .next + @@: + mov dword[esp], 0 + .done: + pop eax + ret +endp + +proc dll.Init, dllentry:dword + pushad + mov eax, mem.Alloc + mov ebx, mem.Free + mov ecx, mem.ReAlloc + mov edx, dll.Load + stdcall [dllentry] + popad + ret +endp + +proc dll.GetProcAddress, exp:dword,sz_name:dword + mov edx, [exp] + .next: + test edx, edx + jz .end + stdcall strncmp, [edx], [sz_name], dword -1 + test eax, eax + jz .ok + add edx, 8 + jmp .next + .ok: + mov eax, [edx+4] + .end: + ret +endp + +;----------------------------------------------------------------------------- +proc mem.Alloc size ;///////////////////////////////////////////////////////// +;----------------------------------------------------------------------------- + push ebx ecx +; mov eax,[size] +; lea ecx,[eax+4+4095] +; and ecx,not 4095 +; stdcall kernel_alloc, ecx +; add ecx,-4 +; mov [eax],ecx +; add eax,4 + + stdcall kernel_alloc, [size] + + pop ecx ebx + ret +endp + +;----------------------------------------------------------------------------- +proc mem.ReAlloc mptr,size;/////////////////////////////////////////////////// +;----------------------------------------------------------------------------- + push ebx ecx esi edi eax + mov eax, [mptr] + mov ebx, [size] + or eax, eax + jz @f + lea ecx, [ebx+4+4095] + and ecx, not 4095 + add ecx, -4 + cmp ecx, [eax-4] + je .exit + @@: + mov eax, ebx + call mem.Alloc + xchg eax, [esp] + or eax, eax + jz .exit + mov esi, eax + xchg eax, [esp] + mov edi, eax + mov ecx, [esi-4] + cmp ecx, [edi-4] + jbe @f + mov ecx, [edi-4] + @@: + add ecx, 3 + shr ecx, 2 + cld + rep movsd + xchg eax, [esp] + call mem.Free + .exit: + pop eax edi esi ecx ebx + ret +endp + +;----------------------------------------------------------------------------- +proc mem.Free mptr ;////////////////////////////////////////////////////////// +;----------------------------------------------------------------------------- +; mov eax,[mptr] +; or eax,eax +; jz @f +; push ebx ecx +; lea ecx,[eax-4] +; stdcall kernel_free, ecx +; pop ecx ebx +; @@: ret + stdcall kernel_free, [mptr] + ret +endp + +uglobal +s_libname db 64 dup (0) +endg +end if diff --git a/kernel/branches/kolibri-process/core/fpu.inc b/kernel/branches/kolibri-process/core/fpu.inc new file mode 100644 index 000000000..249a8d79b --- /dev/null +++ b/kernel/branches/kolibri-process/core/fpu.inc @@ -0,0 +1,183 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3534 $ + + +init_fpu: + clts + fninit + + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + mov ebx, cr4 + mov ecx, cr0 + or ebx, CR4_OSFXSR+CR4_OSXMMEXPT + mov cr4, ebx + + and ecx, not (CR0_MP+CR0_EM) + or ecx, CR0_NE + mov cr0, ecx + + mov dword [esp-4], SSE_INIT + ldmxcsr [esp-4] + + xorps xmm0, xmm0 + xorps xmm1, xmm1 + xorps xmm2, xmm2 + xorps xmm3, xmm3 + xorps xmm4, xmm4 + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + fxsave [fpu_data] ;[eax] + ret +.no_SSE: + mov ecx, cr0 + and ecx, not CR0_EM + or ecx, CR0_MP+CR0_NE + mov cr0, ecx + fnsave [fpu_data] + ret + +; param +; eax= 512 bytes memory area + +align 4 +fpu_save: + push ecx + push esi + push edi + + pushfd + cli + + clts + mov edi, eax + + mov ecx, [fpu_owner] + mov esi, [CURRENT_TASK] + cmp ecx, esi + jne .save + + call save_context + jmp .exit +.save: + mov [fpu_owner], esi + + shl ecx, 8 + mov eax, [ecx+SLOT_BASE+APPDATA.fpu_state] + + call save_context + + shl esi, 8 + mov esi, [esi+SLOT_BASE+APPDATA.fpu_state] + mov ecx, 512/4 + cld + rep movsd + fninit +.exit: + popfd + pop edi + pop esi + pop ecx + ret + +align 4 +save_context: + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxsave [eax] + ret +.no_SSE: + fnsave [eax] + ret + +align 4 +fpu_restore: + push ecx + push esi + + mov esi, eax + + pushfd + cli + + mov ecx, [fpu_owner] + mov eax, [CURRENT_TASK] + cmp ecx, eax + jne .copy + + clts + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxrstor [esi] + popfd + pop esi + pop ecx + ret +.no_SSE: + fnclex ;fix possible problems + frstor [esi] + popfd + pop esi + pop ecx + ret +.copy: + shl eax, 8 + mov edi, [eax+SLOT_BASE+APPDATA.fpu_state] + mov ecx, 512/4 + cld + rep movsd + popfd + pop esi + pop ecx + ret + +align 4 +except_7: ;#NM exception handler + save_ring3_context + clts + mov ax, app_data; + mov ds, ax + mov es, ax + + mov ebx, [fpu_owner] + cmp ebx, [CURRENT_TASK] + je .exit + + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxsave [eax] + mov ebx, [CURRENT_TASK] + mov [fpu_owner], ebx + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + fxrstor [eax] +.exit: + restore_ring3_context + iret + +.no_SSE: + fnsave [eax] + mov ebx, [CURRENT_TASK] + mov [fpu_owner], ebx + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + frstor [eax] + restore_ring3_context + iret + +iglobal + fpu_owner dd 2 +endg diff --git a/kernel/branches/kolibri-process/core/heap.inc b/kernel/branches/kolibri-process/core/heap.inc new file mode 100644 index 000000000..b98e29d9c --- /dev/null +++ b/kernel/branches/kolibri-process/core/heap.inc @@ -0,0 +1,1519 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4424 $ + + +struct MEM_BLOCK + list LHEAD + next_block dd ? ;+8 + prev_block dd ? ;+4 + base dd ? ;+16 + size dd ? ;+20 + flags dd ? ;+24 + handle dd ? ;+28 +ends + +FREE_BLOCK equ 4 +USED_BLOCK equ 8 +DONT_FREE_BLOCK equ 10h + + +block_next equ MEM_BLOCK.next_block +block_prev equ MEM_BLOCK.prev_block +list_fd equ MEM_BLOCK.list.next +list_bk equ MEM_BLOCK.list.prev +block_base equ MEM_BLOCK.base +block_size equ MEM_BLOCK.size +block_flags equ MEM_BLOCK.flags + +macro calc_index op +{ shr op, 12 + dec op + cmp op, 63 + jna @f + mov op, 63 +@@: +} + +align 4 +md: +.add_to_used: + mov eax, [esi+block_base] + mov ebx, [esi+block_base] + shr ebx, 6 + add eax, ebx + shr ebx, 6 + add eax, ebx + shr eax, 12 + and eax, 63 + inc [mem_hash_cnt+eax*4] + + lea ecx, [mem_used_list+eax*8] + list_add esi, ecx + mov [esi+block_flags], USED_BLOCK + mov eax, [esi+block_size] + sub [heap_free], eax + ret +align 4 +.find_used: + mov ecx, eax + mov ebx, eax + shr ebx, 6 + add ecx, ebx + shr ebx, 6 + add ecx, ebx + shr ecx, 12 + and ecx, 63 + + lea ebx, [mem_used_list+ecx*8] + mov esi, ebx +.next: + mov esi, [esi+list_fd] + cmp esi, ebx + je .fail + + cmp eax, [esi+block_base] + jne .next + + ret +.fail: + xor esi, esi + ret + +align 4 +.del_from_used: + call .find_used + test esi, esi + jz .done + + cmp [esi+block_flags], USED_BLOCK + jne .fatal + + dec [mem_hash_cnt+ecx*4] + list_del esi +.done: + ret +.fatal: ;FIXME panic here + xor esi, esi + ret + +;Initial heap state +; +;+heap_size terminator USED_BLOCK +;+4096*MEM_BLOCK.sizeof free space FREE_BLOCK +;HEAP_BASE heap_descriptors USED_BLOCK +; + +align 4 +proc init_kernel_heap + + mov ecx, 64 + mov edi, mem_block_list +@@: + mov eax, edi + stosd + stosd + loop @B + + mov ecx, 64 + mov edi, mem_used_list +@@: + mov eax, edi + stosd + stosd + loop @B + + stdcall alloc_pages, dword 32 + + or eax, PG_SW + mov ebx, HEAP_BASE + mov ecx, 32 + call commit_pages + + mov edi, HEAP_BASE ;descriptors + mov ebx, HEAP_BASE+sizeof.MEM_BLOCK ;free space + mov ecx, HEAP_BASE+sizeof.MEM_BLOCK*2 ;terminator + + xor eax, eax + mov [edi+block_next], ebx + mov [edi+block_prev], eax + mov [edi+list_fd], eax + mov [edi+list_bk], eax + mov [edi+block_base], HEAP_BASE + mov [edi+block_size], 4096*sizeof.MEM_BLOCK + mov [edi+block_flags], USED_BLOCK + + mov [ecx+block_next], eax + mov [ecx+block_prev], ebx + mov [ecx+list_fd], eax + mov [ecx+list_bk], eax + mov [ecx+block_base], eax + mov [ecx+block_size], eax + mov [ecx+block_flags], USED_BLOCK + + mov [ebx+block_next], ecx + mov [ebx+block_prev], edi + mov [ebx+block_base], HEAP_BASE+4096*sizeof.MEM_BLOCK + + mov ecx, [pg_data.kernel_pages] + shl ecx, 12 + sub ecx, HEAP_BASE-OS_BASE+4096*sizeof.MEM_BLOCK + mov [heap_size], ecx + mov [heap_free], ecx + mov [ebx+block_size], ecx + mov [ebx+block_flags], FREE_BLOCK + + mov [mem_block_mask], eax + mov [mem_block_mask+4], 0x80000000 + + mov ecx, mem_block_list+63*8 + list_add ebx, ecx + + mov ecx, 4096-3-1 + mov eax, HEAP_BASE+sizeof.MEM_BLOCK*4 + + mov [next_memblock], HEAP_BASE+sizeof.MEM_BLOCK *3 +@@: + mov [eax-sizeof.MEM_BLOCK], eax + add eax, sizeof.MEM_BLOCK + loop @B + + mov [eax-sizeof.MEM_BLOCK], dword 0 + + mov ecx, heap_mutex + call mutex_init + mov [heap_blocks], 4094 + mov [free_blocks], 4093 + ret +endp + +; param +; eax= required size +; +; retval +; edi= memory block descriptor +; ebx= descriptor index + +align 4 +get_small_block: + mov ecx, eax + shr ecx, 12 + dec ecx + cmp ecx, 63 + jle .get_index + mov ecx, 63 +.get_index: + lea esi, [mem_block_mask] + xor ebx, ebx + or edx, -1 + + cmp ecx, 32 + jb .bit_test + + sub ecx, 32 + add ebx, 32 + add esi, 4 +.bit_test: + shl edx, cl + and edx, [esi] +.find: + bsf edi, edx + jz .high_mask + add ebx, edi + lea ecx, [mem_block_list+ebx*8] + mov edi, ecx +.next: + mov edi, [edi+list_fd] + cmp edi, ecx + je .err + cmp eax, [edi+block_size] + ja .next + ret +.err: + xor edi, edi + ret + +.high_mask: + add esi, 4 + cmp esi, mem_block_mask+8 + jae .err + add ebx, 32 + mov edx, [esi] + jmp .find + + +align 4 +free_mem_block: + mov ebx, [next_memblock] + mov [eax], ebx + mov [next_memblock], eax + xor ebx, ebx + + mov dword [eax+4], ebx + mov dword [eax+8], ebx + mov dword [eax+12], ebx + mov dword [eax+16], ebx +; mov dword [eax+20], 0 ;don't clear block size + mov dword [eax+24], ebx + mov dword [eax+28], ebx + inc [free_blocks] + ret + +align 4 +proc alloc_kernel_space stdcall, size:dword + local block_ind:DWORD + + push ebx + push esi + push edi + + mov eax, [size] + add eax, 4095 + and eax, not 4095 + mov [size], eax + + cmp eax, [heap_free] + ja .error + + spin_lock_irqsave heap_mutex + + mov eax, [size] + + call get_small_block ; eax + test edi, edi + jz .error_unlock + + cmp [edi+block_flags], FREE_BLOCK + jne .error_unlock + + mov [block_ind], ebx ;index of allocated block + + mov eax, [edi+block_size] + cmp eax, [size] + je .m_eq_size + + mov esi, [next_memblock] ;new memory block + test esi, esi + jz .error_unlock + + dec [free_blocks] + mov eax, [esi] + mov [next_memblock], eax + + mov [esi+block_next], edi + mov eax, [edi+block_prev] + mov [esi+block_prev], eax + mov [edi+block_prev], esi + mov [esi+list_fd], 0 + mov [esi+list_bk], 0 + mov [eax+block_next], esi + + mov ebx, [edi+block_base] + mov [esi+block_base], ebx + mov edx, [size] + mov [esi+block_size], edx + add [edi+block_base], edx + sub [edi+block_size], edx + + mov eax, [edi+block_size] + calc_index eax + cmp eax, [block_ind] + je .add_used + + list_del edi + + mov ecx, [block_ind] + lea edx, [mem_block_list+ecx*8] + cmp edx, [edx] + jnz @f + btr [mem_block_mask], ecx +@@: + bts [mem_block_mask], eax + lea edx, [mem_block_list+eax*8] ;edx= list head + list_add edi, edx +.add_used: + + call md.add_to_used + + spin_unlock_irqrestore heap_mutex + mov eax, [esi+block_base] + pop edi + pop esi + pop ebx + ret + +.m_eq_size: + list_del edi + lea edx, [mem_block_list+ebx*8] + cmp edx, [edx] + jnz @f + btr [mem_block_mask], ebx +@@: + mov esi, edi + jmp .add_used + +.error_unlock: + spin_unlock_irqrestore heap_mutex +.error: + xor eax, eax + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword + + spin_lock_irqsave heap_mutex + + mov eax, [base] + + call md.del_from_used + test esi, esi + jz .fail + + mov eax, [esi+block_size] + add [heap_free], eax + + mov edi, [esi+block_next] + cmp [edi+block_flags], FREE_BLOCK + jne .prev + + list_del edi + + mov edx, [edi+block_next] + mov [esi+block_next], edx + mov [edx+block_prev], esi + mov ecx, [edi+block_size] + add [esi+block_size], ecx + + calc_index ecx + + lea edx, [mem_block_list+ecx*8] + cmp edx, [edx] + jne @F + btr [mem_block_mask], ecx +@@: + mov eax, edi + call free_mem_block +.prev: + mov edi, [esi+block_prev] + cmp [edi+block_flags], FREE_BLOCK + jne .insert + + mov edx, [esi+block_next] + mov [edi+block_next], edx + mov [edx+block_prev], edi + + mov eax, esi + call free_mem_block + + mov ecx, [edi+block_size] + mov eax, [esi+block_size] + add eax, ecx + mov [edi+block_size], eax + + calc_index eax ;new index + calc_index ecx ;old index + cmp eax, ecx + je .m_eq + + push ecx + list_del edi + pop ecx + + lea edx, [mem_block_list+ecx*8] + cmp edx, [edx] + jne .add_block + btr [mem_block_mask], ecx + +.add_block: + bts [mem_block_mask], eax + lea edx, [mem_block_list+eax*8] + list_add edi, edx +.m_eq: + spin_unlock_irqrestore heap_mutex + xor eax, eax + not eax + ret +.insert: + mov [esi+block_flags], FREE_BLOCK + mov eax, [esi+block_size] + calc_index eax + mov edi, esi + jmp .add_block + +.fail: + spin_unlock_irqrestore heap_mutex + xor eax, eax + ret +endp + +align 4 +proc kernel_alloc stdcall, size:dword + locals + lin_addr dd ? + pages_count dd ? + endl + + push ebx + push edi + + mov eax, [size] + add eax, 4095 + and eax, not 4095; + mov [size], eax + and eax, eax + jz .err + mov ebx, eax + shr ebx, 12 + mov [pages_count], ebx + + stdcall alloc_kernel_space, eax + mov [lin_addr], eax + mov ebx, [pages_count] + test eax, eax + jz .err + + mov edx, eax + + shr ebx, 3 + jz .tail + + shl ebx, 3 + stdcall alloc_pages, ebx + test eax, eax + jz .err + + mov ecx, ebx + or eax, PG_SW + mov ebx, [lin_addr] + call commit_pages + + mov edx, ebx ; this dirty hack +.tail: + mov ebx, [pages_count] + and ebx, 7 + jz .end +@@: + call alloc_page + test eax, eax + jz .err + + stdcall map_page, edx, eax, dword PG_SW + add edx, 0x1000 + dec ebx + jnz @B +.end: + mov eax, [lin_addr] + pop edi + pop ebx + ret +.err: + xor eax, eax + pop edi + pop ebx + ret +endp + +align 4 +proc kernel_free stdcall, base:dword + + push ebx esi + + spin_lock_irqsave heap_mutex + + mov eax, [base] + call md.find_used + + cmp [esi+block_flags], USED_BLOCK + jne .fail + + spin_unlock_irqrestore heap_mutex + + mov eax, [esi+block_base] + mov ecx, [esi+block_size] + shr ecx, 12 + call release_pages ;eax, ecx + stdcall free_kernel_space, [base] + pop esi ebx + ret +.fail: + spin_unlock_irqrestore heap_mutex + xor eax, eax + pop esi ebx + ret +endp + +restore block_next +restore block_prev +restore block_list +restore block_base +restore block_size +restore block_flags + +;;;;;;;;;;;;;; USER ;;;;;;;;;;;;;;;;; + +HEAP_TOP equ 0x80000000 + +align 4 +proc init_heap + + mov ebx, [current_slot] + mov eax, [ebx+APPDATA.heap_top] + test eax, eax + jz @F + sub eax, [ebx+APPDATA.heap_base] + sub eax, 4096 + ret +@@: + mov esi, [ebx+APPDATA.mem_size] + add esi, 4095 + and esi, not 4095 + mov [ebx+APPDATA.mem_size], esi + mov eax, HEAP_TOP + mov [ebx+APPDATA.heap_base], esi + mov [ebx+APPDATA.heap_top], eax + + sub eax, esi + shr esi, 10 + mov ecx, eax + sub eax, 4096 + or ecx, FREE_BLOCK + mov [page_tabs+esi], ecx + ret +endp + +align 4 +proc user_alloc stdcall, alloc_size:dword + + push ebx + push esi + push edi + + mov ecx, [alloc_size] + add ecx, (4095+4096) + and ecx, not 4095 + + mov ebx, [current_slot] + mov esi, dword [ebx+APPDATA.heap_base] ; heap_base + mov edi, dword [ebx+APPDATA.heap_top] ; heap_top +l_0: + cmp esi, edi + jae m_exit + + mov ebx, esi + shr ebx, 12 + mov eax, [page_tabs+ebx*4] + test al, FREE_BLOCK + jz test_used + and eax, 0xFFFFF000 + cmp eax, ecx ;alloc_size + jb m_next + jz @f + + lea edx, [esi+ecx] + sub eax, ecx + or al, FREE_BLOCK + shr edx, 12 + mov [page_tabs+edx*4], eax +@@: + or ecx, USED_BLOCK + mov [page_tabs+ebx*4], ecx + shr ecx, 12 + inc ebx + dec ecx + jz .no +@@: + mov dword [page_tabs+ebx*4], 2 + inc ebx + dec ecx + jnz @B +.no: + + mov edx, [current_slot] + mov ebx, [alloc_size] + add ebx, 0xFFF + and ebx, not 0xFFF + add ebx, [edx+APPDATA.mem_size] + call update_mem_size + + lea eax, [esi+4096] + + pop edi + pop esi + pop ebx + ret +test_used: + test al, USED_BLOCK + jz m_exit + + and eax, 0xFFFFF000 +m_next: + add esi, eax + jmp l_0 +m_exit: + xor eax, eax + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc user_alloc_at stdcall, address:dword, alloc_size:dword + + push ebx + push esi + push edi + + mov ebx, [current_slot] + mov edx, [address] + and edx, not 0xFFF + mov [address], edx + sub edx, 0x1000 + jb .error + mov esi, [ebx+APPDATA.heap_base] + mov edi, [ebx+APPDATA.heap_top] + cmp edx, esi + jb .error +.scan: + cmp esi, edi + jae .error + mov ebx, esi + shr ebx, 12 + mov eax, [page_tabs+ebx*4] + mov ecx, eax + and ecx, 0xFFFFF000 + add ecx, esi + cmp edx, ecx + jb .found + mov esi, ecx + jmp .scan +.error: + xor eax, eax + pop edi + pop esi + pop ebx + ret +.found: + test al, FREE_BLOCK + jz .error + mov eax, ecx + sub eax, edx + sub eax, 0x1000 + cmp eax, [alloc_size] + jb .error + +; Here we have 1 big free block which includes requested area. +; In general, 3 other blocks must be created instead: +; free at [esi, edx); +; busy at [edx, edx+0x1000+ALIGN_UP(alloc_size,0x1000)); +; free at [edx+0x1000+ALIGN_UP(alloc_size,0x1000), ecx) +; First or third block (or both) may be absent. + mov eax, edx + sub eax, esi + jz .nofirst + or al, FREE_BLOCK + mov [page_tabs+ebx*4], eax +.nofirst: + mov eax, [alloc_size] + add eax, 0x1FFF + and eax, not 0xFFF + mov ebx, edx + add edx, eax + shr ebx, 12 + or al, USED_BLOCK + mov [page_tabs+ebx*4], eax + shr eax, 12 + dec eax + jz .second_nofill + inc ebx +.fill: + mov dword [page_tabs+ebx*4], 2 + inc ebx + dec eax + jnz .fill + +.second_nofill: + sub ecx, edx + jz .nothird + or cl, FREE_BLOCK + mov [page_tabs+ebx*4], ecx + +.nothird: + + mov edx, [current_slot] + mov ebx, [alloc_size] + add ebx, 0xFFF + and ebx, not 0xFFF + add ebx, [edx+APPDATA.mem_size] + call update_mem_size + + mov eax, [address] + + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc user_free stdcall, base:dword + + push esi + + mov esi, [base] + test esi, esi + jz .exit + + push ebx + + xor ebx, ebx + shr esi, 12 + mov eax, [page_tabs+(esi-1)*4] + test al, USED_BLOCK + jz .cantfree + test al, DONT_FREE_BLOCK + jnz .cantfree + + and eax, not 4095 + mov ecx, eax + or al, FREE_BLOCK + mov [page_tabs+(esi-1)*4], eax + sub ecx, 4096 + mov ebx, ecx + shr ecx, 12 + jz .released +.release: + xor eax, eax + xchg eax, [page_tabs+esi*4] + test al, 1 + jz @F + test eax, PG_SHARED + jnz @F + call free_page + mov eax, esi + shl eax, 12 + invlpg [eax] +@@: + inc esi + dec ecx + jnz .release + +.released: + push edi + + mov edx, [current_slot] + mov esi, dword [edx+APPDATA.heap_base] + mov edi, dword [edx+APPDATA.heap_top] + sub ebx, [edx+APPDATA.mem_size] + neg ebx + call update_mem_size + call user_normalize + pop edi + pop ebx + pop esi + ret +.exit: + xor eax, eax + inc eax + pop esi + ret +.cantfree: + xor eax, eax + pop ebx + pop esi + ret +endp + + +align 4 +proc user_unmap stdcall, base:dword, offset:dword, size:dword + + push ebx + + mov ebx, [base] ; must be valid pointer + test ebx, ebx + jz .error + + mov edx, [offset] ; check offset + add edx, ebx ; must be below 2Gb app limit + js .error + + shr ebx, 12 ; chek block attributes + lea ebx, [page_tabs+ebx*4] + mov eax, [ebx-4] ; block attributes + test al, USED_BLOCK + jz .error + test al, DONT_FREE_BLOCK + jnz .error + + shr edx, 12 + lea edx, [page_tabs+edx*4] ; unmap offset + + mov ecx, [size] + add ecx, 4095 + shr ecx, 12 ; unmap size in pages + + shr eax, 12 ; block size + 1 page + lea ebx, [ebx+eax*4-4] ; block end ptr + lea eax, [edx+ecx*4] ; unmap end ptr + + cmp eax, ebx ; check for overflow + ja .error + + mov ebx, [offset] + and ebx, not 4095 ; is it required ? + add ebx, [base] + +.unmap: + mov eax, [edx] ; get page addres + test al, 1 ; page mapped ? + jz @F + test eax, PG_SHARED ; page shared ? + jnz @F + mov [edx], dword 2 + ; mark page as reserved + invlpg [ebx] ; when we start using + call free_page ; empty c-o-w page instead this ? +@@: + add ebx, 4096 + add edx, 4 + dec ecx + jnz .unmap + + pop ebx + or al, 1 ; return non zero on success + ret +.error: + pop ebx + xor eax, eax ; something wrong + ret +endp + +align 4 +user_normalize: +; in: esi=heap_base, edi=heap_top +; out: eax=0 <=> OK +; destroys: ebx,edx,esi,edi + shr esi, 12 + shr edi, 12 +@@: + mov eax, [page_tabs+esi*4] + test al, USED_BLOCK + jz .test_free + shr eax, 12 + add esi, eax + jmp @B +.test_free: + test al, FREE_BLOCK + jz .err + mov edx, eax + shr edx, 12 + add edx, esi + cmp edx, edi + jae .exit + + mov ebx, [page_tabs+edx*4] + test bl, USED_BLOCK + jz .next_free + + shr ebx, 12 + add edx, ebx + mov esi, edx + jmp @B +.next_free: + test bl, FREE_BLOCK + jz .err + and dword [page_tabs+edx*4], 0 + add eax, ebx + and eax, not 4095 + or eax, FREE_BLOCK + mov [page_tabs+esi*4], eax + jmp @B +.exit: + xor eax, eax + inc eax + ret +.err: + xor eax, eax + ret + +user_realloc: +; in: eax = pointer, ebx = new size +; out: eax = new pointer or NULL + test eax, eax + jnz @f +; realloc(NULL,sz) - same as malloc(sz) + push ebx + call user_alloc + ret +@@: + push ecx edx + lea ecx, [eax - 0x1000] + shr ecx, 12 + mov edx, [page_tabs+ecx*4] + test dl, USED_BLOCK + jnz @f +; attempt to realloc invalid pointer +.ret0: + pop edx ecx + xor eax, eax + ret +@@: + test dl, DONT_FREE_BLOCK + jnz .ret0 + add ebx, 0x1FFF + shr edx, 12 + shr ebx, 12 +; edx = allocated size, ebx = new size + add edx, ecx + add ebx, ecx + cmp edx, ebx + jb .realloc_add +; release part of allocated memory +.loop: + cmp edx, ebx + jz .release_done + dec edx + xor eax, eax + xchg eax, [page_tabs+edx*4] + test al, 1 + jz .loop + call free_page + mov eax, edx + shl eax, 12 + invlpg [eax] + jmp .loop +.release_done: + sub ebx, ecx + cmp ebx, 1 + jnz .nofreeall + mov eax, [page_tabs+ecx*4] + and eax, not 0xFFF + mov edx, [current_slot] + mov ebx, [APPDATA.mem_size+edx] + sub ebx, eax + add ebx, 0x1000 + or al, FREE_BLOCK + mov [page_tabs+ecx*4], eax + push esi edi + mov esi, [APPDATA.heap_base+edx] + mov edi, [APPDATA.heap_top+edx] + call update_mem_size + call user_normalize + pop edi esi + jmp .ret0 ; all freed +.nofreeall: + sub edx, ecx + shl ebx, 12 + or ebx, USED_BLOCK + xchg [page_tabs+ecx*4], ebx + shr ebx, 12 + sub ebx, edx + push ebx ecx edx + mov edx, [current_slot] + shl ebx, 12 + sub ebx, [APPDATA.mem_size+edx] + neg ebx + call update_mem_size + pop edx ecx ebx + lea eax, [ecx+1] + shl eax, 12 + push eax + add ecx, edx + lea edx, [ecx+ebx] + shl ebx, 12 + jz .ret + push esi + mov esi, [current_slot] + mov esi, [APPDATA.heap_top+esi] + shr esi, 12 +@@: + cmp edx, esi + jae .merge_done + mov eax, [page_tabs+edx*4] + test al, USED_BLOCK + jnz .merge_done + and dword [page_tabs+edx*4], 0 + shr eax, 12 + add edx, eax + shl eax, 12 + add ebx, eax + jmp @b +.merge_done: + pop esi + or ebx, FREE_BLOCK + mov [page_tabs+ecx*4], ebx +.ret: + pop eax edx ecx + ret +.realloc_add: +; get some additional memory + mov eax, [current_slot] + mov eax, [APPDATA.heap_top+eax] + shr eax, 12 + cmp edx, eax + jae .cant_inplace + mov eax, [page_tabs+edx*4] + test al, FREE_BLOCK + jz .cant_inplace + shr eax, 12 + add eax, edx + sub eax, ebx + jb .cant_inplace + jz @f + shl eax, 12 + or al, FREE_BLOCK + mov [page_tabs+ebx*4], eax +@@: + mov eax, ebx + sub eax, ecx + shl eax, 12 + or al, USED_BLOCK + mov [page_tabs+ecx*4], eax + lea eax, [ecx+1] + shl eax, 12 + push eax + push edi + lea edi, [page_tabs+edx*4] + mov eax, 2 + sub ebx, edx + mov ecx, ebx + cld + rep stosd + pop edi + mov edx, [current_slot] + shl ebx, 12 + add ebx, [APPDATA.mem_size+edx] + call update_mem_size + pop eax edx ecx + ret +.cant_inplace: + push esi edi + mov eax, [current_slot] + mov esi, [APPDATA.heap_base+eax] + mov edi, [APPDATA.heap_top+eax] + shr esi, 12 + shr edi, 12 + sub ebx, ecx +.find_place: + cmp esi, edi + jae .place_not_found + mov eax, [page_tabs+esi*4] + test al, FREE_BLOCK + jz .next_place + shr eax, 12 + cmp eax, ebx + jae .place_found + add esi, eax + jmp .find_place +.next_place: + shr eax, 12 + add esi, eax + jmp .find_place +.place_not_found: + pop edi esi + jmp .ret0 +.place_found: + sub eax, ebx + jz @f + push esi + add esi, ebx + shl eax, 12 + or al, FREE_BLOCK + mov [page_tabs+esi*4], eax + pop esi +@@: + mov eax, ebx + shl eax, 12 + or al, USED_BLOCK + mov [page_tabs+esi*4], eax + inc esi + mov eax, esi + shl eax, 12 + push eax + mov eax, [page_tabs+ecx*4] + and eax, not 0xFFF + or al, FREE_BLOCK + sub edx, ecx + mov [page_tabs+ecx*4], eax + inc ecx + dec ebx + dec edx + jz .no +@@: + xor eax, eax + xchg eax, [page_tabs+ecx*4] + mov [page_tabs+esi*4], eax + mov eax, ecx + shl eax, 12 + invlpg [eax] + inc esi + inc ecx + dec ebx + dec edx + jnz @b +.no: + push ebx + mov edx, [current_slot] + shl ebx, 12 + add ebx, [APPDATA.mem_size+edx] + call update_mem_size + pop ebx +@@: + mov dword [page_tabs+esi*4], 2 + inc esi + dec ebx + jnz @b + pop eax edi esi edx ecx + ret + +if 0 +align 4 +proc alloc_dll + pushf + cli + bsf eax, [dll_map] + jnz .find + popf + xor eax, eax + ret +.find: + btr [dll_map], eax + popf + shl eax, 5 + add eax, dll_tab + ret +endp + +align 4 +proc alloc_service + pushf + cli + bsf eax, [srv_map] + jnz .find + popf + xor eax, eax + ret +.find: + btr [srv_map], eax + popf + shl eax, 0x02 + lea eax, [srv_tab+eax+eax*8] ;srv_tab+eax*36 + ret +endp + +end if + + +;;;;;;;;;;;;;; SHARED ;;;;;;;;;;;;;;;;; + + +; param +; eax= shm_map object + +align 4 +destroy_smap: + + pushfd + cli + + push esi + push edi + + mov edi, eax + mov esi, [eax+SMAP.parent] + test esi, esi + jz .done + + lock dec [esi+SMEM.refcount] + jnz .done + + mov ecx, [esi+SMEM.bk] + mov edx, [esi+SMEM.fd] + + mov [ecx+SMEM.fd], edx + mov [edx+SMEM.bk], ecx + + stdcall kernel_free, [esi+SMEM.base] + mov eax, esi + call free +.done: + mov eax, edi + call destroy_kernel_object + + pop edi + pop esi + popfd + + ret + +E_NOTFOUND equ 5 +E_ACCESS equ 10 +E_NOMEM equ 30 +E_PARAM equ 33 + +SHM_READ equ 0 +SHM_WRITE equ 1 + +SHM_ACCESS_MASK equ 3 + +SHM_OPEN equ (0 shl 2) +SHM_OPEN_ALWAYS equ (1 shl 2) +SHM_CREATE equ (2 shl 2) + +SHM_OPEN_MASK equ (3 shl 2) + +align 4 +proc shmem_open stdcall name:dword, size:dword, access:dword + locals + action dd ? + owner_access dd ? + mapped dd ? + endl + + push ebx + push esi + push edi + + mov [mapped], 0 + mov [owner_access], 0 + + pushfd ;mutex required + cli + + mov eax, [access] + and eax, SHM_OPEN_MASK + mov [action], eax + + mov ebx, [name] + test ebx, ebx + mov edx, E_PARAM + jz .fail + + mov esi, [shmem_list.fd] +align 4 +@@: + cmp esi, shmem_list + je .not_found + + lea edx, [esi+SMEM.name]; link , base, size + stdcall strncmp, edx, ebx, 32 + test eax, eax + je .found + + mov esi, [esi+SMEM.fd] + jmp @B + +.not_found: + mov eax, [action] + + cmp eax, SHM_OPEN + mov edx, E_NOTFOUND + je .fail + + cmp eax, SHM_CREATE + mov edx, E_PARAM + je .create_shm + + cmp eax, SHM_OPEN_ALWAYS + jne .fail + +.create_shm: + + mov ecx, [size] + test ecx, ecx + jz .fail + + add ecx, 4095 + and ecx, -4096 + mov [size], ecx + + mov eax, sizeof.SMEM + call malloc + test eax, eax + mov esi, eax + mov edx, E_NOMEM + jz .fail + + stdcall kernel_alloc, [size] + test eax, eax + mov [mapped], eax + mov edx, E_NOMEM + jz .cleanup + + mov ecx, [size] + mov edx, [access] + and edx, SHM_ACCESS_MASK + + mov [esi+SMEM.base], eax + mov [esi+SMEM.size], ecx + mov [esi+SMEM.access], edx + mov [esi+SMEM.refcount], 0 + mov [esi+SMEM.name+28], 0 + + lea eax, [esi+SMEM.name] + stdcall strncpy, eax, [name], 31 + + mov eax, [shmem_list.fd] + mov [esi+SMEM.bk], shmem_list + mov [esi+SMEM.fd], eax + + mov [eax+SMEM.bk], esi + mov [shmem_list.fd], esi + + mov [action], SHM_OPEN + mov [owner_access], SHM_WRITE + +.found: + mov eax, [action] + + cmp eax, SHM_CREATE + mov edx, E_ACCESS + je .exit + + cmp eax, SHM_OPEN + mov edx, E_PARAM + je .create_map + + cmp eax, SHM_OPEN_ALWAYS + jne .fail + +.create_map: + + mov eax, [access] + and eax, SHM_ACCESS_MASK + cmp eax, [esi+SMEM.access] + mov [access], eax + mov edx, E_ACCESS + ja .fail + + mov ebx, [CURRENT_TASK] + shl ebx, 5 + mov ebx, [CURRENT_TASK+ebx+4] + mov eax, sizeof.SMAP + + call create_kernel_object + test eax, eax + mov edi, eax + mov edx, E_NOMEM + jz .fail + + inc [esi+SMEM.refcount] + + mov [edi+SMAP.magic], 'SMAP' + mov [edi+SMAP.destroy], destroy_smap + mov [edi+SMAP.parent], esi + mov [edi+SMAP.base], 0 + + stdcall user_alloc, [esi+SMEM.size] + test eax, eax + mov [mapped], eax + mov edx, E_NOMEM + jz .cleanup2 + + mov [edi+SMAP.base], eax + + mov ecx, [esi+SMEM.size] + mov [size], ecx + + shr ecx, 12 + shr eax, 10 + + mov esi, [esi+SMEM.base] + shr esi, 10 + lea edi, [page_tabs+eax] + add esi, page_tabs + + mov edx, [access] + or edx, [owner_access] + shl edx, 1 + or edx, PG_USER+PG_SHARED +@@: + lodsd + and eax, 0xFFFFF000 + or eax, edx + stosd + loop @B + + xor edx, edx + + cmp [owner_access], 0 + jne .fail +.exit: + mov edx, [size] +.fail: + mov eax, [mapped] + + popfd + pop edi + pop esi + pop ebx + ret +.cleanup: + mov [size], edx + mov eax, esi + call free + jmp .exit + +.cleanup2: + mov [size], edx + mov eax, edi + call destroy_smap + jmp .exit +endp + +align 4 +proc shmem_close stdcall, name:dword + + mov eax, [name] + test eax, eax + jz .fail + + push esi + push edi + pushfd + cli + + mov esi, [current_slot] + add esi, APP_OBJ_OFFSET +.next: + mov eax, [esi+APPOBJ.fd] + test eax, eax + jz @F + + cmp eax, esi + mov esi, eax + je @F + + cmp [eax+SMAP.magic], 'SMAP' + jne .next + + mov edi, [eax+SMAP.parent] + test edi, edi + jz .next + + lea edi, [edi+SMEM.name] + stdcall strncmp, [name], edi, 32 + test eax, eax + jne .next + + stdcall user_free, [esi+SMAP.base] + + mov eax, esi + call [esi+APPOBJ.destroy] +@@: + popfd + pop edi + pop esi +.fail: + ret +endp diff --git a/kernel/branches/kolibri-process/core/irq.inc b/kernel/branches/kolibri-process/core/irq.inc new file mode 100644 index 000000000..e1b3df32f --- /dev/null +++ b/kernel/branches/kolibri-process/core/irq.inc @@ -0,0 +1,278 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IRQ_RESERVED equ 24 + +IRQ_POOL_SIZE equ 48 + +uglobal + +align 16 +irqh_tab rd sizeof.LHEAD * IRQ_RESERVED / 4 + +irqh_pool rd sizeof.IRQH * IRQ_POOL_SIZE /4 +next_irqh rd 1 + +irq_active_set rd 1 +irq_failed rd IRQ_RESERVED + +endg + +align 4 +init_irqs: + + mov ecx, IRQ_RESERVED + mov edi, irqh_tab +@@: + mov eax, edi + stosd + stosd + loop @B + + mov ecx, IRQ_POOL_SIZE-1 + mov eax, irqh_pool+sizeof.IRQH + mov [next_irqh], irqh_pool +@@: + mov [eax-sizeof.IRQH], eax + add eax, sizeof.IRQH + loop @B + + mov [eax-sizeof.IRQH], dword 0 + ret + + +align 4 +proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword + locals + .irqh dd ? + endl + + DEBUGF 1, "K : Attach Interrupt %d Handler %x\n", [irq], [handler] + + and [.irqh], 0 + + push ebx + + mov ebx, [irq] ;irq num + test ebx, ebx + jz .err + + cmp ebx, IRQ_RESERVED + jae .err + + mov edx, [handler] + test edx, edx + jz .err + + spin_lock_irqsave IrqsList + +;allocate handler + + mov ecx, [next_irqh] + test ecx, ecx + jz .fail + + mov eax, [ecx] + mov [next_irqh], eax + mov [.irqh], ecx + + mov [irq_failed+ebx*4], 0;clear counter + + 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: + spin_unlock_irqrestore IrqsList +.err: + pop ebx + mov eax, [.irqh] + ret + +endp + +if 0 +align 4 +proc get_int_handler stdcall, irq:dword + + mov eax, [irq] + cmp eax, 15 + ja .fail + mov eax, [irq_tab + 4 * eax] + ret +.fail: + xor eax, eax + ret +endp +end if + + +align 4 +proc detach_int_handler + + ret +endp + + +macro irq_serv_h [num] { + forward +align 4 + .irq_#num : + push num + jmp .main +} + +align 16 +irq_serv: + +; .irq_1: +; push 1 +; jmp .main +; etc... + +irq_serv_h 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15 +irq_serv_h 16, 17, 18, 19, 20, 21, 22, 23 + +purge irq_serv_h + +align 16 +.main: + save_ring3_context + + mov ebp, [esp + 32] + mov bx, app_data;os_data + mov ds, bx + mov es, bx + + cmp [v86_irqhooks+ebp*8], 0 + jnz v86_irq + + bts [irq_active_set], ebp + + lea esi, [irqh_tab+ebp*8] ; esi= list head + mov ebx, esi +.next: + mov ebx, [ebx+IRQH.list.next]; ebx= irqh pointer + cmp ebx, esi + je .done + + push ebx ; FIX THIS + push edi + push esi + + push [ebx+IRQH.data] + call [ebx+IRQH.handler] + pop ecx + + pop esi + pop edi + pop ebx + + test eax, eax + jz .next + + inc [ebx+IRQH.num_ints] + btr [irq_active_set], ebp + jmp .next + +.done: + 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. + push ebp + xor ebp, ebp +.try_other_irqs: + cmp ebp, [esp] + jz .try_next_irq + cmp ebp, 1 + jz .try_next_irq + cmp ebp, 6 + jz .try_next_irq + cmp ebp, 12 + jz .try_next_irq + cmp ebp, 14 + jz .try_next_irq + cmp ebp, 15 + 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 ecx, ebp + call irq_eoi + +; IRQ handler could make some kernel thread ready; reschedule + mov bl, SCHEDULE_HIGHER_PRIORITY + call find_next_task + jz .return ; if there is only one running process + call do_change_task + .return: + restore_ring3_context + add esp, 4 + iret + +align 4 +irqD: + push eax + push ecx + xor eax, eax + out 0xf0, al + mov cl, 13 + call irq_eoi + pop ecx + pop eax + iret + diff --git a/kernel/branches/kolibri-process/core/malloc.inc b/kernel/branches/kolibri-process/core/malloc.inc new file mode 100644 index 000000000..ca56e4a06 --- /dev/null +++ b/kernel/branches/kolibri-process/core/malloc.inc @@ -0,0 +1,1035 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3681 $ + + +; Small heap based on malloc/free/realloc written by Doug Lea +; Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) +; Source ftp://gee.cs.oswego.edu/pub/misc/malloc.c +; License http://creativecommons.org/licenses/publicdomain. + + +; eax= size + +; temp +; esi= nb +; ebx= idx +; +align 4 +malloc: + push ebx esi + +; nb = ((size+7)&~7)+8; + + mov esi, eax ;size + add esi, 7 + and esi, -8 + add esi, 8 + + mov ecx, mst.mutex + call mutex_lock + + cmp esi, 256 + jae .large + + mov ecx, esi + shr ecx, 3 + or eax, -1 + shl eax, cl + and eax, [mst.smallmap] + jz .small + + push ebp + push edi + + bsf eax, eax + mov ebx, eax + +; psize= idx<<3; +; B = &ms.smallbins[idx]; +; p = B->fd; +; F = p->fd; +; rsize= psize-nb; + + lea ebp, [eax*8] ;ebp= psize + shl eax, 4 + lea edi, [mst.smallbins+eax] ;edi= B + mov edx, [edi+8] ;edx= p + mov eax, [edx+8] ;eax= F + mov ecx, ebp + sub ecx, esi ;ecx= rsize + +; if (B == F) + cmp edi, eax + jne @F + + btr [mst.smallmap], ebx +@@: + +; B->fd = F; +; F->bk = B; +; if(rsize<16) + + cmp ecx, 16 + mov [edi+8], eax + mov [eax+12], edi + jae .split + +; p->head = psize|PINUSE_BIT|CINUSE_BIT; +; (p + psize)->head |= PINUSE_BIT; + + lea eax, [edx+8] + or dword [edx+ebp+4], 1 + + or ebp, 3 + mov [edx+4], ebp + + pop edi + pop ebp +.done: + mov esi, eax + mov ecx, mst.mutex + call mutex_unlock + mov eax, esi + pop esi ebx + ret + +.split: + lea ebx, [edx+8] ;ebx=mem + +; r = chunk_plus_offset(p, nb); +; p->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; + + lea eax, [edx+esi] ;eax= r + or esi, 3 + mov [edx+4], esi + + mov edx, ecx + or edx, 1 + mov [eax+4], edx + +; (r + rsize)->prev_foot = rsize; + + mov [eax+ecx], ecx + +; I = rsize>>3; + + shr ecx, 3 + +; ms.smallmap |= 1<< I; + bts [mst.smallmap], ecx + +; B = &ms.smallbins[I]; + + shl ecx, 4 + pop edi + pop ebp + add ecx, mst.smallbins ;ecx= B + + mov edx, [ecx+8] ; F = B->fd; + mov [ecx+8], eax ; B->fd = r; + mov [edx+12], eax ; F->bk = r; + mov [eax+8], edx ; r->fd = F; + mov [eax+12], ecx ; r->bk = B; + + mov eax, ebx + jmp .done + +.small: + +; if (ms.treemap != 0 && (mem = malloc_small(nb)) != 0) +;;;;;;;;;;; start a change + mov eax, [mst.treemap] + test eax, eax +;;;;;;;;;;; end the change +; cmp [mst.treemap], 0 + jz .from_top + mov eax, esi + call malloc_small + test eax, eax + jz .from_top + jmp .done + +.large: + +; if (ms.treemap != 0 && (mem = malloc_large(nb)) != 0) + + cmp [mst.treemap], 0 + je .from_top + + call malloc_large ;esi= nb + test eax, eax + jne .done +.from_top: + +; if (nb < ms.topsize) + + mov eax, [mst.topsize] + cmp esi, eax + jae .fail + +; rsize = ms.topsize -= nb; +; p = ms.top; + + mov ecx, [mst.top] + sub eax, esi + mov [mst.topsize], eax + +; r = ms.top = chunk_plus_offset(p, nb); +; r->head = rsize | PINUSE_BIT; +; p->head = nb |PINUSE_BIT|CINUSE_BIT; + + lea edx, [ecx+esi] + or eax, 1 + mov [mst.top], edx + or esi, 3 + mov [edx+4], eax + mov [ecx+4], esi + lea eax, [ecx+8] + jmp .done + +.fail: + xor eax, eax + jmp .done + +; param +; eax= mem +align 4 +free: + test eax, eax + jz .exit + + push ebx edi + mov edi, eax + add edi, -8 + +; if(p->head & CINUSE_BIT) + + test byte [edi+4], 2 + je .fail + + mov ecx, mst.mutex + call mutex_lock + +; psize = p->head & (~3); + + mov eax, [edi+4] + push esi + mov esi, eax + and esi, -4 + +; next = chunk_plus_offset(p, psize); +; if(!(p->head & PINUSE_BIT)) + + test al, 1 + lea ebx, [esi+edi] + jne .next + +; prevsize = p->prev_foot; +; prev=p - prevsize; +; psize += prevsize; +; p = prev; + + mov ecx, [edi] ;ecx= prevsize + add esi, ecx ;esi= psize + sub edi, ecx ;edi= p + +; if (prevsize < 256) + + cmp ecx, 256 + jae .unlink_large + + mov eax, [edi+8] ;F = p->fd; + mov edx, [edi+12] ;B = p->bk; + +; if (F == B) +; ms.smallmap &= ~(1<< I); + shr ecx, 3 + cmp eax, edx + jne @F + btr [mst.smallmap], ecx +@@: + mov [eax+12], edx ;F->bk = B; + mov [edx+8], eax ;B->fd = F + jmp .next +.unlink_large: + mov edx, edi + call unlink_large_chunk +.next: + +; if(next->head & PINUSE_BIT) + + mov eax, [ebx+4] + test al, 1 + jz .fail2 + +; if (! (next->head & CINUSE_BIT)) + + test al, 2 + jnz .fix_next + +; if (next == ms.top) + + cmp ebx, [mst.top] + jne @F + +; tsize = ms.topsize += psize; + + mov eax, [mst.topsize] + add eax, esi + mov [mst.topsize], eax + +; ms.top = p; +; p->head = tsize | PINUSE_BIT; + + or eax, 1 + mov [mst.top], edi + mov [edi+4], eax +.fail2: + mov esi, eax + mov ecx, mst.mutex + call mutex_unlock + mov eax, esi + pop esi +.fail: + pop edi ebx +.exit: + ret + +@@: + +; nsize = next->head & ~INUSE_BITS; + + and eax, -4 + add esi, eax ;psize += nsize; + +; if (nsize < 256) + + cmp eax, 256 + jae .unl_large + + mov edx, [ebx+8] ;F = next->fd + mov ebx, [ebx+12] ;B = next->bk + +; if (F == B) + + cmp edx, ebx + jne @F + mov ecx, eax + shr ecx, 3 + btr [mst.smallmap], ecx +@@: + mov [edx+12], ebx ;F->bk = B + +; p->head = psize|PINUSE_BIT; + + mov ecx, esi + mov [ebx+8], edx + or ecx, 1 + mov [edi+4], ecx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi + +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 +.unl_large: + +; unlink_large_chunk((tchunkptr)next); + + mov edx, ebx + call unlink_large_chunk +; p->head = psize|PINUSE_BIT; + + mov ecx, esi + or ecx, 1 + mov [edi+4], ecx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi + +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 +.fix_next: + +; (p+psize)->prev_foot = psize; +; next->head &= ~PINUSE_BIT; +; p->head = psize|PINUSE_BIT; + + and eax, -2 + mov edx, esi + mov [ebx+4], eax + or edx, 1 + mov [edi+4], edx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 + +; param +; ecx = chunk +; eax = size + +insert_chunk: + + cmp eax, 256 + push esi + mov esi, ecx + jae .large + +; I = S>>3; +; ms.smallmap |= 1<< I; + + shr eax, 3 + bts [mst.smallmap], eax + +; B = &ms.smallbins[I]; + + shl eax, 4 + add eax, mst.smallbins + mov edx, [eax+8] ;F = B->fd + mov [eax+8], esi ;B->fd = P + mov [edx+12], esi ;F->bk = P + mov [esi+8], edx ;P->fd = F + mov [esi+12], eax ;P->bk = B + pop esi + ret +.large: + mov ebx, eax + call insert_large_chunk + pop esi + ret + + +; param +; esi= chunk +; ebx= size + +insert_large_chunk: + +; I = compute_tree_index(S); + + mov edx, ebx + shr edx, 8 + bsr eax, edx + lea ecx, [eax+7] + mov edx, ebx + shr edx, cl + and edx, 1 + lea ecx, [edx+eax*2] + +; X->index = I; + mov dword [esi+28], ecx + +; X->child[0] = X->child[1] = 0; + and dword [esi+20], 0 + and dword [esi+16], 0 + +; H = &ms.treebins[I]; + + mov eax, ecx + lea edx, [mst.treebins+eax*4] + +; if (!(ms.treemap & 1<child[(K >> 31) & 1]); + mov ecx, eax + shr ecx, 31 + lea ecx, [edx+ecx*4+16] + +; K <<= 1; +; if (*C != 0) + mov edi, [ecx] + add eax, eax + test edi, edi + jz .insert_child + +; T = *C; + mov edx, edi +.loop: + +; for (;;) +; if ((T->head & ~INUSE_BITS) != S) + + mov ecx, [edx+4] + and ecx, not 3 + cmp ecx, ebx + jne .not_eq_size + +; F = T->fd; + mov eax, [edx+8] + +; T->fd = F->bk = X; + mov [eax+12], esi + mov [edx+8], esi + +; X->fd = F; +; X->bk = T; +; X->parent = 0; + + and dword [esi+24], 0 + mov [esi+8], eax + mov [esi+12], edx + ret +.insert_child: + +; *C = X; + mov [ecx], esi +.done: + +; X->parent = T; + mov [esi+24], edx + +; X->fd = X->bk = X; + mov [esi+12], esi + mov [esi+8], esi + ret + + +; param +; edx= chunk + +unlink_large_chunk: + + mov eax, [edx+12] + cmp eax, edx + push edi + mov edi, [edx+24] + je @F + + mov ecx, [edx+8] ;F = X->fd + mov [ecx+12], eax ;F->bk = R; + mov [eax+8], ecx ;R->fd = F + jmp .parent +@@: + mov eax, [edx+20] + test eax, eax + push esi + lea esi, [edx+20] + jne .loop + + mov eax, [edx+16] + test eax, eax + lea esi, [edx+16] + je .l2 +.loop: + cmp dword [eax+20], 0 + lea ecx, [eax+20] + jne @F + + cmp dword [eax+16], 0 + lea ecx, [eax+16] + je .l1 +@@: + mov eax, [ecx] + mov esi, ecx + jmp .loop +.l1: + mov dword [esi], 0 +.l2: + pop esi +.parent: + test edi, edi + je .done + + mov ecx, [edx+28] + cmp edx, [mst.treebins+ecx*4] + lea ecx, [mst.treebins+ecx*4] + jne .l3 + + test eax, eax + mov [ecx], eax + jne .l5 + + mov ecx, [edx+28] + btr [mst.treemap], ecx + pop edi + ret + +.l3: + cmp [edi+16], edx + jne @F + + mov [edi+16], eax + jmp .l4 + +@@: + mov [edi+20], eax + +.l4: + test eax, eax + je .done + +.l5: + mov [eax+24], edi + mov ecx, [edx+16] + test ecx, ecx + je .l6 + + mov [eax+16], ecx + mov [ecx+24], eax + +.l6: + mov edx, [edx+20] + test edx, edx + je .done + + mov [eax+20], edx + mov [edx+24], eax + +.done: + pop edi + ret + +; param +; esi= nb + +malloc_small: + push ebp + mov ebp, esi + + push edi + + bsf eax, [mst.treemap] + mov ecx, [mst.treebins+eax*4] + +; rsize = (t->head & ~INUSE_BITS) - nb; + + mov edi, [ecx+4] + and edi, -4 + sub edi, esi + +.loop: + mov ebx, ecx + +.loop_1: + +; while ((t = leftmost_child(t)) != 0) + + mov eax, [ecx+16] + test eax, eax + jz @F + mov ecx, eax + jmp .l1 + +@@: + mov ecx, [ecx+20] + +.l1: + test ecx, ecx + jz .unlink + +; trem = (t->head & ~INUSE_BITS) - nb; + + mov eax, [ecx+4] + and eax, -4 + sub eax, ebp + +; if (trem < rsize) + + cmp eax, edi + jae .loop_1 + +; rsize = trem; + + mov edi, eax + jmp .loop +.unlink: + + +; r = chunk_plus_offset((mchunkptr)v, nb); +; unlink_large_chunk(v); + + mov edx, ebx + lea esi, [ebx+ebp] + call unlink_large_chunk + +; if (rsize < 16) + + cmp edi, 16 + jae .split + +; v->head = (rsize + nb)|PINUSE_BIT|CINUSE_BIT; + + lea ecx, [edi+ebp] + +; (v+rsize + nb)->head |= PINUSE_BIT; + + add edi, ebx + lea eax, [edi+ebp+4] + pop edi + or ecx, 3 + mov [ebx+4], ecx + or dword [eax], 1 + pop ebp + + lea eax, [ebx+8] + ret + +.split: + +; v->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; +; (r+rsize)->prev_foot = rsize; + + or ebp, 3 + mov edx, edi + or edx, 1 + + cmp edi, 256 + mov [ebx+4], ebp + mov [esi+4], edx + mov [esi+edi], edi + jae .large + + shr edi, 3 + bts [mst.smallmap], edi + + mov eax, edi + shl eax, 4 + add eax, mst.smallbins + + mov edx, [eax+8] + mov [eax+8], esi + mov [edx+12], esi + pop edi + mov [esi+12], eax + mov [esi+8], edx + pop ebp + lea eax, [ebx+8] + ret + +.large: + lea eax, [ebx+8] + push eax + mov ebx, edi + call insert_large_chunk + pop eax + pop edi + pop ebp + ret + + +; param +; esi= nb + +malloc_large: +.idx equ esp+4 +.rst equ esp + + push ebp + push esi + push edi + sub esp, 8 +; v = 0; +; rsize = -nb; + + mov edi, esi + mov ebx, esi + xor ebp, ebp + neg edi + +; idx = compute_tree_index(nb); + + mov edx, esi + shr edx, 8 + bsr eax, edx + lea ecx, [eax+7] + shr esi, cl + and esi, 1 + lea ecx, [esi+eax*2] + mov [.idx], ecx + +; if ((t = ms.treebins[idx]) != 0) + + mov eax, [mst.treebins+ecx*4] + test eax, eax + jz .l3 + +; sizebits = nb << leftshift_for_tree_index(idx); + + cmp ecx, 31 + jne @F + xor ecx, ecx + jmp .l1 + +@@: + mov edx, ecx + shr edx, 1 + mov ecx, 37 + sub ecx, edx + +.l1: + mov edx, ebx + shl edx, cl + +; rst = 0; + mov [.rst], ebp + +.loop: + +; trem = (t->head & ~INUSE_BITS) - nb; + + mov ecx, [eax+4] + and ecx, -4 + sub ecx, ebx + +; if (trem < rsize) + + cmp ecx, edi + jae @F +; v = t; +; if ((rsize = trem) == 0) + + test ecx, ecx + mov ebp, eax + mov edi, ecx + je .l2 + +@@: + +; rt = t->child[1]; + + mov ecx, [eax+20] + +; t = t->child[(sizebits >> 31) & 1]; + + mov esi, edx + shr esi, 31 + +; if (rt != 0 && rt != t) + + test ecx, ecx + mov eax, [eax+esi*4+16] + jz @F + cmp ecx, eax + jz @F + +; rst = rt; + mov [.rst], ecx + +@@: +; if (t == 0) + + test eax, eax + jz @F + +; sizebits <<= 1; + + add edx, edx + jmp .loop + +@@: +; t = rst; + mov eax, [.rst] + +.l2: +; if (t == 0 && v == 0) + + test eax, eax + jne .l4 + test ebp, ebp + jne .l7 + mov ecx, [.idx] + +.l3: + +; leftbits = (-1<head & ~INUSE_BITS) - nb; + + mov ecx, [eax+4] + and ecx, -4 + sub ecx, ebx + +; if (trem < rsize) + + cmp ecx, edi + jae @F +; rsize = trem; + + mov edi, ecx +; v = t; + mov ebp, eax + +@@: + +; t = leftmost_child(t); + + mov ecx, [eax+16] + test ecx, ecx + je @F + mov eax, ecx + jmp .l6 + +@@: + mov eax, [eax+20] + +.l6: + +; while (t != 0) + + test eax, eax + jne .l4 + +.l5: + +; if (v != 0) + + test ebp, ebp + jz .done + +.l7: + +; r = chunk_plus_offset((mchunkptr)v, nb); +; unlink_large_chunk(v); + + mov edx, ebp + lea esi, [ebx+ebp] + call unlink_large_chunk + +; if (rsize < 16) + + cmp edi, 16 + jae .large + +; v->head = (rsize + nb)|PINUSE_BIT|CINUSE_BIT; + + lea ecx, [edi+ebx] + +; (v+rsize + nb)->head |= PINUSE_BIT; + + add edi, ebp + lea eax, [edi+ebx+4] + or ecx, 3 + mov [ebp+4], ecx + or dword [eax], 1 + lea eax, [ebp+8] + add esp, 8 + pop edi + pop esi + pop ebp + ret + +.large: + +; v->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; + + mov edx, edi + or ebx, 3 + mov [ebp+4], ebx + or edx, 1 + mov [esi+4], edx + +; (r+rsize)->prev_foot = rsize; +; insert_large_chunk((tchunkptr)r, rsize); + + mov [esi+edi], edi + mov eax, edi + mov ecx, esi + call insert_chunk + + lea eax, [ebp+8] + add esp, 8 + pop edi + pop esi + pop ebp + ret + +.done: + add esp, 8 + pop edi + pop esi + pop ebp + xor eax, eax + ret + +init_malloc: + + stdcall kernel_alloc, 0x40000 + + mov [mst.top], eax + mov [mst.topsize], 128*1024 + mov dword [eax+4], (128*1024) or 1 + mov eax, mst.smallbins + +@@: + mov [eax+8], eax + mov [eax+12], eax + add eax, 16 + cmp eax, mst.smallbins+512 + jb @B + + mov ecx, mst.mutex + call mutex_init + + ret + diff --git a/kernel/branches/kolibri-process/core/memory.inc b/kernel/branches/kolibri-process/core/memory.inc new file mode 100644 index 000000000..1489ec0e7 --- /dev/null +++ b/kernel/branches/kolibri-process/core/memory.inc @@ -0,0 +1,1546 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4424 $ + + +align 4 +proc alloc_page + + pushfd + cli + push ebx +;//- + cmp [pg_data.pages_free], 1 + jle .out_of_memory +;//- + + mov ebx, [page_start] + mov ecx, [page_end] +.l1: + bsf eax, [ebx]; + jnz .found + add ebx, 4 + cmp ebx, ecx + jb .l1 + pop ebx + popfd + xor eax, eax + ret +.found: +;//- + dec [pg_data.pages_free] + jz .out_of_memory +;//- + btr [ebx], eax + mov [page_start], ebx + sub ebx, sys_pgmap + lea eax, [eax+ebx*8] + shl eax, 12 +;//- dec [pg_data.pages_free] + pop ebx + popfd + ret +;//- +.out_of_memory: + mov [pg_data.pages_free], 1 + xor eax, eax + pop ebx + popfd + ret +;//- +endp + +align 4 +proc alloc_pages stdcall, count:dword + pushfd + push ebx + push edi + cli + mov eax, [count] + add eax, 7 + shr eax, 3 + mov [count], eax +;//- + mov ebx, [pg_data.pages_free] + sub ebx, 9 + js .out_of_memory + shr ebx, 3 + cmp eax, ebx + jg .out_of_memory +;//- + mov ecx, [page_start] + mov ebx, [page_end] +.find: + mov edx, [count] + mov edi, ecx +.match: + cmp byte [ecx], 0xFF + jne .next + dec edx + jz .ok + inc ecx + cmp ecx, ebx + jb .match +.out_of_memory: +.fail: + xor eax, eax + pop edi + pop ebx + popfd + ret +.next: + inc ecx + cmp ecx, ebx + jb .find + pop edi + pop ebx + popfd + xor eax, eax + ret +.ok: + sub ecx, edi + inc ecx + push esi + mov esi, edi + xor eax, eax + rep stosb + sub esi, sys_pgmap + shl esi, 3+12 + mov eax, esi + mov ebx, [count] + shl ebx, 3 + sub [pg_data.pages_free], ebx + pop esi + pop edi + pop ebx + popfd + ret +endp + +align 4 +;proc map_page stdcall,lin_addr:dword,phis_addr:dword,flags:dword +map_page: + push ebx + mov eax, [esp+12] ; phis_addr + and eax, not 0xFFF + or eax, [esp+16] ; flags + mov ebx, [esp+8] ; lin_addr + shr ebx, 12 + mov [page_tabs+ebx*4], eax + mov eax, [esp+8] ; lin_addr + pop ebx + invlpg [eax] + ret 12 + +align 4 +map_space: ;not implemented + + + ret + + +align 4 +proc free_page +;arg: eax page address + pushfd + cli + shr eax, 12 ;page index + bts dword [sys_pgmap], eax ;that's all! + cmc + adc [pg_data.pages_free], 0 + shr eax, 3 + and eax, not 3 ;dword offset from page_map + add eax, sys_pgmap + cmp [page_start], eax + ja @f + popfd + ret +@@: + mov [page_start], eax + popfd + ret +endp + +align 4 +proc map_io_mem stdcall, base:dword, size:dword, flags:dword + + push ebx + push edi + mov eax, [size] + add eax, [base] + add eax, 4095 + and eax, -4096 + mov ecx, [base] + and ecx, -4096 + sub eax, ecx + mov [size], eax + + stdcall alloc_kernel_space, eax + test eax, eax + jz .fail + push eax + + mov edi, 0x1000 + mov ebx, eax + mov ecx, [size] + mov edx, [base] + shr eax, 12 + shr ecx, 12 + and edx, -4096 + or edx, [flags] +@@: + mov [page_tabs+eax*4], edx + invlpg [ebx] + inc eax + add ebx, edi + add edx, edi + loop @B + + pop eax + mov edx, [base] + and edx, 4095 + add eax, edx +.fail: + pop edi + pop ebx + ret +endp + +; param +; eax= page base + page flags +; ebx= linear address +; ecx= count + +align 4 +commit_pages: + test ecx, ecx + jz .fail + + push edi + push eax + push ecx + mov ecx, pg_data.mutex + call mutex_lock + pop ecx + pop eax + + mov edi, ebx + shr edi, 12 + lea edi, [page_tabs+edi*4] +@@: + stosd + invlpg [ebx] + add eax, 0x1000 + add ebx, 0x1000 + loop @B + + pop edi + + mov ecx, pg_data.mutex + call mutex_unlock +.fail: + ret + + +; param +; eax= base +; ecx= count + +align 4 +release_pages: + + push ebp + push esi + push edi + push ebx + + mov esi, eax + mov edi, eax + + shr esi, 12 + lea esi, [page_tabs+esi*4] + + push ecx + mov ecx, pg_data.mutex + call mutex_lock + pop ecx + + mov ebp, [pg_data.pages_free] + mov ebx, [page_start] + mov edx, sys_pgmap +@@: + xor eax, eax + xchg eax, [esi] + invlpg [edi] + + test eax, 1 + jz .next + + shr eax, 12 + bts [edx], eax + cmc + adc ebp, 0 + shr eax, 3 + and eax, -4 + add eax, edx + cmp eax, ebx + jae .next + + mov ebx, eax +.next: + add edi, 0x1000 + add esi, 4 + loop @B + + mov [pg_data.pages_free], ebp + mov ecx, pg_data.mutex + call mutex_unlock + + pop ebx + pop edi + pop esi + pop ebp + ret + +; param +; eax= base +; ecx= count + +align 4 +unmap_pages: + + push edi + + mov edi, eax + mov edx, eax + + shr edi, 10 + add edi, page_tabs + + xor eax, eax +@@: + stosd + invlpg [edx] + add edx, 0x1000 + loop @b + + pop edi + ret + + +align 4 +proc map_page_table stdcall, lin_addr:dword, phis_addr:dword + push ebx + mov ebx, [lin_addr] + shr ebx, 22 + mov eax, [phis_addr] + and eax, not 0xFFF + or eax, PG_UW ;+PG_NOCACHE + mov dword [master_tab+ebx*4], eax + mov eax, [lin_addr] + shr eax, 10 + add eax, page_tabs + invlpg [eax] + pop ebx + ret +endp + +align 4 +proc init_LFB + locals + pg_count dd ? + endl + + cmp dword [LFBAddress], -1 + jne @f + mov [BOOT_VARS+BOOT_MTRR], byte 2 +; max VGA=640*480*4=1228800 bytes +; + 32*640*4=81920 bytes for mouse pointer + stdcall alloc_pages, ((1228800+81920)/4096) + + push eax + call alloc_page + stdcall map_page_table, LFB_BASE, eax + pop eax + or eax, PG_UW + mov ebx, LFB_BASE +; max VGA=640*480*4=1228800 bytes +; + 32*640*4=81920 bytes for mouse pointer + mov ecx, (1228800+81920)/4096 + call commit_pages + mov [LFBAddress], dword LFB_BASE + ret +@@: + test [SCR_MODE], word 0100000000000000b + jnz @f + mov [BOOT_VARS+BOOT_MTRR], byte 2 + ret +@@: + call init_mtrr + + mov edx, LFB_BASE + mov esi, [LFBAddress] + mov edi, 0x00C00000 + mov dword [exp_lfb+4], edx + + shr edi, 12 + mov [pg_count], edi + shr edi, 10 + + bt [cpu_caps], CAPS_PSE + jnc .map_page_tables + or esi, PG_LARGE+PG_UW + mov edx, sys_proc+PROC.pdt_0+(LFB_BASE shr 20) +@@: + mov [edx], esi + add edx, 4 + add esi, 0x00400000 + dec edi + jnz @B + + bt [cpu_caps], CAPS_PGE + jnc @F + or dword [sys_proc+PROC.pdt_0+(LFB_BASE shr 20)], PG_GLOBAL +@@: + mov dword [LFBAddress], LFB_BASE + mov eax, cr3 ;flush TLB + mov cr3, eax + ret + +.map_page_tables: + +@@: + call alloc_page + stdcall map_page_table, edx, eax + add edx, 0x00400000 + dec edi + jnz @B + + mov eax, [LFBAddress] + mov edi, page_tabs + (LFB_BASE shr 10) + or eax, PG_UW + mov ecx, [pg_count] + cld +@@: + stosd + add eax, 0x1000 + dec ecx + jnz @B + + mov dword [LFBAddress], LFB_BASE + mov eax, cr3 ;flush TLB + mov cr3, eax + + ret +endp + +align 4 +proc new_mem_resize stdcall, new_size:dword + + push ebx + push esi + push edi + + mov edx, [current_slot] + cmp [edx+APPDATA.heap_base], 0 + jne .exit + + mov edi, [new_size] + add edi, 4095 + and edi, not 4095 + mov [new_size], edi + + mov esi, [edx+APPDATA.mem_size] + add esi, 4095 + and esi, not 4095 + + cmp edi, esi + ja .expand + je .exit + + mov ebx, edi + shr edi, 12 + shr esi, 12 + + mov ecx, pg_data.mutex + call mutex_lock +@@: + mov eax, [app_page_tabs+edi*4] + test eax, 1 + jz .next + + mov dword [app_page_tabs+edi*4], 0 + invlpg [ebx] + call free_page + +.next: + inc edi + add ebx, 0x1000 + cmp edi, esi + jb @B + + mov ecx, pg_data.mutex + call mutex_unlock + +.update_size: + mov edx, [current_slot] + mov ebx, [new_size] + call update_mem_size +.exit: + pop edi + pop esi + pop ebx + xor eax, eax + ret + +.expand: + + mov ecx, pg_data.mutex + call mutex_lock + + xchg esi, edi + + push esi ;new size + push edi ;old size + + add edi, 0x3FFFFF + and edi, not(0x3FFFFF) + add esi, 0x3FFFFF + and esi, not(0x3FFFFF) + + cmp edi, esi + jae .grow + @@: + call alloc_page + test eax, eax + jz .exit_fail + + stdcall map_page_table, edi, eax + + push edi + shr edi, 10 + add edi, page_tabs + mov ecx, 1024 + xor eax, eax + cld + rep stosd + pop edi + + add edi, 0x00400000 + cmp edi, esi + jb @B +.grow: + pop edi ;old size + pop ecx ;new size + + shr edi, 10 + shr ecx, 10 + sub ecx, edi + shr ecx, 2 ;pages count + mov eax, 2 + + add edi, app_page_tabs + rep stosd + + mov ecx, pg_data.mutex + call mutex_unlock + + jmp .update_size + +.exit_fail: + mov ecx, pg_data.mutex + call mutex_unlock + + add esp, 8 + pop edi + pop esi + pop ebx + xor eax, eax + inc eax + ret +endp + + +align 4 +update_mem_size: +; in: edx = slot base +; ebx = new memory size +; destroys eax,ecx,edx + + mov [APPDATA.mem_size+edx], ebx +;search threads and update +;application memory size infomation + mov ecx, [APPDATA.process+edx] + mov eax, 2 + +.search_threads: +;eax = current slot +;ebx = new memory size +;ecx = page directory + cmp eax, [TASK_COUNT] + jg .search_threads_end + mov edx, eax + shl edx, 5 + cmp word [CURRENT_TASK+edx+TASKDATA.state], 9 ;if slot empty? + jz .search_threads_next + shl edx, 3 + cmp [SLOT_BASE+edx+APPDATA.process], ecx ;if it is our thread? + jnz .search_threads_next + mov [SLOT_BASE+edx+APPDATA.mem_size], ebx ;update memory size +.search_threads_next: + inc eax + jmp .search_threads +.search_threads_end: + ret + +; param +; eax= linear address +; +; retval +; eax= phisical page address + +align 4 +get_pg_addr: + sub eax, OS_BASE + cmp eax, 0x400000 + jb @f + shr eax, 12 + mov eax, [page_tabs+(eax+(OS_BASE shr 12))*4] +@@: + and eax, 0xFFFFF000 + ret + + +align 4 +; Now it is called from core/sys32::exc_c (see stack frame there) +proc page_fault_handler + + .err_addr equ ebp-4 + + push ebx ;save exception number (#PF) + mov ebp, esp + mov ebx, cr2 + push ebx ;that is locals: .err_addr = cr2 + inc [pg_data.pages_faults] + + mov eax, [pf_err_code] + + cmp ebx, OS_BASE ;ebx == .err_addr + jb .user_space ;страница в памяти приложения ; + + cmp ebx, page_tabs + jb .kernel_space ;страница в памяти ядра + + cmp ebx, kernel_tabs + jb .alloc;.app_tabs ;таблицы страниц приложения ; + ;просто создадим одну +if 0 ;пока это просто лишнее + cmp ebx, LFB_BASE + jb .core_tabs ;таблицы страниц ядра + ;Ошибка + .lfb: + ;область LFB + ;Ошибка + jmp .fail +end if +.core_tabs: +.fail: ;simply return to caller + mov esp, ebp + pop ebx ;restore exception number (#PF) + ret + +; xchg bx, bx +; add esp,12 ;clear in stack: locals(.err_addr) + #PF + ret_to_caller +; restore_ring3_context +; iretd + +.user_space: + test eax, PG_MAP + jnz .err_access ;Страница присутствует + ;Ошибка доступа ? + + shr ebx, 12 + mov ecx, ebx + shr ecx, 10 + mov edx, [master_tab+ecx*4] + test edx, PG_MAP + jz .fail ;таблица страниц не создана + ;неверный адрес в программе + + mov eax, [page_tabs+ebx*4] + test eax, 2 + jz .fail ;адрес не зарезервирован для ; + ;использования. Ошибка +.alloc: + call alloc_page + test eax, eax + jz .fail + + stdcall map_page, [.err_addr], eax, PG_UW + + mov edi, [.err_addr] + and edi, 0xFFFFF000 + mov ecx, 1024 + xor eax, eax + ;cld ;caller is duty for this + rep stosd +.exit: ;iret with repeat fault instruction + add esp, 12;clear in stack: locals(.err_addr) + #PF + ret_to_caller + restore_ring3_context + iretd + +.err_access: +; access denied? this may be a result of copy-on-write protection for DLL +; check list of HDLLs + and ebx, not 0xFFF + mov eax, [CURRENT_TASK] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.dlls_list_ptr] + test eax, eax + jz .fail + mov esi, [eax+HDLL.fd] +.scan_hdll: + cmp esi, eax + jz .fail + mov edx, ebx + sub edx, [esi+HDLL.base] + cmp edx, [esi+HDLL.size] + jb .fault_in_hdll +.scan_hdll.next: + mov esi, [esi+HDLL.fd] + jmp .scan_hdll +.fault_in_hdll: +; allocate new page, map it as rw and copy data + call alloc_page + test eax, eax + jz .fail + stdcall map_page, ebx, eax, PG_UW + mov edi, ebx + mov ecx, 1024 + sub ebx, [esi+HDLL.base] + mov esi, [esi+HDLL.parent] + mov esi, [esi+DLLDESCR.data] + add esi, ebx + rep movsd + jmp .exit + +.kernel_space: + test eax, PG_MAP + jz .fail ;страница не присутствует + + test eax, 12 ;U/S (+below) + jnz .fail ;приложение обратилось к памяти + ;ядра + ;test eax, 8 + ;jnz .fail ;установлен зарезервированный бит + ;в таблицах страниц. добавлено в P4/Xeon + +;попытка записи в защищённую страницу ядра + + cmp ebx, tss._io_map_0 + jb .fail + + cmp ebx, tss._io_map_0+8192 + jae .fail + +; io permission map +; copy-on-write protection + + call alloc_page + test eax, eax + jz .fail + + push eax + stdcall map_page, [.err_addr], eax, dword PG_SW + pop eax + mov edi, [.err_addr] + and edi, -4096 + lea esi, [edi+(not tss._io_map_0)+1]; -tss._io_map_0 + + mov ebx, esi + shr ebx, 12 + mov edx, [current_slot] + or eax, PG_SW + mov [edx+APPDATA.io_map+ebx*4], eax + + add esi, [default_io_map] + mov ecx, 4096/4 + ;cld ;caller is duty for this + rep movsd + jmp .exit +endp + +; returns number of mapped bytes +proc map_mem stdcall, lin_addr:dword,slot:dword,\ + ofs:dword,buf_size:dword,req_access:dword + push 0 ; initialize number of mapped bytes + + cmp [buf_size], 0 + jz .exit + + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.process] + mov eax, [eax+PROC.pdt_0_phys] + and eax, 0xFFFFF000 + + stdcall map_page, [ipc_pdir], eax, PG_UW + mov ebx, [ofs] + shr ebx, 22 + mov esi, [ipc_pdir] + mov edi, [ipc_ptab] + mov eax, [esi+ebx*4] + and eax, 0xFFFFF000 + jz .exit + stdcall map_page, edi, eax, PG_UW + +@@: + mov edi, [lin_addr] + and edi, 0xFFFFF000 + mov ecx, [buf_size] + add ecx, 4095 + shr ecx, 12 + inc ecx + + mov edx, [ofs] + shr edx, 12 + and edx, 0x3FF + mov esi, [ipc_ptab] + +.map: + stdcall safe_map_page, [slot], [req_access], [ofs] + jnc .exit + add dword [ebp-4], 4096 + add [ofs], 4096 + dec ecx + jz .exit + add edi, 0x1000 + inc edx + cmp edx, 0x400 + jnz .map + inc ebx + mov eax, [ipc_pdir] + mov eax, [eax+ebx*4] + and eax, 0xFFFFF000 + jz .exit + stdcall map_page, esi, eax, PG_UW + xor edx, edx + jmp .map + +.exit: + pop eax + ret +endp + +proc map_memEx stdcall, lin_addr:dword,slot:dword,\ + ofs:dword,buf_size:dword,req_access:dword + push 0 ; initialize number of mapped bytes + + cmp [buf_size], 0 + jz .exit + + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.process] + mov eax, [eax+PROC.pdt_0_phys] + and eax, 0xFFFFF000 + + stdcall map_page, [proc_mem_pdir], eax, PG_UW + mov ebx, [ofs] + shr ebx, 22 + mov esi, [proc_mem_pdir] + mov edi, [proc_mem_tab] + mov eax, [esi+ebx*4] + and eax, 0xFFFFF000 + test eax, eax + jz .exit + stdcall map_page, edi, eax, PG_UW + +@@: + mov edi, [lin_addr] + and edi, 0xFFFFF000 + mov ecx, [buf_size] + add ecx, 4095 + shr ecx, 12 + inc ecx + + mov edx, [ofs] + shr edx, 12 + and edx, 0x3FF + mov esi, [proc_mem_tab] + +.map: + stdcall safe_map_page, [slot], [req_access], [ofs] + jnc .exit + add dword [ebp-4], 0x1000 + add edi, 0x1000 + add [ofs], 0x1000 + inc edx + dec ecx + jnz .map +.exit: + pop eax + ret +endp + +; in: esi+edx*4 = pointer to page table entry +; in: [slot], [req_access], [ofs] on the stack +; in: edi = linear address to map +; out: CF cleared <=> failed +; destroys: only eax +proc safe_map_page stdcall, slot:dword, req_access:dword, ofs:dword + mov eax, [esi+edx*4] + test al, PG_MAP + jz .not_present + test al, PG_WRITE + jz .resolve_readonly +; normal case: writable page, just map with requested access +.map: + stdcall map_page, edi, eax, [req_access] + stc +.fail: + ret +.not_present: +; check for alloc-on-demand page + test al, 2 + jz .fail +; allocate new page, save it to source page table + push ecx + call alloc_page + pop ecx + test eax, eax + jz .fail + or al, PG_UW + mov [esi+edx*4], eax + jmp .map +.resolve_readonly: +; readonly page, probably copy-on-write +; check: readonly request of readonly page is ok + test [req_access], PG_WRITE + jz .map +; find control structure for this page + pushf + cli + cld + push ebx ecx + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.dlls_list_ptr] + test eax, eax + jz .no_hdll + mov ecx, [eax+HDLL.fd] +.scan_hdll: + cmp ecx, eax + jz .no_hdll + mov ebx, [ofs] + and ebx, not 0xFFF + sub ebx, [ecx+HDLL.base] + cmp ebx, [ecx+HDLL.size] + jb .hdll_found + mov ecx, [ecx+HDLL.fd] + jmp .scan_hdll +.no_hdll: + pop ecx ebx + popf + clc + ret +.hdll_found: +; allocate page, save it in page table, map it, copy contents from base + mov eax, [ecx+HDLL.parent] + add ebx, [eax+DLLDESCR.data] + call alloc_page + test eax, eax + jz .no_hdll + or al, PG_UW + mov [esi+edx*4], eax + stdcall map_page, edi, eax, [req_access] + push esi edi + mov esi, ebx + mov ecx, 4096/4 + rep movsd + pop edi esi + pop ecx ebx + popf + stc + ret +endp + +sys_IPC: +;input: +; ebx=1 - set ipc buffer area +; ecx=address of buffer +; edx=size of buffer +; eax=2 - send message +; ebx=PID +; ecx=address of message +; edx=size of message + + dec ebx + jnz @f + + mov eax, [current_slot] + pushf + cli + mov [eax+APPDATA.ipc_start], ecx ;set fields in extended information area + mov [eax+APPDATA.ipc_size], edx + + add edx, ecx + add edx, 4095 + and edx, not 4095 + +.touch: + mov eax, [ecx] + add ecx, 0x1000 + cmp ecx, edx + jb .touch + + popf + mov [esp+32], ebx ;ebx=0 + ret + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;2 +@@: + dec ebx + jnz @f + + stdcall sys_ipc_send, ecx, edx, esi + mov [esp+32], eax + ret +@@: + or eax, -1 + mov [esp+32], eax + ret + +;align 4 +;proc set_ipc_buff + +; mov eax,[current_slot] +; pushf +; cli +; mov [eax+APPDATA.ipc_start],ebx ;set fields in extended information area +; mov [eax+APPDATA.ipc_size],ecx +; +; add ecx, ebx +; add ecx, 4095 +; and ecx, not 4095 +; +;.touch: mov eax, [ebx] +; add ebx, 0x1000 +; cmp ebx, ecx +; jb .touch +; +; popf +; xor eax, eax +; ret +;endp + +proc sys_ipc_send stdcall, PID:dword, msg_addr:dword, msg_size:dword + locals + dst_slot dd ? + dst_offset dd ? + buf_size dd ? + used_buf dd ? + endl + + pushf + cli + + mov eax, [PID] + call pid_to_slot + test eax, eax + jz .no_pid + + mov [dst_slot], eax + shl eax, 8 + mov edi, [eax+SLOT_BASE+0xa0] ;is ipc area defined? + test edi, edi + jz .no_ipc_area + + mov ebx, edi + and ebx, 0xFFF + mov [dst_offset], ebx + + mov esi, [eax+SLOT_BASE+0xa4] + mov [buf_size], esi + + mov ecx, [ipc_tmp] + cmp esi, 0x40000-0x1000; size of [ipc_tmp] minus one page + jbe @f + push esi edi + add esi, 0x1000 + stdcall alloc_kernel_space, esi + mov ecx, eax + pop edi esi +@@: + mov [used_buf], ecx + stdcall map_mem, ecx, [dst_slot], \ + edi, esi, PG_SW + + mov edi, [dst_offset] + add edi, [used_buf] + cmp dword [edi], 0 + jnz .ipc_blocked ;if dword [buffer]<>0 - ipc blocked now + + mov edx, dword [edi+4] + lea ebx, [edx+8] + add ebx, [msg_size] + cmp ebx, [buf_size] + ja .buffer_overflow ;esi<0 - not enough memory in buffer + + mov dword [edi+4], ebx + mov eax, [TASK_BASE] + mov eax, [eax+0x04] ;eax - our PID + add edi, edx + mov [edi], eax + mov ecx, [msg_size] + + mov [edi+4], ecx + add edi, 8 + mov esi, [msg_addr] + ; add esi, new_app_base + cld + rep movsb + + mov ebx, [ipc_tmp] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov ebx, [ipc_pdir] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov ebx, [ipc_ptab] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov eax, [dst_slot] + shl eax, 8 + or [eax+SLOT_BASE+0xA8], dword 0x40 + push 0 + jmp .ret +.no_pid: + popf + mov eax, 4 + ret +.no_ipc_area: + popf + xor eax, eax + inc eax + ret +.ipc_blocked: + push 2 + jmp .ret +.buffer_overflow: + push 3 +.ret: + mov eax, [used_buf] + cmp eax, [ipc_tmp] + jz @f + stdcall free_kernel_space, eax +@@: + pop eax + popf + ret +endp + +align 4 +sysfn_meminfo: + + ; add ecx, new_app_base + cmp ecx, OS_BASE + jae .fail + + mov eax, [pg_data.pages_count] + mov [ecx], eax + shl eax, 12 + mov [esp+32], eax + mov eax, [pg_data.pages_free] + mov [ecx+4], eax + mov eax, [pg_data.pages_faults] + mov [ecx+8], eax + mov eax, [heap_size] + mov [ecx+12], eax + mov eax, [heap_free] + mov [ecx+16], eax + mov eax, [heap_blocks] + mov [ecx+20], eax + mov eax, [free_blocks] + mov [ecx+24], eax + ret +.fail: + or dword [esp+32], -1 + ret + +align 4 +f68: + cmp ebx, 4 + jbe sys_sheduler + + cmp ebx, 11 + jb .fail + + cmp ebx, 27 + ja .fail + + jmp dword [f68call+ebx*4-11*4] +.11: + call init_heap + mov [esp+32], eax + ret +.12: + stdcall user_alloc, ecx + mov [esp+32], eax + ret +.13: + stdcall user_free, ecx + mov [esp+32], eax + ret +.14: + cmp ecx, OS_BASE + jae .fail + mov edi, ecx + call get_event_ex + mov [esp+32], eax + ret +.16: + test ecx, ecx + jz .fail + cmp ecx, OS_BASE + jae .fail + stdcall get_service, ecx + mov [esp+32], eax + ret +.17: + call srv_handlerEx ;ecx + mov [esp+32], eax + ret +.19: + cmp ecx, OS_BASE + jae .fail + stdcall load_library, ecx + mov [esp+32], eax + ret +.20: + mov eax, edx + mov ebx, ecx + call user_realloc ;in: eax = pointer, ebx = new size + mov [esp+32], eax + ret +.21: + cmp ecx, OS_BASE + jae .fail + + cmp edx, OS_BASE + jae .fail + + stdcall load_pe_driver, ecx, edx + mov [esp+32], eax + ret +.22: + cmp ecx, OS_BASE + jae .fail + + stdcall shmem_open, ecx, edx, esi + mov [esp+24], edx + mov [esp+32], eax + ret + +.23: + cmp ecx, OS_BASE + jae .fail + + stdcall shmem_close, ecx + mov [esp+32], eax + ret +.24: + mov eax, [current_slot] + xchg ecx, [eax+APPDATA.exc_handler] + xchg edx, [eax+APPDATA.except_mask] + mov [esp+32], ecx ; reg_eax+8 + mov [esp+20], edx ; reg_ebx+8 + ret +.25: + cmp ecx, 32 + jae .fail + mov eax, [current_slot] + btr [eax+APPDATA.except_mask], ecx + setc byte[esp+32] + jecxz @f + bts [eax+APPDATA.except_mask], ecx +@@: + ret + +.26: + stdcall user_unmap, ecx, edx, esi + mov [esp+32], eax + ret + +.27: + cmp ecx, OS_BASE + jae .fail + + stdcall load_file_umode, ecx + mov [esp+24], edx + mov [esp+32], eax + ret + +.fail: + xor eax, eax + mov [esp+32], eax + ret + + +align 4 +f68call: ; keep this table closer to main code + + dd f68.11 ; init_heap + dd f68.12 ; user_alloc + dd f68.13 ; user_free + dd f68.14 ; get_event_ex + dd f68.fail ; moved to f68.24 + dd f68.16 ; get_service + dd f68.17 ; call_service + dd f68.fail ; moved to f68.25 + dd f68.19 ; load_dll + dd f68.20 ; user_realloc + dd f68.21 ; load_driver + dd f68.22 ; shmem_open + dd f68.23 ; shmem_close + dd f68.24 ; set exception handler + dd f68.25 ; unmask exception + dd f68.26 ; user_unmap + dd f68.27 ; load_file_umode + + +align 4 +proc load_pe_driver stdcall, file:dword, cmdline:dword + push esi + + stdcall load_PE, [file] + test eax, eax + jz .fail + + mov esi, eax + push [cmdline] + push DRV_ENTRY + call eax + pop ecx + pop ecx + test eax, eax + jz .fail + + mov [eax+SRV.entry], esi + pop esi + ret + +.fail: + xor eax, eax + pop esi + ret +endp + +align 4 +proc init_mtrr + + cmp [BOOT_VARS+BOOT_MTRR], byte 2 + je .exit + + bt [cpu_caps], CAPS_MTRR + jnc .exit + + mov eax, cr0 + or eax, 0x60000000 ;disable caching + mov cr0, eax + wbinvd ;invalidate cache + + mov ecx, 0x2FF + rdmsr ; +; has BIOS already initialized MTRRs? + test ah, 8 + jnz .skip_init +; rarely needed, so mainly placeholder +; main memory - cached + push eax + + mov eax, [MEM_AMOUNT] +; round eax up to next power of 2 + dec eax + bsr ecx, eax + mov ebx, 2 + shl ebx, cl + dec ebx +; base of memory range = 0, type of memory range = MEM_WB + xor edx, edx + mov eax, MEM_WB + mov ecx, 0x200 + wrmsr +; mask of memory range = 0xFFFFFFFFF - (size - 1), ebx = size - 1 + mov eax, 0xFFFFFFFF + mov edx, 0x0000000F + sub eax, ebx + sbb edx, 0 + or eax, 0x800 + inc ecx + wrmsr +; clear unused MTRRs + xor eax, eax + xor edx, edx +@@: + inc ecx + wrmsr + cmp ecx, 0x20F + jb @b +; enable MTRRs + pop eax + or ah, 8 + and al, 0xF0; default memtype = UC + mov ecx, 0x2FF + wrmsr +.skip_init: + stdcall set_mtrr, [LFBAddress], [LFBSize], MEM_WC + + wbinvd ;again invalidate + + mov eax, cr0 + and eax, not 0x60000000 + mov cr0, eax ; enable caching +.exit: + ret +endp + +align 4 +proc set_mtrr stdcall, base:dword,size:dword,mem_type:dword +; find unused register + mov ecx, 0x201 +@@: + rdmsr + dec ecx + test ah, 8 + jz .found + rdmsr + mov al, 0; clear memory type field + cmp eax, [base] + jz .ret + add ecx, 3 + cmp ecx, 0x210 + jb @b +; no free registers, ignore the call +.ret: + ret +.found: +; found, write values + xor edx, edx + mov eax, [base] + or eax, [mem_type] + wrmsr + + mov ebx, [size] + dec ebx + mov eax, 0xFFFFFFFF + mov edx, 0x00000000 + sub eax, ebx + sbb edx, 0 + or eax, 0x800 + inc ecx + wrmsr + ret +endp + +align 4 +proc create_ring_buffer stdcall, size:dword, flags:dword + locals + buf_ptr dd ? + endl + + mov eax, [size] + test eax, eax + jz .fail + + add eax, eax + stdcall alloc_kernel_space, eax + test eax, eax + jz .fail + + push ebx + + mov [buf_ptr], eax + + mov ebx, [size] + shr ebx, 12 + push ebx + + stdcall alloc_pages, ebx + pop ecx + + test eax, eax + jz .mm_fail + + push edi + + or eax, [flags] + mov edi, [buf_ptr] + mov ebx, [buf_ptr] + mov edx, ecx + shl edx, 2 + shr edi, 10 +@@: + mov [page_tabs+edi], eax + mov [page_tabs+edi+edx], eax + invlpg [ebx] + invlpg [ebx+0x10000] + add eax, 0x1000 + add ebx, 0x1000 + add edi, 4 + dec ecx + jnz @B + + mov eax, [buf_ptr] + pop edi + pop ebx + ret +.mm_fail: + stdcall free_kernel_space, [buf_ptr] + xor eax, eax + pop ebx +.fail: + ret +endp + + +align 4 +proc print_mem + mov edi, BOOT_VAR + 0x9104 + mov ecx, [edi-4] + test ecx, ecx + jz .done + +@@: + mov eax, [edi] + mov edx, [edi+4] + add eax, [edi+8] + adc edx, [edi+12] + + DEBUGF 1, "K : E820 %x%x - %x%x type %d\n", \ + [edi+4], [edi],\ + edx, eax, [edi+16] + add edi, 20 + dec ecx + jnz @b +.done: + ret +endp diff --git a/kernel/branches/kolibri-process/core/peload.inc b/kernel/branches/kolibri-process/core/peload.inc new file mode 100644 index 000000000..9872e4304 --- /dev/null +++ b/kernel/branches/kolibri-process/core/peload.inc @@ -0,0 +1,282 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4421 $ + +include 'export.inc' + +align 4 + +proc load_PE stdcall, file_name:dword + locals + image dd ? + entry dd ? + base dd ? + endl + + stdcall load_file, [file_name] + test eax, eax + jz .fail + + mov [image], eax + + mov edx, [eax+60] + + stdcall kernel_alloc, [eax+80+edx] + test eax, eax + jz .cleanup + + mov [base], eax + + stdcall map_PE, eax, [image] + + mov [entry], eax + test eax, eax + jnz .cleanup + + stdcall kernel_free, [base] +.cleanup: + stdcall kernel_free, [image] + mov eax, [entry] + ret +.fail: + xor eax, eax + ret +endp + +DWORD equ dword +PTR equ + +align 4 +map_PE: ;stdcall base:dword, image:dword + cld + push ebp + push edi + push esi + push ebx + sub esp, 60 + mov ebx, DWORD PTR [esp+84] + mov ebp, DWORD PTR [esp+80] + mov edx, ebx + mov esi, ebx + add edx, DWORD PTR [ebx+60] + mov edi, ebp + mov DWORD PTR [esp+32], edx + mov ecx, DWORD PTR [edx+84] + + shr ecx, 2 + rep movsd + + movzx eax, WORD PTR [edx+6] + mov DWORD PTR [esp+36], 0 + mov DWORD PTR [esp+16], eax + jmp L2 +L3: + mov eax, DWORD PTR [edx+264] + test eax, eax + je L4 + mov esi, ebx + mov edi, ebp + add esi, DWORD PTR [edx+268] + mov ecx, eax + add edi, DWORD PTR [edx+260] + + add ecx, 3 + shr ecx, 2 + rep movsd + +L4: + mov ecx, DWORD PTR [edx+256] + cmp ecx, eax + jbe L6 + sub ecx, eax + add eax, DWORD PTR [edx+260] + lea edi, [eax+ebp] + + xor eax, eax + rep stosb + +L6: + inc DWORD PTR [esp+36] + add edx, 40 +L2: + mov esi, DWORD PTR [esp+16] + cmp DWORD PTR [esp+36], esi + jne L3 + mov edi, DWORD PTR [esp+32] + cmp DWORD PTR [edi+164], 0 + je L9 + pushd [edi+164] + mov esi, ebp + mov ecx, ebp + sub esi, DWORD PTR [edi+52] + add ecx, DWORD PTR [edi+160] + mov eax, esi + shr eax, 16 + mov DWORD PTR [esp+16], eax +L12: + mov eax, [ecx+4] + sub [esp], eax + lea ebx, [eax-8] + xor edi, edi + shr ebx, 1 + jmp L13 +L14: + movzx eax, WORD PTR [ecx+8+edi*2] + mov edx, eax + shr eax, 12 + and edx, 4095 + add edx, DWORD PTR [ecx] + cmp ax, 2 + je L17 + cmp ax, 3 + je L18 + dec ax + jne L15 + mov eax, DWORD PTR [esp+16] + add WORD PTR [edx+ebp], ax +L17: + add WORD PTR [edx+ebp], si +L18: + add DWORD PTR [edx+ebp], esi +L15: + inc edi +L13: + cmp edi, ebx + jne L14 + add ecx, DWORD PTR [ecx+4] +L11: + cmp dword [esp], 0 + jg L12 + pop eax +L9: + mov edx, DWORD PTR [esp+32] + cmp DWORD PTR [edx+132], 0 + je L20 + mov eax, ebp + add eax, DWORD PTR [edx+128] + mov DWORD PTR [esp+40], 0 + add eax, 20 + mov DWORD PTR [esp+56], eax +L22: + mov ecx, DWORD PTR [esp+56] + cmp DWORD PTR [ecx-16], 0 + jne L23 + cmp DWORD PTR [ecx-8], 0 + je L25 +L23: + mov edi, DWORD PTR [__exports+32] + mov esi, DWORD PTR [__exports+28] + mov eax, DWORD PTR [esp+56] + mov DWORD PTR [esp+20], edi + add edi, OS_BASE + add esi, OS_BASE + mov DWORD PTR [esp+44], esi + mov ecx, DWORD PTR [eax-4] + mov DWORD PTR [esp+48], edi + mov edx, DWORD PTR [eax-20] + test edx, edx + jnz @f + mov edx, ecx +@@: + mov DWORD PTR [esp+52], 0 + add ecx, ebp + add edx, ebp + mov DWORD PTR [esp+24], edx + mov DWORD PTR [esp+28], ecx +L26: + mov esi, DWORD PTR [esp+52] + mov edi, DWORD PTR [esp+24] + mov eax, DWORD PTR [edi+esi*4] + test eax, eax + je L27 + test eax, eax + js L27 + lea edi, [ebp+eax] + mov eax, DWORD PTR [esp+28] + mov DWORD PTR [eax+esi*4], 0 + lea esi, [edi+2] + push eax + push 32 + movzx eax, WORD PTR [edi] + mov edx, DWORD PTR [esp+56] + mov eax, DWORD PTR [edx+eax*4] + add eax, OS_BASE + push eax + push esi + call strncmp + pop ebx + xor ebx, ebx + test eax, eax + jne L32 + jmp L30 +L33: + push ecx + push 32 + mov ecx, DWORD PTR [esp+28] + mov eax, DWORD PTR [ecx+OS_BASE+ebx*4] + add eax, OS_BASE + push eax + push esi + call strncmp + pop edx + test eax, eax + jne L34 + mov esi, DWORD PTR [esp+44] + mov edx, DWORD PTR [esp+52] + mov ecx, DWORD PTR [esp+28] + mov eax, DWORD PTR [esi+ebx*4] + add eax, OS_BASE + mov DWORD PTR [ecx+edx*4], eax + jmp L36 +L34: + inc ebx +L32: + cmp ebx, DWORD PTR [__exports+24] + jb L33 +L36: + cmp ebx, DWORD PTR [__exports+24] + jne L37 + + mov esi, msg_unresolved + call sys_msg_board_str + lea esi, [edi+2] + call sys_msg_board_str + mov esi, msg_CR + call sys_msg_board_str + + mov DWORD PTR [esp+40], 1 + jmp L37 +L30: + movzx eax, WORD PTR [edi] + mov esi, DWORD PTR [esp+44] + mov edi, DWORD PTR [esp+52] + mov edx, DWORD PTR [esp+28] + mov eax, DWORD PTR [esi+eax*4] + add eax, OS_BASE + mov DWORD PTR [edx+edi*4], eax +L37: + inc DWORD PTR [esp+52] + jmp L26 +L27: + add DWORD PTR [esp+56], 20 + jmp L22 +L25: + xor eax, eax + cmp DWORD PTR [esp+40], 0 + jne L40 +L20: + mov ecx, DWORD PTR [esp+32] + mov eax, ebp + add eax, DWORD PTR [ecx+40] +L40: + add esp, 60 + pop ebx + pop esi + pop edi + pop ebp + ret 8 diff --git a/kernel/branches/kolibri-process/core/sched.inc b/kernel/branches/kolibri-process/core/sched.inc new file mode 100644 index 000000000..7558e345c --- /dev/null +++ b/kernel/branches/kolibri-process/core/sched.inc @@ -0,0 +1,513 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3617 $ + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IRQ0 HANDLER (TIMER INTERRUPT) ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +align 32 +irq0: + pushad + Mov ds, ax, app_data + mov es, ax + inc [timer_ticks] + mov eax, [timer_ticks] + call playNote ; <<<--- Speaker driver + sub eax, [next_usage_update] + cmp eax, 100 + jb .nocounter + add [next_usage_update], 100 + call updatecputimes + .nocounter: + xor ecx, ecx ; send End Of Interrupt signal + call irq_eoi +; btr dword[DONT_SWITCH], 0 +; jc .return + mov bl, SCHEDULE_ANY_PRIORITY + call find_next_task + jz .return ; if there is only one running process + call do_change_task + .return: + popad + iretd + +align 4 +change_task: + pushfd + cli + pushad +if 0 +; \begin{Mario79} ; <- must be refractoried, if used... + cmp [dma_task_switched], 1 + jne .find_next_task + mov [dma_task_switched], 0 + mov ebx, [dma_process] + cmp [CURRENT_TASK], ebx + je .return + mov edi, [dma_slot_ptr] + mov [CURRENT_TASK], ebx + mov [TASK_BASE], edi + jmp @f +.find_next_task: +; \end{Mario79} +end if + mov bl, SCHEDULE_ANY_PRIORITY + call find_next_task + jz .return ; the same task -> skip switch + @@: +; mov byte[DONT_SWITCH], 1 + call do_change_task + .return: + popad + popfd + ret + +uglobal +align 4 +; far_jump: +; .offs dd ? +; .sel dw ? + context_counter dd 0 ;noname & halyavin + next_usage_update dd 0 + timer_ticks dd 0 +; prev_slot dd ? +; event_sched dd ? +endg + +align 4 +update_counters: + mov edi, [TASK_BASE] + rdtsc + sub eax, [edi+TASKDATA.counter_add] ; time stamp counter add + add [edi+TASKDATA.counter_sum], eax ; counter sum + ret +align 4 +updatecputimes: + mov ecx, [TASK_COUNT] + mov edi, TASK_DATA + .newupdate: + xor eax, eax + xchg eax, [edi+TASKDATA.counter_sum] + mov [edi+TASKDATA.cpu_usage], eax + add edi, 0x20 + loop .newupdate + ret + +;TODO: Надо бы убрать использование do_change_task из V86... +; и после этого перенести обработку TASKDATA.counter_add/sum в do_change_task + +align 4 +do_change_task: +;param: +; ebx = address of the APPDATA for incoming task (new) +;warning: +; [CURRENT_TASK] and [TASK_BASE] must be changed before (e.g. in find_next_task) +; [current_slot] is the outcoming (old), and set here to a new value (ebx) +;scratched: eax,ecx,esi + mov esi, ebx + xchg esi, [current_slot] +; set new stack after saving old + mov [esi+APPDATA.saved_esp], esp + mov esp, [ebx+APPDATA.saved_esp] +; set new thread io-map + Mov dword [page_tabs+((tss._io_map_0 and -4096) shr 10)],eax,[ebx+APPDATA.io_map] + Mov dword [page_tabs+((tss._io_map_1 and -4096) shr 10)],eax,[ebx+APPDATA.io_map+4] +; set new thread memory-map + mov ecx, APPDATA.process + mov eax, [ebx+ecx] ;offset>0x7F + cmp eax, [esi+ecx] ;offset>0x7F + je @f + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax +@@: +; set tss.esp0 + + Mov [tss._esp0],eax,[ebx+APPDATA.saved_esp0] + + mov edx, [ebx+APPDATA.tls_base] + cmp edx, [esi+APPDATA.tls_base] + je @f + + mov [tls_data_l+2], dx + shr edx, 16 + mov [tls_data_l+4], dl + mov [tls_data_l+7], dh + + mov dx, app_tls + mov fs, dx +@@: +; set gs selector unconditionally + Mov gs,ax,graph_data + ; set CR0.TS + cmp bh, byte[fpu_owner] ;bh == incoming task (new) + clts ;clear a task switch flag + je @f + mov eax, cr0 ;and set it again if the owner + or eax, CR0_TS ;of a fpu has changed + mov cr0, eax + @@: ; set context_counter (only for user pleasure ???) + inc [context_counter] ;noname & halyavin + ; set debug-registers, if it's necessary + test byte[ebx+APPDATA.dbg_state], 1 + jz @f + xor eax, eax + mov dr6, eax + lea esi, [ebx+APPDATA.dbg_regs] + cld + macro lodsReg [reg] { + lodsd + mov reg, eax + } lodsReg dr0, dr1, dr2, dr3, dr7 + purge lodsReg + @@: + ret +;end. + + + +struct MUTEX_WAITER + list LHEAD + task dd ? +ends + +;void __fastcall mutex_init(struct mutex *lock) + +align 4 +mutex_init: + mov [ecx+MUTEX.lhead.next], ecx + mov [ecx+MUTEX.lhead.prev], ecx + mov [ecx+MUTEX.count], 1 + ret + + +;void __fastcall mutex_lock(struct mutex *lock) + +align 4 +mutex_lock: + + dec [ecx+MUTEX.count] + jns .done + + pushfd + cli + + sub esp, sizeof.MUTEX_WAITER + + list_add_tail esp, ecx ;esp= new waiter, ecx= list head + + mov edx, [TASK_BASE] + mov [esp+MUTEX_WAITER.task], edx + +.forever: + + mov eax, -1 + xchg eax, [ecx+MUTEX.count] + dec eax + jz @F + + mov [edx+TASKDATA.state], 1 + call change_task + jmp .forever +@@: + mov edx, [esp+MUTEX_WAITER.list.next] + mov eax, [esp+MUTEX_WAITER.list.prev] + + mov [eax+MUTEX_WAITER.list.next], edx + mov [edx+MUTEX_WAITER.list.prev], eax + cmp [ecx+MUTEX.lhead.next], ecx + jne @F + + mov [ecx+MUTEX.count], 0 +@@: + add esp, sizeof.MUTEX_WAITER + + popfd +.done: + ret + +;void __fastcall mutex_unlock(struct mutex *lock) + +align 4 +mutex_unlock: + + pushfd + cli + + mov eax, [ecx+MUTEX.lhead.next] + cmp eax, ecx + mov [ecx+MUTEX.count], 1 + je @F + + mov eax, [eax+MUTEX_WAITER.task] + mov [eax+TASKDATA.state], 0 +@@: + popfd + ret + + +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 + +SCHEDULE_ANY_PRIORITY = 0 +SCHEDULE_HIGHER_PRIORITY = 1 +;info: +; Find next task to execute +;in: +; bl = SCHEDULE_ANY_PRIORITY: +; consider threads with any priority +; bl = SCHEDULE_HIGHER_PRIORITY: +; consider only threads with strictly higher priority than the current one, +; keep running the current thread if other ready threads have the same or lower priority +;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 + push NR_SCHED_QUEUES +; If bl == SCHEDULE_ANY_PRIORITY = 0, loop over all NR_SCHED lists. +; Otherwise, loop over first [APPDATA.priority] lists. + test bl, bl + jz .start + mov ebx, [current_slot] + mov edi, [TASK_BASE] + mov eax, [ebx+APPDATA.priority] + test eax, eax + jz .unlock_found + mov [esp], eax +.start: + 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 +; If we have selected a thread with higher priority +; AND rescheduling is due to IRQ, +; turn the current scheduler list one entry back, +; so the current thread will be next after high-priority thread is done. + mov ecx, [esp] + cmp ecx, NR_SCHED_QUEUES + jz .unlock_found + mov eax, [current_slot] + mov eax, [eax+APPDATA.in_schedule.prev] + mov [scheduler_current+ecx*4], eax +.unlock_found: + pop ecx + 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, [esp] + jb .priority_loop + mov ebx, [current_slot] + mov edi, [TASK_BASE] + jmp .unlock_found +endp + +if 0 + +struc TIMER +{ + .next dd ? + .exp_time dd ? + .func dd ? + .arg dd ? +} + + +uglobal +rdy_head rd 16 +endg + +align 4 +pick_task: + + xor eax, eax + .pick: + mov ebx, [rdy_head+eax*4] + test ebx, ebx + jz .next + + mov [next_task], ebx + test [ebx+flags.billable] + jz @F + mov [bill_task], ebx + @@: + ret + .next: + inc eax + jmp .pick + +; param +; eax= task +; +; retval +; eax= task +; ebx= queue +; ecx= front if 1 or back if 0 +align 4 +shed: + cmp [eax+.tics_left], 0;signed compare + mov ebx, [eax+.priority] + setg ecx + jg @F + + mov edx, [eax+.tics_quantum] + mov [eax+.ticks_left], edx + cmp ebx, (IDLE_PRIORITY-1) + je @F + inc ebx + @@: + ret + +; param +; eax= task +align 4 +enqueue: + call shed;eax + cmp [rdy_head+ebx*4], 0 + jnz @F + + mov [rdy_head+ebx*4], eax + mov [rdy_tail+ebx*4], eax + mov [eax+.next_ready], 0 + jmp .pick + @@: + test ecx, ecx + jz .back + + mov ecx, [rdy_head+ebx*4] + mov [eax+.next_ready], ecx + mov [rdy_head+ebx*4], eax + jmp .pick + .back: + mov ecx, [rdy_tail+ebx*4] + mov [ecx+.next_ready], eax + mov [rdy_tail+ebx*4], eax + mov [eax+.next_ready], 0 + .pick: + call pick_proc;select next task + ret + +end if diff --git a/kernel/branches/kolibri-process/core/string.inc b/kernel/branches/kolibri-process/core/string.inc new file mode 100644 index 000000000..6da273c70 --- /dev/null +++ b/kernel/branches/kolibri-process/core/string.inc @@ -0,0 +1,188 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Author: Kees J. Bot 1 Jan 1994 ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; size_t strncat(char *s1, const char *s2, size_t n) +; Append string s2 to s1. + +; char *strchr(const char *s, int c) + + +; int strncmp(const char *s1, const char *s2, size_t n) +; Compare two strings. + +; char *strncpy(char *s1, const char *s2, size_t n) +; Copy string s2 to s1. + +; size_t strnlen(const char *s, size_t n) +; Return the length of a string. + +; proc strrchr stdcall, s:dword, c:dword +; Look for the last occurrence a character in a string. + +proc strncat stdcall, s1:dword, s2:dword, n:dword + push esi + push edi + mov edi, [s1] ; String s1 + mov edx, [n] ; Maximum length + + mov ecx, -1 + xor al, al ; Null byte + cld + repne scasb ; Look for the zero byte in s1 + dec edi ; Back one up (and clear 'Z' flag) + push edi ; Save end of s1 + mov edi, [s2] ; edi = string s2 + mov ecx, edx ; Maximum count + repne scasb ; Look for the end of s2 + jne @F + inc ecx ; Exclude null byte +@@: + sub edx, ecx ; Number of bytes in s2 + mov ecx, edx + mov esi, [s2] ; esi = string s2 + pop edi ; edi = end of string s1 + rep movsb ; Copy bytes + stosb ; Add a terminating null + mov eax, [s1] ; Return s1 + pop edi + pop esi + ret +endp + +align 4 +proc strncmp stdcall, s1:dword, s2:dword, n:dword + + push esi + push edi + mov ecx, [n] + test ecx, ecx ; Max length is zero? + je .done + + mov esi, [s1] ; esi = string s1 + mov edi, [s2] ; edi = string s2 + cld +.compare: + cmpsb ; Compare two bytes + jne .done + cmp byte [esi-1], 0 ; End of string? + je .done + dec ecx ; Length limit reached? + jne .compare +.done: + seta al ; al = (s1 > s2) + setb ah ; ah = (s1 < s2) + sub al, ah + movsx eax, al ; eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1 + pop edi + pop esi + ret +endp + +align 4 +proc strncpy stdcall, s1:dword, s2:dword, n:dword + + push esi + push edi + + mov ecx, [n] ; Maximum length + mov edi, [s2] ; edi = string s2 + xor al, al ; Look for a zero byte + mov edx, ecx ; Save maximum count + cld + repne scasb ; Look for end of s2 + sub edx, ecx ; Number of bytes in s2 including null + xchg ecx, edx + mov esi, [s2] ; esi = string s2 + mov edi, [s1] ; edi = string s1 + rep movsb ; Copy bytes + + mov ecx, edx ; Number of bytes not copied + rep stosb ; strncpy always copies n bytes by null padding + mov eax, [s1] ; Return s1 + pop edi + pop esi + ret +endp + +align 4 +proc strnlen stdcall, s:dword, n:dword + + push edi + mov edi, [s] ; edi = string + xor al, al ; Look for a zero byte + mov edx, ecx ; Save maximum count + cmp cl, 1 ; 'Z' bit must be clear if ecx = 0 + cld + repne scasb ; Look for zero + jne @F + inc ecx ; Don't count zero byte +@@: + mov eax, edx + sub eax, ecx ; Compute bytes scanned + pop edi + ret +endp + +align 4 +proc strchr stdcall, s:dword, c:dword + push edi + cld + mov edi, [s] ; edi = string + mov edx, 16 ; Look at small chunks of the string +.next: + shl edx, 1 ; Chunks become bigger each time + mov ecx, edx + xor al, al ; Look for the zero at the end + repne scasb + pushf ; Remember the flags + sub ecx, edx + neg ecx ; Some or all of the chunk + sub edi, ecx ; Step back + mov eax, [c] ; The character to look for + repne scasb + je .found + popf ; Did we find the end of string earlier? + jne .next ; No, try again + xor eax, eax ; Return NULL + pop edi + ret +.found: + pop eax ; Get rid of those flags + lea eax, [edi-1] ; Address of byte found + pop edi + ret + +endp + + +proc strrchr stdcall, s:dword, c:dword + push edi + mov edi, [s] ; edi = string + mov ecx, -1 + xor al, al + cld + repne scasb ; Look for the end of the string + not ecx ; -1 - ecx = Length of the string + null + dec edi ; Put edi back on the zero byte + mov eax, [c] ; The character to look for + std ; Downwards search + repne scasb + cld ; Direction bit back to default + jne .fail + lea eax, [edi+1] ; Found it + pop edi + ret +.fail: + xor eax, eax ; Not there + pop edi + ret +endp + + diff --git a/kernel/branches/kolibri-process/core/sync.inc b/kernel/branches/kolibri-process/core/sync.inc new file mode 100644 index 000000000..1e477aa49 --- /dev/null +++ b/kernel/branches/kolibri-process/core/sync.inc @@ -0,0 +1,119 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Synhronization for MenuetOS. ;; +;; Author: Halyavin Andrey, halyavin@land.ru ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +if ~defined sync_inc +sync_inc_fix: +sync_inc fix sync_inc_fix + +;simplest mutex. +macro SimpleMutex name +{ +; iglobal + name dd 0 + name#.type = 1 +; endg +} +macro WaitSimpleMutex name +{ + local start_wait,ok +start_wait=$ + cli + cmp [name], dword 0 + jz ok + sti + call change_task + jmp start_wait +ok=$ + push eax + mov eax, dword [TASK_BASE+second_base_address] + mov eax, [eax+TASKDATA.pid] + mov [name], eax + pop eax + sti +} +macro ReleaseSimpleMutex name +{ + mov [name], dword 0 +} +macro TryWaitSimpleMutex name ;result in eax and in flags +{ + local ok,try_end + cmp [name], dword 0 + jz ok + xor eax, eax + jmp try_end +ok=$ + xor eax, eax + inc eax +try_end=$ +} +macro SimpleCriticalSection name +{ +; iglobal + name dd 0 + dd 0 + name#.type=2 +; endg +} +macro WaitSimpleCriticalSection name +{ + local start_wait,first_wait,inc_counter,end_wait + push eax + mov eax, [TASK_BASE+second_base_address] + mov eax, [eax+TASKDATA.pid] +start_wait=$ + cli + cmp [name], dword 0 + jz first_wait + cmp [name], eax + jz inc_counter + sti + call change_task + jmp start_wait +first_wait=$ + mov [name], eax + mov [name+4], dword 1 + jmp end_wait +inc_counter=$ + inc dword [name+4] +end_wait=$ + sti + pop eax +} +macro ReleaseSimpleCriticalSection name +{ + local release_end + dec dword [name+4] + jnz release_end + mov [name], dword 0 +release_end=$ +} +macro TryWaitSimpleCriticalSection name ;result in eax and in flags +{ + local ok,try_end + mov eax, [CURRENT_TASK+second_base_address] + mov eax, [eax+TASKDATA.pid] + cmp [name], eax + jz ok + cmp [name], 0 + jz ok + xor eax, eax + jmp try_end +ok=$ + xor eax, eax + inc eax +try_end=$ +} +_cli equ call MEM_HeapLock +_sti equ call MEM_HeapUnLock +end if + diff --git a/kernel/branches/kolibri-process/core/sys32-sp.inc b/kernel/branches/kolibri-process/core/sys32-sp.inc new file mode 100644 index 000000000..12a29eda4 --- /dev/null +++ b/kernel/branches/kolibri-process/core/sys32-sp.inc @@ -0,0 +1,4 @@ +; Éste archivo debe ser editado con codificación CP866 + + msg_sel_ker cp850 "núcleo", 0 + msg_sel_app cp850 "aplicación", 0 diff --git a/kernel/branches/kolibri-process/core/sys32.inc b/kernel/branches/kolibri-process/core/sys32.inc new file mode 100644 index 000000000..6ab130451 --- /dev/null +++ b/kernel/branches/kolibri-process/core/sys32.inc @@ -0,0 +1,849 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ;; +;; MenuetOS process management, protected ring3 ;; +;; ;; +;; Distributed under GPL. See file COPYING for details. ;; +;; Copyright 2003 Ville Turjanmaa ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4313 $ + +align 4 ;3A08 +build_interrupt_table: + mov edi, idts + mov esi, sys_int + mov ecx, 0x40 + mov eax, (10001110b shl 24) + os_code + @@: + movsw ;low word of code-entry + stosd ;interrupt gate type : os_code selector + movsw ;high word of code-entry + loop @b + movsd ;copy low dword of trap gate for int 0x40 + movsd ;copy high dword of trap gate for int 0x40 + lidt [esi] + ret + +iglobal + align 4 + sys_int: + ;exception handlers addresses (for interrupt gate construction) + dd e0,e1,e2,e3,e4,e5,e6,except_7 ; SEE: core/fpu.inc + dd e8,e9,e10,e11,e12,e13,page_fault_exc,e15 + dd e16, e17,e18, e19 + times 12 dd unknown_interrupt ;int_20..int_31 + + ;interrupt handlers addresses (for interrupt gate construction) + ; 0x20 .. 0x2F - IRQ handlers + dd irq0, irq_serv.irq_1, irq_serv.irq_2 + dd irq_serv.irq_3, irq_serv.irq_4 + dd irq_serv.irq_5, irq_serv.irq_6, irq_serv.irq_7 + dd irq_serv.irq_8, irq_serv.irq_9, irq_serv.irq_10 + dd irq_serv.irq_11, irq_serv.irq_12, irqD, irq_serv.irq_14, irq_serv.irq_15 + dd irq_serv.irq_16 + dd irq_serv.irq_17 + dd irq_serv.irq_18 + dd irq_serv.irq_19 + dd irq_serv.irq_20 + dd irq_serv.irq_21 + dd irq_serv.irq_22 + dd irq_serv.irq_23 + + times 32 - IRQ_RESERVED dd unknown_interrupt + ;int_0x40 gate trap (for directly copied) + dw i40 and 0xFFFF, os_code, 11101111b shl 8, i40 shr 16 + + idtreg: ; data for LIDT instruction (!!! must be immediately below sys_int data) + dw 2*($-sys_int-4)-1 + dd idts ;0x8000B100 + 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 + + msg_exc_8 db "Double fault", 0 + msg_exc_u db "Undefined Exception", 0 + msg_exc_a db "Invalid TSS", 0 + msg_exc_b db "Segment not present", 0 + msg_exc_c db "Stack fault", 0 + msg_exc_d db "General protection fault", 0 + msg_exc_e db "Page fault", 0 + + if lang eq sp + include 'core/sys32-sp.inc' + else + msg_sel_ker db "kernel", 0 + msg_sel_app db "application", 0 + end if + +endg + +macro save_ring3_context { + pushad +} +macro restore_ring3_context { + popad +} +macro exc_wo_code [num] { + e#num : + save_ring3_context + mov bl, num + jmp exc_c +} exc_wo_code 0,1,2,3,4,5,6,15,16,19 + +macro exc_w_code [num] { + e#num : + add esp, 4 + save_ring3_context + mov bl, num + jmp exc_c +} exc_w_code 8,9,10,11,12,13,17,18 + + +uglobal + pf_err_code dd ? +endg + +page_fault_exc: ; дуракоусточивость: селекторы испорчены... + pop [ss:pf_err_code]; действительно до следующего #PF + save_ring3_context + mov bl, 14 + +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 + reg_eax equ esp+0x1C + reg_ecx equ esp+0x18 + reg_edx equ esp+0x14 + reg_ebx equ esp+0x10 + reg_esp0 equ esp+0x0C + reg_ebp equ esp+0x08 + reg_esi equ esp+0x04 + reg_edi equ esp+0x00 + + 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 + jnz v86_exc_c + cmp bl, 14 ; #PF + jne @f + call page_fault_handler ; SEE: core/memory.inc + @@: + mov esi, [current_slot] + btr [esi+APPDATA.except_mask], ebx + jnc @f + mov eax, [esi+APPDATA.exc_handler] + test eax, eax + jnz IRetToUserHook + @@: + cli + mov eax, [esi+APPDATA.debugger_slot] + test eax, eax + jnz .debug + sti +; not debuggee => say error and terminate + call show_error_parameters ;; only ONE using, inline ??? + ;mov edx, [TASK_BASE] + mov [edx + TASKDATA.state], byte 4 ; terminate + call wakeup_osloop + call change_task +; If we're here, then the main OS thread has crashed before initializing IDLE thread. +; Or they both have crashed. Anyway, things are hopelessly broken. + hlt + jmp $-1 +.debug: +; we are debugged process, notify debugger and suspend ourself +; eax=debugger PID + mov ecx, 1 ; debug_message code=other_exception + cmp bl, 1 ; #DB + jne .notify ; notify debugger and suspend ourself + mov ebx, dr6 ; debug_message data=DR6_image + xor edx, edx + mov dr6, edx + mov edx, dr7 + mov cl, not 8 + .l1: + shl dl, 2 + jc @f + and bl, cl + @@: + sar cl, 1 + jc .l1 + mov cl, 3 ; debug_message code=debug_exception +.notify: + push ebx ; debug_message data + mov ebx, [TASK_BASE] + push [ebx+TASKDATA.pid] ; PID + push ecx ; debug_message code ((here: ecx==1/3)) + mov cl, 12 ; debug_message size + call debugger_notify ;; only ONE using, inline ??? SEE: core/debug.inc + add esp, 12 + mov edx, [TASK_BASE] + mov byte [edx+TASKDATA.state], 1 ; suspended + call change_task ; SEE: core/shed.inc + restore_ring3_context + iretd + +IRetToUserHook: + xchg eax, [reg_eip] + sub dword[reg_esp3], 8 + mov edi, [reg_esp3] + stosd + mov [edi], ebx + restore_ring3_context +; simply return control to interrupted process +unknown_interrupt: + iretd + +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +; bl - error vector +show_error_parameters: + cmp bl, 0x06 + jnz .no_ud + push ebx + mov ebx, ud_user_message + mov ebp, notifyapp + call fs_execute_from_sysdir_param + pop ebx +.no_ud: + mov edx, [TASK_BASE];not scratched below + if lang eq sp + DEBUGF 1, "K : Proceso - terminado forzado PID: %x [%s]\n", [edx+TASKDATA.pid], [current_slot] + else + DEBUGF 1, "K : Process - forced terminate PID: %x [%s]\n", [edx+TASKDATA.pid], [current_slot] + end if + cmp bl, 0x08 + jb .l0 + cmp bl, 0x0e + jbe .l1 + .l0: + mov bl, 0x09 + .l1: + mov eax, [msg_fault_sel+ebx*4 - 0x08*4] + DEBUGF 1, "K : %s\n", eax + mov eax, [reg_cs3+4] + mov edi, msg_sel_app + mov ebx, [reg_esp3+4] + cmp eax, app_code + je @f + mov edi, msg_sel_ker + mov ebx, [reg_esp0+4] + @@: + DEBUGF 1, "K : EAX : %x EBX : %x ECX : %x\n", [reg_eax+4], [reg_ebx+4], [reg_ecx+4] + DEBUGF 1, "K : EDX : %x ESI : %x EDI : %x\n", [reg_edx+4], [reg_esi+4], [reg_edi+4] + DEBUGF 1, "K : EBP : %x EIP : %x ESP : %x\n", [reg_ebp+4], [reg_eip+4], ebx + DEBUGF 1, "K : Flags : %x CS : %x (%s)\n", [reg_eflags+4], eax, edi + + DEBUGF 1, "K : Stack dump:\n" + push eax ebx ecx edx + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+00]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+04]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+08]: %x\n",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+12]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+16]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+20]: %x\n",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+24]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+28]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+32]: %x\n",[ebx] + pop edx ecx ebx eax + ret +.error_ESP: + pop edx ecx ebx eax + DEBUGF 1, "\n" + DEBUGF 1, "K : Unexpected end of the stack\n" + ret +;-------------------------------------- +.check_ESP: + push ebx + shr ebx, 12 + mov ecx, ebx + shr ecx, 10 + mov edx, [master_tab+ecx*4] + test edx, PG_MAP + jz .fail ;page table is not created + ;incorrect address in the program + + mov eax, [page_tabs+ebx*4] + test eax, 2 + jz .fail ;address not reserved for use. error + + pop ebx + xor eax, eax + ret + +.fail: + pop ebx + xor eax, eax + dec eax + ret +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + restore reg_ss + restore reg_esp3 + restore reg_eflags + restore reg_cs + restore reg_eip + restore reg_eax + restore reg_ecx + restore reg_edx + restore reg_ebx + restore reg_esp0 + restore reg_ebp + restore reg_esi + restore reg_edi + + +align 4 +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_owner], eax + + pop edx ecx eax + + ret + +align 4 +unlock_application_table: + push eax ecx edx + + mov [application_table_owner], 0 + mov ecx, application_table_mutex + call mutex_unlock + + pop edx ecx eax + + ret + +; * eax = 64 - номер функции +; * ebx = 1 - единственная подфункция +; * ecx = новый размер памяти +;Возвращаемое значение: +; * eax = 0 - успешно +; * eax = 1 - недостаточно памяти + +align 4 +sys_resize_app_memory: + ; ebx = 1 - resize + ; ecx = new amount of memory + +; cmp eax,1 + dec ebx + jnz .no_application_mem_resize + + mov eax, [pg_data.pages_free] + shl eax, 12 + cmp eax, ecx + jae @f + + xor eax, eax + inc eax + jmp .store_result +@@: + stdcall new_mem_resize, ecx +.store_result: + mov [esp+32], eax +.no_application_mem_resize: + ret + +iglobal +; process_terminating db 'K : Process - terminating',13,10,0 +; process_terminated db 'K : Process - done',13,10,0 + msg_obj_destroy db 'K : destroy app object',13,10,0 +endg + +; param +; esi= slot + +align 4 +terminate: ; terminate application + + .slot equ esp ;locals + + push esi ;save .slot + + shl esi, 8 + cmp [SLOT_BASE+esi+APPDATA.process], 0 + jne @F + pop esi + shl esi, 5 + 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 + call lock_application_table + +; if the process is in V86 mode... + mov eax, [.slot] + shl eax, 8 + mov esi, [eax+SLOT_BASE+APPDATA.pl0_stack] + add esi, RING0_STACK_SIZE + cmp [eax+SLOT_BASE+APPDATA.saved_esp0], esi + jz .nov86 +; ...it has page directory for V86 mode + mov esi, [eax+SLOT_BASE+APPDATA.saved_esp0] + mov ecx, [esi+4] + mov [eax+SLOT_BASE+APPDATA.process], ecx +; ...and I/O permission map for V86 mode + mov ecx, [esi+12] + mov [eax+SLOT_BASE+APPDATA.io_map], ecx + mov ecx, [esi+8] + mov [eax+SLOT_BASE+APPDATA.io_map+4], ecx +.nov86: + + mov esi, [.slot] + shl esi, 8 + add esi, SLOT_BASE+APP_OBJ_OFFSET +@@: + mov eax, [esi+APPOBJ.fd] + test eax, eax + jz @F + + cmp eax, esi + je @F + + push esi + call [eax+APPOBJ.destroy] + DEBUGF 1,"%s",msg_obj_destroy + pop esi + jmp @B +@@: + + mov eax, [.slot] + shl eax, 8 + ; stdcall destroy_app_space, [SLOT_BASE+eax+APPDATA.process], [SLOT_BASE+eax+APPDATA.dlls_list_ptr] + + mov esi, [.slot] + cmp [fpu_owner], esi ; if user fpu last -> fpu user = 2 + jne @F + + mov [fpu_owner], 2 + mov eax, [256*2+SLOT_BASE+APPDATA.fpu_state] + clts + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + fxrstor [eax] + jmp @F +.no_SSE: + fnclex + frstor [eax] +@@: + + mov [KEY_COUNT], byte 0 ; empty keyboard buffer + mov [BTN_COUNT], byte 0 ; empty button buffer + + +; remove defined hotkeys + mov eax, hotkey_list +.loop: + cmp [eax+8], esi + jnz .cont + mov ecx, [eax] + jecxz @f + push dword [eax+12] + pop dword [ecx+12] +@@: + mov ecx, [eax+12] + push dword [eax] + pop dword [ecx] + xor ecx, ecx + mov [eax], ecx + mov [eax+4], ecx + mov [eax+8], ecx + mov [eax+12], ecx +.cont: + add eax, 16 + cmp eax, hotkey_list+256*16 + jb .loop +; get process PID + mov eax, esi + shl eax, 5 + mov eax, [eax+CURRENT_TASK+TASKDATA.pid] +; compare current lock input with process PID + cmp eax, [PID_lock_input] + jne @f + + xor eax, eax + mov [PID_lock_input], eax +@@: +; remove hotkeys in buffer + mov eax, hotkey_buffer +.loop2: + cmp [eax], esi + jnz .cont2 + and dword [eax+4], 0 + and dword [eax], 0 +.cont2: + add eax, 8 + cmp eax, hotkey_buffer+120*8 + jb .loop2 + + mov ecx, esi ; remove buttons + bnewba2: + mov edi, [BTN_ADDR] + mov eax, edi + cld + movzx ebx, word [edi] + inc bx + bnewba: + dec bx + jz bnmba + add eax, 0x10 + cmp cx, [eax] + jnz bnewba + pusha + mov ecx, ebx + inc ecx + shl ecx, 4 + mov ebx, eax + add eax, 0x10 + call memmove + dec dword [edi] + popa + jmp bnewba2 + bnmba: + + pusha ; save window coordinates for window restoring + cld + shl esi, 5 + add esi, window_data + mov eax, [esi+WDATA.box.left] + mov [draw_limits.left], eax + add eax, [esi+WDATA.box.width] + mov [draw_limits.right], eax + mov eax, [esi+WDATA.box.top] + mov [draw_limits.top], eax + add eax, [esi+WDATA.box.height] + mov [draw_limits.bottom], eax + + xor eax, eax + mov [esi+WDATA.box.left], eax + mov [esi+WDATA.box.width], eax + mov [esi+WDATA.box.top], eax + mov [esi+WDATA.box.height], eax + mov [esi+WDATA.cl_workarea], eax + mov [esi+WDATA.cl_titlebar], eax + mov [esi+WDATA.cl_frames], eax + mov dword [esi+WDATA.reserved], eax; clear all flags: wstate, redraw, wdrawn + lea edi, [esi-window_data+draw_data] + mov ecx, 32/4 + rep stosd + popa + +; debuggee test + pushad + mov edi, esi + shl edi, 5 + mov eax, [SLOT_BASE+edi*8+APPDATA.debugger_slot] + test eax, eax + jz .nodebug + movi ecx, 8 + push dword [CURRENT_TASK+edi+TASKDATA.pid]; PID + push 2 + call debugger_notify + pop ecx + pop ecx +.nodebug: + popad + + mov ebx, [.slot] + shl ebx, 8 + push ebx + mov ebx, [SLOT_BASE+ebx+APPDATA.pl0_stack] + + stdcall kernel_free, ebx + + pop ebx + mov ebx, [SLOT_BASE+ebx+APPDATA.cur_dir] + stdcall kernel_free, ebx + + mov edi, [.slot] + shl edi, 8 + add edi, SLOT_BASE + + mov eax, [edi+APPDATA.io_map] + cmp eax, [SLOT_BASE+256+APPDATA.io_map] + je @F + call free_page +@@: + mov eax, [edi+APPDATA.io_map+4] + cmp eax, [SLOT_BASE+256+APPDATA.io_map+4] + je @F + call free_page +@@: + mov eax, 0x20202020 + stosd + stosd + stosd + mov ecx, 244/4 + xor eax, eax + rep stosd + + ; activate window + movzx eax, word [WIN_STACK + esi*2] + cmp eax, [TASK_COUNT] + jne .dont_activate + pushad + .check_next_window: + dec eax + cmp eax, 1 + jbe .nothing_to_activate + lea esi, [WIN_POS+eax*2] + movzx edi, word [esi] ; edi = process + shl edi, 5 + cmp [CURRENT_TASK + edi + TASKDATA.state], byte 9 ; skip dead slots + je .check_next_window + add edi, window_data +; \begin{diamond}[19.09.2006] +; skip minimized windows + test [edi + WDATA.fl_wstate], WSTATE_MINIMIZED + jnz .check_next_window +; \end{diamond} + call waredraw + .nothing_to_activate: + popad + .dont_activate: + + push esi ; remove hd1 & cd & flp reservation + shl esi, 5 + mov esi, [esi+CURRENT_TASK+TASKDATA.pid] + cmp [cd_status], esi + jnz @f + call free_cd_channel + and [cd_status], 0 +@@: + pop esi + cmp [bgrlockpid], esi + jnz @f + and [bgrlockpid], 0 + and [bgrlock], 0 +@@: + + pusha ; remove all port reservations + mov edx, esi + shl edx, 5 + add edx, CURRENT_TASK + mov edx, [edx+TASKDATA.pid] + + rmpr0: + + mov esi, [RESERVED_PORTS] + + test esi, esi + jz rmpr9 + + rmpr3: + + mov edi, esi + shl edi, 4 + add edi, RESERVED_PORTS + + cmp edx, [edi] + je rmpr4 + + dec esi + jnz rmpr3 + + jmp rmpr9 + + rmpr4: + + mov ecx, 256 + sub ecx, esi + shl ecx, 4 + + mov esi, edi + add esi, 16 + cld + rep movsb + + dec dword [RESERVED_PORTS] + + jmp rmpr0 + + rmpr9: + + popa + mov edi, esi ; do not run this process slot + shl edi, 5 + mov [edi+CURRENT_TASK + TASKDATA.state], byte 9 +; debugger test - terminate all debuggees + mov eax, 2 + mov ecx, SLOT_BASE+2*0x100+APPDATA.debugger_slot +.xd0: + cmp eax, [TASK_COUNT] + ja .xd1 + cmp dword [ecx], esi + jnz @f + and dword [ecx], 0 + pushad + xchg eax, ecx + mov ebx, 2 + call sys_system + popad +@@: + inc eax + add ecx, 0x100 + jmp .xd0 +.xd1: +; call systest + sti ; .. and life goes on + + mov eax, [draw_limits.left] + mov ebx, [draw_limits.top] + mov ecx, [draw_limits.right] + mov edx, [draw_limits.bottom] + call calculatescreen + xor eax, eax + xor esi, esi + call redrawscreen + + call unlock_application_table + ;mov esi,process_terminated + ;call sys_msg_board_str + add esp, 4 + ret +restore .slot + +;build_scheduler: +; mov esi, boot_sched_1 +; call boot_log +; call build_process_gdt_tss_pointer + +; mov esi,boot_sched_2 +; call boot_log +; ret + +; Three following procedures are used to guarantee that +; some part of kernel code will not be terminated from outside +; while it is running. +; Note: they do not protect a thread from terminating due to errors inside +; the thread; accessing a nonexisting memory would still terminate it. + +; First two procedures must be used in pair by thread-to-be-protected +; to signal the beginning and the end of an important part. +; It is OK to have nested areas. + +; The last procedure must be used by outside wanna-be-terminators; +; if it is safe to terminate the given thread immediately, it returns eax=1; +; otherwise, it returns eax=0 and notifies the target thread that it should +; terminate itself when leaving a critical area (the last critical area if +; they are nested). + +; Implementation. Those procedures use one dword in APPDATA for the thread, +; APPDATA.terminate_protection. +; * The upper bit is 1 during normal operations and 0 when terminate is requested. +; * Other bits form a number = depth of critical regions, +; plus 1 if the upper bit is 1. +; * When this dword goes to zero, the thread should be destructed, +; and the procedure in which it happened becomes responsible for destruction. + +; Enter critical area. Called by thread which wants to be protected. +proc protect_from_terminate + mov edx, [current_slot] +; Atomically increment depth of critical areas and get the old value. + mov eax, 1 + lock xadd [edx+APPDATA.terminate_protection], eax +; If the old value was zero, somebody has started to terminate us, +; so we are destructing and cannot do anything protected. +; Otherwise, return to the caller. + test eax, eax + jz @f + ret +@@: +; Wait for somebody to finish us. + call change_task + jmp @b +endp + +; Leave critical area. Called by thread which wants to be protected. +proc unprotect_from_terminate + mov edx, [current_slot] +; Atomically decrement depth of critical areas. + lock dec [edx+APPDATA.terminate_protection] +; If the result of decrement is zero, somebody has requested termination, +; but at that moment we were inside a critical area; terminate now. + jz sys_end +; Otherwise, return to the caller. + ret +endp + +; Request termination of thread identified by edx = SLOT_BASE + slot*256. +; Called by anyone. +proc request_terminate + xor eax, eax ; set return value +; Atomically clear the upper bit. If it was already zero, then +; somebody has requested termination before us, so just exit. + lock btr [edx+APPDATA.terminate_protection], 31 + jnc .unsafe +; Atomically decrement depth of critical areas. + lock dec [edx+APPDATA.terminate_protection] +; If the result of decrement is nonzero, the target thread is inside a +; critical area; leave termination to leaving that area. + jnz .unsafe +; Otherwise, it is safe to kill the target now and the caller is responsible +; for this. Return eax=1. + inc eax +.unsafe: + ret +endp + diff --git a/kernel/branches/kolibri-process/core/syscall.inc b/kernel/branches/kolibri-process/core/syscall.inc new file mode 100644 index 000000000..941991b6d --- /dev/null +++ b/kernel/branches/kolibri-process/core/syscall.inc @@ -0,0 +1,181 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSENTER ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +align 32 +sysenter_entry: + ; Настраиваем стек + mov esp, [ss:tss._esp0] + sti + push ebp ; save app esp + 4 + mov ebp, [ebp] ; ebp - original ebp + ;------------------ + pushad + cld + + call protect_from_terminate + + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + + call unprotect_from_terminate + popad + ;------------------ + xchg ecx, [ss:esp] ; в вершин стека - app ecx, ecx - app esp + 4 + sub ecx, 4 + xchg edx, [ecx] ; edx - return point, & save original edx + push edx + mov edx, [ss:esp + 4] + mov [ecx + 4], edx ; save original ecx + pop edx + sysexit + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSTEM CALL ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +align 16 +i40: + pushad + cld + call protect_from_terminate + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + call unprotect_from_terminate + popad + iretd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSCALL ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +align 32 +syscall_entry: + ; cli syscall clear IF + xchg esp, [ss:tss._esp0] + push ecx + lea ecx, [esp+4] + xchg ecx, [ss:tss._esp0] + sti + push ecx + mov ecx, [ecx] + ;------------------ + pushad + cld + call protect_from_terminate + + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + + call unprotect_from_terminate + popad + ;------------------ + mov ecx, [ss:esp+4] + pop esp + sysret + +iglobal + ;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; SYSTEM FUNCTIONS TABLE ;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;; + align 4 + servetable2: + + dd syscall_draw_window ; 0-DrawWindow + dd syscall_setpixel ; 1-SetPixel + dd sys_getkey ; 2-GetKey + dd sys_clock ; 3-GetTime + dd syscall_writetext ; 4-WriteText + dd delay_hs_unprotected ; 5-DelayHs + dd undefined_syscall ; 6-deprecated OpenRamdiskFile + dd syscall_putimage ; 7-PutImage + dd syscall_button ; 8-DefineButton + dd sys_cpuusage ; 9-GetProcessInfo + dd sys_waitforevent ; 10-WaitForEvent + dd sys_getevent ; 11-CheckForEvent + dd sys_redrawstat ; 12-BeginDraw and EndDraw + dd syscall_drawrect ; 13-DrawRect + dd syscall_getscreensize ; 14-GetScreenSize + dd sys_background ; 15-bgr + dd sys_cachetodiskette ; 16-FlushFloppyCache + dd sys_getbutton ; 17-GetButton + dd sys_system ; 18-System Services + dd paleholder ; 19-reserved + dd sys_midi ; 20-ResetMidi and OutputMidi + dd sys_setup ; 21-SetMidiBase,SetKeymap,SetShiftKeymap,. + dd sys_settime ; 22-setting date,time,clock and alarm-clock + dd sys_wait_event_timeout ; 23-TimeOutWaitForEvent + dd syscall_cdaudio ; 24-PlayCdTrack,StopCd and GetCdPlaylist + dd syscall_putarea_backgr ; 25-Put Area to background + dd sys_getsetup ; 26-GetMidiBase,GetKeymap,GetShiftKeymap,. + dd undefined_syscall ; 27-reserved + dd undefined_syscall ; 28-reserved + dd sys_date ; 29-GetDate + dd sys_current_directory ; 30-Get/SetCurrentDirectory + dd undefined_syscall ; 31-reserved + dd undefined_syscall ; 32-reserved + dd undefined_syscall ; 33-reserved + dd syscall_getpixel_WinMap ; 34-GetPixel WinMap + dd syscall_getpixel ; 35-GetPixel + dd syscall_getarea ; 36-GetArea + dd readmousepos ; 37-GetMousePosition_ScreenRelative,. + dd syscall_drawline ; 38-DrawLine + dd sys_getbackground ; 39-GetBackgroundSize,ReadBgrData,. + dd set_app_param ; 40-WantEvents + dd undefined_syscall ; 41- deprecated GetIrqOwner + dd undefined_syscall ; 42- deprecated ReadIrqData + dd sys_outport ; 43-SendDeviceData + dd undefined_syscall ; 44- deprecated ProgramIrqs + dd undefined_syscall ; 45- deprecated ReserveIrq and FreeIrq + dd syscall_reserveportarea ; 46-ReservePortArea and FreePortArea + dd display_number ; 47-WriteNum + dd syscall_display_settings ; 48-SetRedrawType and SetButtonType + dd sys_apm ; 49-Advanced Power Management (APM) + dd syscall_set_window_shape ; 50-Window shape & scale + dd syscall_threads ; 51-Threads + dd undefined_syscall ; 52- deprecated Stack driver status + dd undefined_syscall ; 53- deprecated Socket interface + dd sys_clipboard ; 54-Custom clipboard + dd sound_interface ; 55-Sound interface + dd undefined_syscall ; 56-reserved + dd sys_pcibios ; 57-PCI BIOS32 + dd undefined_syscall ; 58-deprecated Common file system interface + dd undefined_syscall ; 59-reserved + dd sys_IPC ; 60-Inter Process Communication + dd sys_gs ; 61-Direct graphics access + dd pci_api ; 62-PCI functions + dd sys_msg_board ; 63-System message board + dd sys_resize_app_memory ; 64-Resize application memory usage + dd sys_putimage_palette ; 65-PutImagePalette + dd sys_process_def ; 66-Process definitions - keyboard + dd syscall_move_window ; 67-Window move or resize + dd f68 ; 68-Some internal services + dd sys_debug_services ; 69-Debug + dd file_system_lfn ; 70-Common file system interface, version 2 + dd syscall_window_settings ; 71-Window settings + dd sys_sendwindowmsg ; 72-Send window message + dd blit_32 ; 73-blitter; + 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 + +endg diff --git a/kernel/branches/kolibri-process/core/taskman.inc b/kernel/branches/kolibri-process/core/taskman.inc new file mode 100644 index 000000000..03a321ce2 --- /dev/null +++ b/kernel/branches/kolibri-process/core/taskman.inc @@ -0,0 +1,1257 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4424 $ + + +GREEDY_KERNEL equ 0 + +struct APP_HEADER_00_ + banner dq ? + version dd ? ;+8 + start dd ? ;+12 + i_end dd ? ;+16 + mem_size dd ? ;+20 + i_param dd ? ;+24 +ends + +struct APP_HEADER_01_ + banner dq ? + version dd ? ;+8 + start dd ? ;+12 + i_end dd ? ;+16 + mem_size dd ? ;+20 + stack_top dd ? ;+24 + i_param dd ? ;+28 + i_icon dd ? ;+32 +ends + + +struct APP_PARAMS + app_cmdline dd ? ;0x00 + app_path dd ? ;0x04 + app_eip dd ? ;0x08 + app_esp dd ? ;0x0C + app_mem dd ? ;0x10 +ends + +macro _clear_ op +{ mov ecx, op/4 + xor eax, eax + cld + rep stosd +} + +fs_execute_from_sysdir: + xor ebx, ebx +fs_execute_from_sysdir_param: + xor edx, edx + mov esi, sysdir_path + +align 4 +proc fs_execute + +;fn_read:dword, file_size:dword, cluster:dword + +; ebx - cmdline +; edx - flags +; ebp - full filename +; [esp+4] = procedure DoRead, [esp+8] = filesize & [esp+12]... - arguments for it + + locals + cmdline_size dd ? ; +0 ; cmdline -12 + cmdline_adr dd ? ; +4 ; cmdline -8 + cmdline_flag dd ? ; +8 ; cmdline -4 + cmdline rd 64 ;256/4 + filename rd 256 ;1024/4 + flags dd ? + + save_proc dd ? + slot dd ? + slot_base dd ? + file_base dd ? + file_size dd ? + handle dd ? ;temp. for default cursor handle for curr. thread + ;app header data + hdr_cmdline dd ? ;0x00 + hdr_path dd ? ;0x04 + hdr_eip dd ? ;0x08 + hdr_esp dd ? ;0x0C + hdr_mem dd ? ;0x10 + hdr_i_end dd ? ;0x14 + endl + + pushad + + cmp [SCR_MODE], word 0x13 + jbe @f + pushad + stdcall set_cursor, [def_cursor_clock] + mov [handle], eax + mov [redrawmouse_unconditional], 1 + call wakeup_osloop + popad +@@: + mov [flags], edx + +; [ebp] pointer to filename + + lea edi, [filename] + lea ecx, [edi+1024] + mov al, '/' + stosb +@@: + cmp edi, ecx + jae .bigfilename + lodsb + stosb + test al, al + jnz @b + mov esi, [ebp] + test esi, esi + jz .namecopied + mov byte [edi-1], '/' +@@: + cmp edi, ecx + jae .bigfilename + lodsb + stosb + test al, al + jnz @b + jmp .namecopied +.bigfilename: + popad + mov eax, -ERROR_FILE_NOT_FOUND + + jmp .final + +.namecopied: + xor eax, eax + mov [cmdline_flag], eax + mov [cmdline_adr], eax + mov [cmdline_size], eax + + mov [cmdline], ebx + test ebx, ebx + jz .no_copy +;-------------------------------------- + pushad + pushfd + mov esi, ebx + mov ecx, 65536 ; 64 Kb max for ext.cmdline + cld +@@: + dec ecx + jz .end_string + + lodsb + test al, al + jnz @b + +.end_string: + mov eax, 65536 ; 64 Kb max for ext.cmdline + sub eax, ecx + mov [cmdline_size], eax + cmp eax, 255 + ja @f + + popfd + popad + jmp .old_copy + +@@: + xor eax, eax + dec eax + mov [cmdline_flag], eax + popfd + popad +; get memory for the extended command line + stdcall kernel_alloc, [cmdline_size] ;eax + test eax, eax + jz .old_copy ; get memory failed + + mov [cmdline_adr], eax + + pushad + pushfd + mov esi, ebx + mov edi, eax + mov ecx, [cmdline_size] + cld + rep movsb + popfd + popad + jmp .no_copy + +.old_copy: +; clear flag because old method with 256 bytes + xor eax, eax + mov [cmdline_flag], eax +;-------------------------------------- + lea eax, [cmdline] + mov dword [eax+252], 0 +.copy: + stdcall strncpy, eax, ebx, 255 +.no_copy: + lea eax, [filename] + stdcall load_file, eax + + mov esi, -ERROR_FILE_NOT_FOUND + test eax, eax + jz .err_file + + mov [file_base], eax + mov [file_size], ebx + + lea ebx, [hdr_cmdline] + call test_app_header + mov esi, -0x1F + test eax, eax + jz .err_hdr + + call lock_application_table + + call alloc_thread_slot + test eax, eax + mov esi, -0x20 ; too many processes + jz .err + + mov [slot], eax + shl eax, 8 + add eax, SLOT_BASE + mov [slot_base], eax + mov edi, eax + _clear_ 256 ;clean extended information about process + +; write application name + lea eax, [filename] + stdcall strrchr, eax, '/' ; now eax points to name without path + + lea esi, [eax+1] + test eax, eax + jnz @F + lea esi, [filename] +@@: + mov ecx, 11 ; 11 chars for name! 8 - is old value! + mov edi, [slot_base] +.copy_process_name_loop: + lodsb + cmp al, '.' + jz .copy_process_name_done + test al, al + jz .copy_process_name_done + stosb + loop .copy_process_name_loop +.copy_process_name_done: + + mov ebx, [current_slot] + mov ebx, [ebx+APPDATA.process] + mov [save_proc], ebx + + stdcall create_process, [hdr_mem], [file_base], [file_size] + mov esi, -30; no memory + test eax, eax + jz .failed + + mov ebx, [slot_base] + mov [ebx+APPDATA.process], eax + mov eax, [hdr_mem] + mov [ebx+APPDATA.mem_size], eax + + xor edx, edx + cmp word [6], '02' + jne @f + + not edx +@@: + mov [ebx+APPDATA.tls_base], edx + +if GREEDY_KERNEL +else + mov ecx, [hdr_mem] + mov edi, [file_size] + add edi, 4095 + and edi, not 4095 + sub ecx, edi + jna @F + + xor eax, eax + cld + rep stosb +@@: +end if + +; release only virtual space, not phisical memory + + stdcall free_kernel_space, [file_base] + lea eax, [hdr_cmdline] + lea ebx, [cmdline] + lea ecx, [filename] + stdcall set_app_params , [slot], eax, ebx, ecx, [flags] + + mov eax, [save_proc] + call set_cr3 + + mov eax, [process_number];set result + call unlock_application_table + + jmp .final + +.failed: + mov eax, [save_proc] + call set_cr3 +.err: +.err_hdr: + stdcall kernel_free, [file_base] +.err_file: + call unlock_application_table + mov eax, esi +.final: + cmp [SCR_MODE], word 0x13 + jbe @f + pushad + stdcall set_cursor, [handle] + mov [redrawmouse_unconditional], 1 + call wakeup_osloop + popad +@@: + ret +endp + +align 4 +test_app_header: + virtual at eax + APP_HEADER_00 APP_HEADER_00_ + end virtual + virtual at eax + APP_HEADER_01 APP_HEADER_01_ + end virtual + + cmp dword [eax], 'MENU' + jne .fail + cmp word [eax+4], 'ET' + jne .fail + + cmp [eax+6], word '00' + jne .check_01_header + + mov ecx, [APP_HEADER_00.start] + mov [ebx+0x08], ecx ;app_eip + mov edx, [APP_HEADER_00.mem_size] + mov [ebx+0x10], edx ;app_mem + shr edx, 1 + sub edx, 0x10 + mov [ebx+0x0C], edx ;app_esp + mov ecx, [APP_HEADER_00.i_param] + mov [ebx], ecx ;app_cmdline + mov [ebx+4], dword 0 ;app_path + mov edx, [APP_HEADER_00.i_end] + mov [ebx+0x14], edx + ret + + .check_01_header: + + cmp [eax+6], word '01' + je @f + cmp [eax+6], word '02' + jne .fail +@@: + mov ecx, [APP_HEADER_01.start] + mov [ebx+0x08], ecx ;app_eip + mov edx, [APP_HEADER_01.mem_size] + +; \begin{diamond}[20.08.2006] +; sanity check (functions 19,58 load app_i_end bytes and that must +; fit in allocated memory to prevent kernel faults) + cmp edx, [APP_HEADER_01.i_end] + jb .fail +; \end{diamond}[20.08.2006] + + mov [ebx+0x10], edx ;app_mem + mov ecx, [APP_HEADER_01.stack_top] + mov [ebx+0x0C], ecx ;app_esp + mov edx, [APP_HEADER_01.i_param] + mov [ebx], edx ;app_cmdline + mov ecx, [APP_HEADER_01.i_icon] + mov [ebx+4], ecx ;app_path + mov edx, [APP_HEADER_01.i_end] + mov [ebx+0x14], edx + ret +.fail: + xor eax, eax + ret + + +align 4 +alloc_thread_slot: +;input: +; none +;result: +; eax=[new_thread_slot]<>0 - ok +; 0 - failed. +;This function find least empty slot. +;It doesn't increase [TASK_COUNT]! + + + mov edx, thr_slot_map + pushfd + cli +.l1: + bsf eax, [edx] + jnz .found + add edx, 4 + cmp edx, thr_slot_map+32 + jb .l1 + + popfd + xor eax, eax + ret +.found: + btr [edx], eax + sub edx, thr_slot_map + lea eax, [eax+edx*8] + popfd + ret + + +align 4 +proc create_process stdcall, app_size:dword,img_base:dword,img_size:dword + locals + app_pages dd ? + img_pages dd ? + process dd ? + app_tabs dd ? + endl + + mov ecx, pg_data.mutex + call mutex_lock + + xchg bx, bx + + xor eax, eax + mov [process], eax + + mov eax, [app_size] + add eax, 4095 + and eax, NOT(4095) + mov [app_size], eax + mov ebx, eax + shr eax, 12 + mov [app_pages], eax + + add ebx, 0x3FFFFF + and ebx, NOT(0x3FFFFF) + shr ebx, 22 + mov [app_tabs], ebx + + mov ecx, [img_size] + add ecx, 4095 + and ecx, NOT(4095) + + mov [img_size], ecx + shr ecx, 12 + mov [img_pages], ecx + + lea eax, [eax+ebx+2];all requested memory + + cmp eax, [pg_data.pages_free] + ja .fail + + stdcall kernel_alloc, 0x2000 + test eax, eax + jz .fail + mov [process], eax + + lea edi, [eax+PROC.heap_lock] + mov ecx, (4096-PROC.heap_lock)/4 + + list_init eax + add eax, PROC.thr_list + list_init eax + + xor eax, eax + cld + rep stosd + + mov eax, edi + call get_pg_addr + mov [edi-4096+PROC.pdt_0_phys], eax + + mov ecx, (OS_BASE shr 20)/4 + xor eax, eax + rep stosd + + mov ecx, (OS_BASE shr 20)/4 + mov esi, sys_proc+PROC.pdt_0+(OS_BASE shr 20) + rep movsd + + mov eax, [edi-8192+PROC.pdt_0_phys] + or eax, PG_SW + mov [edi-4096+(page_tabs shr 20)], eax + + lea eax, [edi-8192] + call set_cr3 + + mov edx, [app_tabs] + xor edi, edi +@@: + call alloc_page + test eax, eax + jz .fail + + stdcall map_page_table, edi, eax + add edi, 0x00400000 + dec edx + jnz @B + + mov edi, page_tabs + + mov ecx, [app_tabs] + shl ecx, 10 + xor eax, eax + rep stosd + + mov ecx, [img_pages] + mov ebx, PG_UW + xor edx, edx + mov esi, [img_base] + shr esi, 10 + add esi, page_tabs + mov edi, page_tabs +.remap: + lodsd + and eax, 0xFFFFF000 + or eax, ebx; force user level r/w access + stosd + add edx, 0x1000 + dec [app_pages] + dec ecx + jnz .remap + + mov ecx, [app_pages] + test ecx, ecx + jz .done + +.alloc: + call alloc_page + test eax, eax + jz .fail + + stdcall map_page, edx, eax, dword PG_UW + add edx, 0x1000 + dec [app_pages] + jnz .alloc + +.done: + mov ecx, pg_data.mutex + call mutex_unlock + mov eax, [process] + ret +.fail: + mov ecx, pg_data.mutex + call mutex_unlock + cmp [process], 0 + je @f +;; stdcall destroy_app_space, [dir_addr], 0 +@@: + xor eax, eax + ret +endp + +align 4 +set_cr3: + + mov ebx, [current_slot] + mov [ebx+APPDATA.process], eax + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax + ret + +align 4 +proc destroy_page_table stdcall, pg_tab:dword + + push esi + + mov esi, [pg_tab] + mov ecx, 1024 +.free: + mov eax, [esi] + test eax, 1 + jz .next + test eax, 1 shl 9 + jnz .next ;skip shared pages + call free_page +.next: + add esi, 4 + dec ecx + jnz .free + pop esi + ret +endp + +align 4 +proc destroy_app_space stdcall, pg_dir:dword, dlls_list:dword + + xor edx, edx + push edx + mov eax, 0x1 + mov ebx, [pg_dir] +.loop: +;eax = current slot of process + mov ecx, eax + shl ecx, 5 + cmp byte [CURRENT_TASK+ecx+0xa], 9;if process running? + jz @f ;skip empty slots + shl ecx, 3 + add ecx, SLOT_BASE + cmp [ecx+APPDATA.process], ebx;compare page directory addresses + jnz @f + mov [ebp-4], ecx + inc edx ;thread found +@@: + inc eax + cmp eax, [TASK_COUNT] ;exit loop if we look through all processes + jle .loop + +;edx = number of threads +;our process is zombi so it isn't counted + pop ecx + cmp edx, 1 + jg .ret +;if there isn't threads then clear memory. + mov esi, [dlls_list] + call destroy_all_hdlls;ecx=APPDATA + + mov ecx, pg_data.mutex + call mutex_lock + + mov eax, [pg_dir] + and eax, not 0xFFF +; stdcall map_page, [tmp_task_pdir], eax, PG_SW +; mov esi, [tmp_task_pdir] + mov edi, (OS_BASE shr 20)/4 +.destroy: + mov eax, [esi] + test eax, 1 + jz .next + and eax, not 0xFFF + stdcall map_page, [tmp_task_ptab], eax, PG_SW + stdcall destroy_page_table, [tmp_task_ptab] + mov eax, [esi] + call free_page +.next: + add esi, 4 + dec edi + jnz .destroy + + mov eax, [pg_dir] + call free_page +.exit: + stdcall map_page, [tmp_task_ptab], 0, PG_UNMAP +; stdcall map_page, [tmp_task_pdir], 0, PG_UNMAP + mov ecx, pg_data.mutex + call mutex_unlock +.ret: + ret +endp + +align 4 +get_pid: + mov eax, [TASK_BASE] + mov eax, [eax+TASKDATA.pid] + ret + +pid_to_slot: +;Input: +; eax - pid of process +;Output: +; eax - slot of process or 0 if process don't exists +;Search process by PID. + push ebx + push ecx + mov ebx, [TASK_COUNT] + shl ebx, 5 + mov ecx, 2*32 + +.loop: +;ecx=offset of current process info entry +;ebx=maximum permitted offset + cmp byte [CURRENT_TASK+ecx+0xa], 9 + jz .endloop ;skip empty slots + cmp [CURRENT_TASK+ecx+0x4], eax;check PID + jz .pid_found +.endloop: + add ecx, 32 + cmp ecx, ebx + jle .loop + + pop ecx + pop ebx + xor eax, eax + ret + +.pid_found: + shr ecx, 5 + mov eax, ecx ;convert offset to index of slot + pop ecx + pop ebx + ret + +check_region: +;input: +; esi - start of buffer +; edx - size of buffer +;result: +; eax = 1 region lays in app memory +; eax = 0 region don't lays in app memory + mov eax, [CURRENT_TASK] +; jmp check_process_region +;----------------------------------------------------------------------------- +;check_process_region: +;input: +; eax - slot +; esi - start of buffer +; edx - size of buffer +;result: +; eax = 1 region lays in app memory +; eax = 0 region don't lays in app memory + + test edx, edx + jle .ok + shl eax, 5 + cmp word [CURRENT_TASK+eax+0xa], 0 + jnz .failed + shl eax, 3 + mov eax, [SLOT_BASE+eax+0xb8] + test eax, eax + jz .failed + + mov eax, 1 + ret + + +; call MEM_Get_Linear_Address +; push ebx +; push ecx +; push edx +; mov edx,ebx +; and edx,not (4096-1) +; sub ebx,edx +; add ecx,ebx +; mov ebx,edx +; add ecx,(4096-1) +; and ecx,not (4096-1) +;.loop: +;;eax - linear address of page directory +;;ebx - current page +;;ecx - current size +; mov edx,ebx +; shr edx,22 +; mov edx,[eax+4*edx] +; and edx,not (4096-1) +; test edx,edx +; jz .failed1 +; push eax +; mov eax,edx +; call MEM_Get_Linear_Address +; mov edx,ebx +; shr edx,12 +; and edx,(1024-1) +; mov eax,[eax+4*edx] +; and eax,not (4096-1) +; test eax,eax +; pop eax +; jz .failed1 +; add ebx,4096 +; sub ecx,4096 +; jg .loop +; pop edx +; pop ecx +; pop ebx +.ok: + mov eax, 1 + ret +; +;.failed1: +; pop edx +; pop ecx +; pop ebx +.failed: + xor eax, eax + ret + +align 4 +proc read_process_memory +;Input: +; eax - process slot +; ecx - buffer address +; edx - buffer size +; esi - start address in other process +;Output: +; eax - number of bytes read. + locals + slot dd ? + buff dd ? + r_count dd ? + offset dd ? + tmp_r_cnt dd ? + endl + + mov [slot], eax + mov [buff], ecx + and [r_count], 0 + mov [tmp_r_cnt], edx + mov [offset], esi + + pushad +.read_mem: + mov edx, [offset] + mov ebx, [tmp_r_cnt] + + mov ecx, 0x400000 + and edx, 0x3FFFFF + sub ecx, edx + cmp ecx, ebx + jbe @f + mov ecx, ebx +@@: + cmp ecx, 0x8000 + jna @F + mov ecx, 0x8000 +@@: + mov ebx, [offset] + + push ecx + stdcall map_memEx, [proc_mem_map], \ + [slot], ebx, ecx, PG_MAP + pop ecx + + mov esi, [offset] + and esi, 0xfff + sub eax, esi + jbe .ret + cmp ecx, eax + jbe @f + mov ecx, eax + mov [tmp_r_cnt], eax +@@: + add esi, [proc_mem_map] + mov edi, [buff] + mov edx, ecx + rep movsb + add [r_count], edx + + add [offset], edx + sub [tmp_r_cnt], edx + jnz .read_mem +.ret: + popad + mov eax, [r_count] + ret +endp + +align 4 +proc write_process_memory +;Input: +; eax - process slot +; ecx - buffer address +; edx - buffer size +; esi - start address in other process +;Output: +; eax - number of bytes written + + locals + slot dd ? + buff dd ? + w_count dd ? + offset dd ? + tmp_w_cnt dd ? + endl + + mov [slot], eax + mov [buff], ecx + and [w_count], 0 + mov [tmp_w_cnt], edx + mov [offset], esi + + pushad +.read_mem: + mov edx, [offset] + mov ebx, [tmp_w_cnt] + + mov ecx, 0x400000 + and edx, 0x3FFFFF + sub ecx, edx + cmp ecx, ebx + jbe @f + mov ecx, ebx +@@: + cmp ecx, 0x8000 + jna @F + mov ecx, 0x8000 +@@: + mov ebx, [offset] + ; add ebx, new_app_base + push ecx + stdcall map_memEx, [proc_mem_map], \ + [slot], ebx, ecx, PG_SW + pop ecx + + mov edi, [offset] + and edi, 0xfff + sub eax, edi + jbe .ret + cmp ecx, eax + jbe @f + mov ecx, eax + mov [tmp_w_cnt], eax +@@: + add edi, [proc_mem_map] + mov esi, [buff] + mov edx, ecx + rep movsb + + add [w_count], edx + add [offset], edx + sub [tmp_w_cnt], edx + jnz .read_mem +.ret: + popad + mov eax, [w_count] + ret +endp + +;ebx = 1 - kernel thread +;ecx=thread entry point +;edx=thread stack pointer +;creation flags 0x01 - debugged +; 0x02 - kernel + +align 4 +proc new_sys_threads + locals + slot dd ? + flags dd ? + app_cmdline dd ? ;0x00 + app_path dd ? ;0x04 + app_eip dd ? ;0x08 + app_esp dd ? ;0x0C + app_mem dd ? ;0x10 + endl + + shl ebx, 1 + mov [flags], ebx + + xor eax, eax + mov [app_eip], ecx + mov [app_cmdline], eax + mov [app_esp], edx + mov [app_path], eax + + call lock_application_table + + call alloc_thread_slot + test eax, eax + jz .failed + + mov [slot], eax + + mov esi, [current_slot] + mov ebx, esi ;ebx=esi - pointer to extended information about current thread + + mov edi, eax + shl edi, 8 + add edi, SLOT_BASE + mov edx, edi ;edx=edi - pointer to extended infomation about new thread + mov ecx, 256/4 + xor eax, eax + cld + rep stosd ;clean extended information about new thread + mov esi, ebx + mov edi, edx + mov ecx, 11 + rep movsb ;copy process name + + mov eax, [ebx+APPDATA.heap_base] + mov [edx+APPDATA.heap_base], eax + + mov ecx, [ebx+APPDATA.heap_top] + mov [edx+APPDATA.heap_top], ecx + + mov eax, [ebx+APPDATA.mem_size] + mov [edx+APPDATA.mem_size], eax + + mov ecx, [ebx+APPDATA.process] + mov [edx+APPDATA.process], ecx;copy page directory + + mov eax, [ebx+APPDATA.dlls_list_ptr] + mov [edx+APPDATA.dlls_list_ptr], eax + + mov eax, [ebx+APPDATA.tls_base] + test eax, eax + jz @F + + push edx + stdcall user_alloc, 4096 + pop edx + test eax, eax + jz .failed1;eax=0 +@@: + mov [edx+APPDATA.tls_base], eax + + lea eax, [app_cmdline] + stdcall set_app_params , [slot], eax, dword 0, \ + dword 0, [flags] + + mov eax, [process_number] ;set result + call unlock_application_table + ret +.failed: + xor eax, eax +.failed1: + call unlock_application_table + dec eax ;-1 + ret +endp + +align 4 +tls_app_entry: + + call init_heap + stdcall user_alloc, 4096 + + mov edx, [current_slot] + mov [edx+APPDATA.tls_base], eax + mov [tls_data_l+2], ax + shr eax, 16 + mov [tls_data_l+4], al + mov [tls_data_l+7], ah + mov dx, app_tls + mov fs, dx + popad + iretd + + +EFL_IF equ 0x0200 +EFL_IOPL1 equ 0x1000 +EFL_IOPL2 equ 0x2000 +EFL_IOPL3 equ 0x3000 + + +align 4 +proc set_app_params stdcall,slot:dword, params:dword,\ + cmd_line:dword, app_path:dword, flags:dword + + locals + pl0_stack dd ? + endl + + stdcall kernel_alloc, RING0_STACK_SIZE+512 + mov [pl0_stack], eax + + lea edi, [eax+RING0_STACK_SIZE] + + mov eax, [slot] + mov ebx, eax + + shl eax, 8 + mov [eax+SLOT_BASE+APPDATA.fpu_state], edi + mov [eax+SLOT_BASE+APPDATA.exc_handler], 0 + mov [eax+SLOT_BASE+APPDATA.except_mask], 0 + mov [eax+SLOT_BASE+APPDATA.terminate_protection], 80000001h + +;set default io permission map + mov ecx, [SLOT_BASE+256+APPDATA.io_map] + mov [eax+SLOT_BASE+APPDATA.io_map], ecx + mov ecx, [SLOT_BASE+256+APPDATA.io_map+4] + mov [eax+SLOT_BASE+APPDATA.io_map+4], ecx + + mov esi, fpu_data + mov ecx, 512/4 + rep movsd + + cmp ebx, [TASK_COUNT] + jle .noinc + inc dword [TASK_COUNT] ;update number of processes +.noinc: + shl ebx, 8 + lea edx, [ebx+SLOT_BASE+APP_EV_OFFSET] + mov [SLOT_BASE+APPDATA.fd_ev+ebx], edx + mov [SLOT_BASE+APPDATA.bk_ev+ebx], edx + + add edx, APP_OBJ_OFFSET-APP_EV_OFFSET + mov [SLOT_BASE+APPDATA.fd_obj+ebx], edx + mov [SLOT_BASE+APPDATA.bk_obj+ebx], edx + + mov ecx, [def_cursor] + mov [SLOT_BASE+APPDATA.cursor+ebx], ecx + mov eax, [pl0_stack] + mov [SLOT_BASE+APPDATA.pl0_stack+ebx], eax + add eax, RING0_STACK_SIZE + mov [SLOT_BASE+APPDATA.saved_esp0+ebx], eax + + push ebx + stdcall kernel_alloc, 0x1000 + pop ebx + mov esi, [current_slot] + mov esi, [esi+APPDATA.cur_dir] + mov ecx, 0x1000/4 + mov edi, eax + mov [ebx+SLOT_BASE+APPDATA.cur_dir], eax + rep movsd + + shr ebx, 3 + mov eax, new_app_base + mov dword [CURRENT_TASK+ebx+0x10], eax + +.add_command_line: + mov edx, [params] + mov edx, [edx] ;app_cmdline + test edx, edx + jz @f ;application doesn't need parameters + + mov eax, edx + add eax, 256 + jc @f + + cmp eax, [SLOT_BASE+APPDATA.mem_size+ebx*8] + ja @f + + mov eax, [cmd_line] + + cmp [edx], dword 0xffffffff ; extended destination tag + jne .no_ext_dest + + mov edx, [edx+4] ; extended destination for cmdline + jmp .continue + +.no_ext_dest: + mov [eax-12], dword 255 +.continue: + mov byte [edx], 0 ;force empty string if no cmdline given + + test eax, eax + jz @f +;-------------------------------------- + cmp [eax-4], dword 0xffffffff ; cmdline_flag + jne .old_copy + + push eax + stdcall strncpy, edx, [eax-8], [eax-12] + pop eax + + stdcall kernel_free, [eax-8] + jmp @f + +.old_copy: +;-------------------------------------- + stdcall strncpy, edx, eax, 256 +@@: + mov edx, [params] + mov edx, [edx+4];app_path + test edx, edx + jz @F ;application don't need path of file + mov eax, edx + add eax, 1024 + jc @f + cmp eax, [SLOT_BASE+APPDATA.mem_size+ebx*8] + ja @f + stdcall strncpy, edx, [app_path], 1024 +@@: + mov ebx, [slot] + mov eax, ebx + shl ebx, 5 + lea ecx, [draw_data+ebx];ecx - pointer to draw data + + mov edx, irq0.return + cmp [ebx*8+SLOT_BASE+APPDATA.tls_base], -1 + jne @F + mov edx, tls_app_entry +@@: +; set window state to 'normal' (non-minimized/maximized/rolled-up) state + mov [ebx+window_data+WDATA.fl_wstate], WSTATE_NORMAL + mov [ebx+window_data+WDATA.fl_redraw], 1 + add ebx, CURRENT_TASK ;ebx - pointer to information about process + mov [ebx+TASKDATA.wnd_number], al;set window number on screen = process slot + + mov [ebx+TASKDATA.event_mask], dword 1+2+4;set default event flags (see 40 function) + + inc dword [process_number] + mov eax, [process_number] + mov [ebx+4], eax ;set PID + +;set draw data to full screen + xor eax, eax + mov [ecx+0], dword eax + mov [ecx+4], dword eax + mov eax, [Screen_Max_X] + mov [ecx+8], eax + mov eax, [Screen_Max_Y] + mov [ecx+12], eax + + mov ebx, [pl0_stack] + mov esi, [params] + lea ecx, [ebx+REG_EIP] + xor eax, eax + + mov [ebx+REG_RET], edx + mov [ebx+REG_EDI], eax + mov [ebx+REG_ESI], eax + mov [ebx+REG_EBP], eax + mov [ebx+REG_ESP], ecx;ebx+REG_EIP + mov [ebx+REG_EBX], eax + mov [ebx+REG_EDX], eax + mov [ebx+REG_ECX], eax + mov [ebx+REG_EAX], eax + + mov eax, [esi+0x08] ;app_eip + mov [ebx+REG_EIP], eax ;app_entry + mov [ebx+REG_CS], dword app_code + mov ecx, USER_PRIORITY + + test byte [flags], 2 + jz @F + + mov [ebx+REG_CS], dword os_code ; kernel thread + mov ecx, MAX_PRIORITY + +@@: + mov [ebx+REG_EFLAGS], dword EFL_IOPL1+EFL_IF + + mov eax, [esi+0x0C] ;app_esp + mov [ebx+REG_APP_ESP], eax;app_stack + mov [ebx+REG_SS], dword app_data + + lea edx, [ebx+REG_RET] + mov ebx, [slot] + shl ebx, 5 + mov [ebx*8+SLOT_BASE+APPDATA.saved_esp], edx + + xor edx, edx; process state - running +; set if debuggee + test byte [flags], 1 + jz .no_debug + 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], dl + lea edx, [SLOT_BASE+ebx*8] + call scheduler_add_thread + ret +endp + + +align 4 + +get_stack_base: + mov eax, [current_slot] + mov eax, [eax+APPDATA.pl0_stack] + ret + + +include "debug.inc" diff --git a/kernel/branches/kolibri-process/core/timers.inc b/kernel/branches/kolibri-process/core/timers.inc new file mode 100644 index 000000000..b343b8c7a --- /dev/null +++ b/kernel/branches/kolibri-process/core/timers.inc @@ -0,0 +1,229 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3598 $ + +; Simple implementation of timers. All timers are organized in a double-linked +; list, and the OS loop after every timer tick processes the list. + +; This structure describes a timer for the kernel. +struct TIMER + Next dd ? + Prev dd ? +; These fields organize a double-linked list of all timers. + TimerFunc dd ? +; Function to be called when the timer is activated. + UserData dd ? +; The value that is passed as is to .TimerFunc. + Time dd ? +; Time at which the timer should be activated. + Interval dd ? +; Interval between activations of the timer, in 0.01s. +ends + +iglobal +align 4 +; The head of timer list. +timer_list: + dd timer_list + dd timer_list +endg +uglobal +; These two variables are used to synchronize access to the global list. +; Logically, they form an recursive mutex. Physically, the first variable holds +; the slot number of the current owner or 0, the second variable holds the +; recursion count. +; The mutex should be recursive to allow a timer function to add/delete other +; timers or itself. +timer_list_owner dd 0 +timer_list_numlocks dd 0 +; A timer function can delete any timer, including itself and the next timer in +; the chain. To handle such situation correctly, we keep the next timer in a +; global variable, so the removing operation can update it. +timer_next dd 0 +endg + +; This internal function acquires the lock for the global list. +lock_timer_list: + mov edx, [CURRENT_TASK] +@@: + xor eax, eax + lock cmpxchg [timer_list_owner], edx + jz @f + cmp eax, edx + jz @f + call change_task + jmp @b +@@: + inc [timer_list_numlocks] + ret + +; This internal function releases the lock for the global list. +unlock_timer_list: + dec [timer_list_numlocks] + jnz .nothing + mov [timer_list_owner], 0 +.nothing: + ret + +; This function adds a timer. +; If deltaStart is nonzero, the timer is activated after deltaStart hundredths +; of seconds starting from the current time. If interval is nonzero, the timer +; is activated every deltaWork hundredths of seconds starting from the first +; activation. The activated timer calls timerFunc as stdcall function with one +; argument userData. +; Return value is NULL if something has failed or some value which is opaque +; for the caller. Later this value can be used for cancel_timer_hs. +proc timer_hs stdcall uses ebx, deltaStart:dword, interval:dword, \ + timerFunc:dword, userData:dword +; 1. Allocate memory for the TIMER structure. +; 1a. Call the allocator. + movi eax, sizeof.TIMER + call malloc +; 1b. If allocation failed, return (go to 5) with eax = 0. + test eax, eax + jz .nothing +; 2. Setup the TIMER structure. + xchg ebx, eax +; 2a. Copy values from the arguments. + mov ecx, [interval] + mov [ebx+TIMER.Interval], ecx + mov ecx, [timerFunc] + mov [ebx+TIMER.TimerFunc], ecx + mov ecx, [userData] + mov [ebx+TIMER.UserData], ecx +; 2b. Get time of the next activation. + mov ecx, [deltaStart] + test ecx, ecx + jnz @f + mov ecx, [interval] +@@: + add ecx, [timer_ticks] + mov [ebx+TIMER.Time], ecx +; 3. Insert the TIMER structure to the global list. +; 3a. Acquire the lock. + call lock_timer_list +; 3b. Insert an item at ebx to the tail of the timer_list. + mov eax, timer_list + mov ecx, [eax+TIMER.Prev] + mov [ebx+TIMER.Next], eax + mov [ebx+TIMER.Prev], ecx + mov [eax+TIMER.Prev], ebx + mov [ecx+TIMER.Next], ebx +; 3c. Release the lock. + call unlock_timer_list +; 4. Return with eax = pointer to TIMER structure. + xchg ebx, eax +.nothing: +; 5. Returning. + ret +endp + +; This function removes a timer. +; The only argument is [esp+4] = the value which was returned from timer_hs. +cancel_timer_hs: + push ebx ; save used register to be stdcall +; 1. Remove the TIMER structure from the global list. +; 1a. Acquire the lock. + call lock_timer_list + mov ebx, [esp+4+4] +; 1b. Delete an item at ebx from the double-linked list. + mov eax, [ebx+TIMER.Next] + mov ecx, [ebx+TIMER.Prev] + mov [eax+TIMER.Prev], ecx + mov [ecx+TIMER.Next], eax +; 1c. If we are removing the next timer in currently processing chain, +; the next timer for this timer becomes new next timer. + cmp ebx, [timer_next] + jnz @f + mov [timer_next], eax +@@: +; 1d. Release the lock. + call unlock_timer_list +; 2. Free the TIMER structure. + xchg eax, ebx + call free +; 3. Return. + pop ebx ; restore used register to be stdcall + ret 4 ; purge one dword argument to be stdcall + +; This function is regularly called from osloop. It processes the global list +; and activates the corresponding timers. +check_timers: +; 1. Acquire the lock. + call lock_timer_list +; 2. Loop over all registered timers, checking time. +; 2a. Get the first item. + mov eax, [timer_list+TIMER.Next] + mov [timer_next], eax +.loop: +; 2b. Check for end of list. + cmp eax, timer_list + jz .done +; 2c. Get and store the next timer. + mov edx, [eax+TIMER.Next] + mov [timer_next], edx +; 2d. Check time for timer activation. +; We can't just compare [timer_ticks] and [TIMER.Time], since overflows are +; possible: if the current time is 0FFFFFFFFh ticks and timer should be +; activated in 3 ticks, the simple comparison will produce incorrect result. +; So we calculate the difference [timer_ticks] - [TIMER.Time]; if it is +; non-negative, the time is over; if it is negative, then either the time is +; not over or we have not processed this timer for 2^31 ticks, what is very +; unlikely. + mov edx, [timer_ticks] + sub edx, [eax+TIMER.Time] + js .next +; The timer should be activated now. +; 2e. Store the timer data in the stack. This is required since 2f can delete +; the timer, invalidating the content. + push [eax+TIMER.UserData] ; parameter for TimerFunc + push [eax+TIMER.TimerFunc] ; to be restored in 2g +; 2f. Calculate time of next activation or delete the timer if it is one-shot. + mov ecx, [eax+TIMER.Interval] + add [eax+TIMER.Time], ecx + test ecx, ecx + jnz .nodelete + stdcall cancel_timer_hs, eax +.nodelete: +; 2g. Activate timer, using data from the stack. + pop eax + call eax +.next: +; 2h. Advance to the next timer and continue the loop. + mov eax, [timer_next] + jmp .loop +.done: +; 3. Release the lock. + 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-process/core/v86.inc b/kernel/branches/kolibri-process/core/v86.inc new file mode 100644 index 000000000..18fa3c354 --- /dev/null +++ b/kernel/branches/kolibri-process/core/v86.inc @@ -0,0 +1,924 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2007-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3696 $ + +; Virtual-8086 mode manager +; diamond, 2007, 2008 + +DEBUG_SHOW_IO = 0 + +struct V86_machine +; page directory + pagedir dd ? +; translation table: V86 address -> flat linear address + pages dd ? +; mutex to protect all data from writing by multiple threads at one time + mutex dd ? +; i/o permission map + iopm dd ? +ends + +; Create V86 machine +; in: nothing +; out: eax = handle (pointer to struc V86_machine) +; eax = NULL => failure +; destroys: ebx, ecx, edx (due to malloc) +v86_create: +; allocate V86_machine structure + mov eax, sizeof.V86_machine + call malloc + test eax, eax + jz .fail +; initialize mutex + and dword [eax+V86_machine.mutex], 0 +; allocate tables + mov ebx, eax +; We allocate 4 pages. +; First is main page directory for V86 mode. +; Second page: +; first half (0x800 bytes) is page table for addresses 0 - 0x100000, +; second half is for V86-to-linear translation. +; Third and fourth are for I/O permission map. + push 8000h ; blocks less than 8 pages are discontinuous + call kernel_alloc + test eax, eax + jz .fail2 + mov [ebx+V86_machine.pagedir], eax + push edi eax + mov edi, eax + add eax, 1800h + mov [ebx+V86_machine.pages], eax +; initialize tables + mov ecx, 2000h/4 + xor eax, eax + rep stosd + mov [ebx+V86_machine.iopm], edi + dec eax + mov ecx, 2000h/4 + rep stosd + pop eax +; page directory: first entry is page table... + mov edi, eax + add eax, 1000h + push eax + call get_pg_addr + or al, PG_UW + stosd +; ...and also copy system page tables +; thx to Serge, system is located at high addresses + add edi, (OS_BASE shr 20) - 4 + push esi + mov esi, sys_proc+PROC.pdt_0+(OS_BASE shr 20) + mov ecx, 0x80000000 shr 22 + rep movsd + + mov eax, [ebx+V86_machine.pagedir] ;root dir also is + call get_pg_addr ;used as page table + or al, PG_SW + mov [edi-4096+(page_tabs shr 20)], eax + + pop esi +; now V86 specific: initialize known addresses in first Mb + pop eax +; first page - BIOS data (shared between all machines!) +; physical address = 0 +; linear address = OS_BASE + mov dword [eax], 111b + mov dword [eax+800h], OS_BASE +; page before 0xA0000 - Extended BIOS Data Area (shared between all machines!) +; physical address = 0x9C000 +; linear address = 0x8009C000 +; (I have seen one computer with EBDA segment = 0x9D80, +; all other computers use less memory) + mov ecx, 4 + mov edx, 0x9C000 + push eax + lea edi, [eax+0x9C*4] +@@: + lea eax, [edx + OS_BASE] + mov [edi+800h], eax + lea eax, [edx + 111b] + stosd + add edx, 0x1000 + loop @b + pop eax + pop edi +; addresses 0xC0000 - 0xFFFFF - BIOS code (shared between all machines!) +; physical address = 0xC0000 +; linear address = 0x800C0000 + mov ecx, 0xC0 +@@: + mov edx, ecx + shl edx, 12 + push edx + or edx, 111b + mov [eax+ecx*4], edx + pop edx + add edx, OS_BASE + mov [eax+ecx*4+0x800], edx + inc cl + jnz @b + mov eax, ebx + ret +.fail2: + mov eax, ebx + call free +.fail: + xor eax, eax + ret + +; Destroy V86 machine +; in: eax = handle +; out: nothing +; destroys: eax, ebx, ecx, edx (due to free) +v86_destroy: + push eax + stdcall kernel_free, [eax+V86_machine.pagedir] + pop eax + jmp free + +; Translate V86-address to linear address +; in: eax=V86 address +; esi=handle +; out: eax=linear address +; destroys: nothing +v86_get_lin_addr: + push ecx edx + mov ecx, eax + mov edx, [esi+V86_machine.pages] + shr ecx, 12 + and eax, 0xFFF + add eax, [edx+ecx*4] ; atomic operation, no mutex needed + pop edx ecx + ret + +; Sets linear address for V86-page +; in: eax=linear address (must be page-aligned) +; ecx=V86 page (NOT address!) +; esi=handle +; out: nothing +; destroys: nothing +v86_set_page: + push eax ebx + mov ebx, [esi+V86_machine.pagedir] + mov [ebx+ecx*4+0x1800], eax + call get_pg_addr + or al, 111b + mov [ebx+ecx*4+0x1000], eax + pop ebx eax + ret + +; Allocate memory in V86 machine +; in: eax=size (in bytes) +; 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] +; call wait_mutex +; add eax, 0x1F +; shr eax, 4 +; mov ebx, 0x1000 ; start with address 0x1000 (second page) +; mov edi, [esi+V86_machine.tables] +;.l: +; mov ecx, ebx +; shr ecx, 12 +; mov edx, [edi+0x1000+ecx*4] ; get linear address +; test edx, edx ; page allocated? +; jz .unalloc +; mov ecx, ebx +; and ecx, 0xFFF +; add edx, ecx +; cmp dword [edx], 0 ; free block? +; jnz .n +; cmp dword [edx+4], +; and [esi+V86_machine.mutex], 0 +; pop edi edx ecx ebx +; ret + +uglobal +sys_v86_machine dd ? +endg + +; Called from kernel.asm at first stages of loading +; Initialize system V86 machine (used to simulate BIOS int 13h) +init_sys_v86: + call v86_create + mov [sys_v86_machine], eax + test eax, eax + jz .ret + mov byte [OS_BASE + 0x500], 0xCD + mov byte [OS_BASE + 0x501], 0x13 + mov byte [OS_BASE + 0x502], 0xF4 + mov byte [OS_BASE + 0x503], 0xCD + mov byte [OS_BASE + 0x504], 0x10 + mov byte [OS_BASE + 0x505], 0xF4 + mov esi, eax + mov ebx, [eax+V86_machine.pagedir] +; one page for stack, two pages for results (0x2000 bytes = 16 sectors) + mov dword [ebx+0x99*4+0x1000], 0x99000 or 111b + mov dword [ebx+0x99*4+0x1800], OS_BASE + 0x99000 + mov dword [ebx+0x9A*4+0x1000], 0x9A000 or 111b + mov dword [ebx+0x9A*4+0x1800], OS_BASE + 0x9A000 + mov dword [ebx+0x9B*4+0x1000], 0x9B000 or 111b + mov dword [ebx+0x9B*4+0x1800], OS_BASE + 0x9B000 +if ~DEBUG_SHOW_IO +; allow access to all ports + mov ecx, [esi+V86_machine.iopm] + xor eax, eax + mov edi, ecx + mov ecx, 10000h/8/4 + rep stosd +end if +.ret: + ret + +struct v86_regs +; don't change the order, it is important + edi dd ? + esi dd ? + ebp dd ? + dd ? ; ignored + ebx dd ? + edx dd ? + ecx dd ? + eax dd ? + eip dd ? + cs dd ? + eflags dd ? ; VM flag must be set! + esp dd ? + ss dd ? + es dd ? + ds dd ? + fs dd ? + gs dd ? +ends + +; Run V86 machine +; in: ebx -> registers for V86 (two structures: in and out) +; esi = handle +; ecx = expected end address (CS:IP) +; edx = IRQ to hook or -1 if not required +; out: structure pointed to by ebx is filled with new values +; eax = 1 - exception has occured, cl contains code +; eax = 2 - access to disabled i/o port, ecx contains port address +; eax = 3 - IRQ is already hooked by another VM +; destroys: nothing +v86_start: + pushad + + cli + + mov ecx, [CURRENT_TASK] + shl ecx, 8 + add ecx, SLOT_BASE + + mov eax, [esi+V86_machine.iopm] + call get_pg_addr + inc eax + push dword [ecx+APPDATA.io_map] + push dword [ecx+APPDATA.io_map+4] + mov dword [ecx+APPDATA.io_map], eax + mov dword [page_tabs + (tss._io_map_0 shr 10)], eax + add eax, 0x1000 + mov dword [ecx+APPDATA.io_map+4], eax + mov dword [page_tabs + (tss._io_map_1 shr 10)], eax + + push [ecx+APPDATA.process] + push [ecx+APPDATA.saved_esp0] + mov [ecx+APPDATA.saved_esp0], esp + mov [tss._esp0], esp + + mov eax, [esi+V86_machine.pagedir] + call get_pg_addr + mov [ecx+APPDATA.process], eax +; mov cr3, eax + +; mov [irq_tab+5*4], my05 + +; We do not enable interrupts, because V86 IRQ redirector assumes that +; machine is running +; They will be enabled by IRET. +; sti + + mov eax, esi + sub esp, sizeof.v86_regs + mov esi, ebx + mov edi, esp + mov ecx, sizeof.v86_regs/4 + rep movsd + + cmp edx, -1 + jz .noirqhook +uglobal +v86_irqhooks rd IRQ_RESERVED * 2 +endg + cmp [v86_irqhooks+edx*8], 0 + jz @f + cmp [v86_irqhooks+edx*8], eax + jz @f + mov esi, v86_irqerr + call sys_msg_board_str + inc [v86_irqhooks+edx*8+4] + mov eax, 3 + jmp v86_exc_c.exit +@@: + mov [v86_irqhooks+edx*8], eax + inc [v86_irqhooks+edx*8+4] +.noirqhook: + + popad + iretd + +; It is only possible to leave virtual-8086 mode by faulting to +; a protected-mode interrupt handler (typically the general-protection +; exception handler, which in turn calls the virtual 8086-mode monitor). + +iglobal + v86_exc_str1 db 'V86 : unexpected exception ',0 + v86_exc_str2 db ' at ',0 + v86_exc_str3 db ':',0 + v86_exc_str4 db 13,10,'V86 : faulted code:',0 + v86_exc_str5 db ' (unavailable)',0 + v86_newline db 13,10,0 + v86_io_str1 db 'V86 : access to disabled i/o port ',0 + v86_io_byte db ' (byte)',13,10,0 + v86_io_word db ' (word)',13,10,0 + v86_io_dword db ' (dword)',13,10,0 + v86_irqerr db 'V86 : IRQ already hooked',13,10,0 +endg + +v86_exc_c: +; Did we all that we have wanted to do? + cmp bl, 1 + jne @f + xor eax, eax + mov dr6, eax + @@: + mov eax, [esp+sizeof.v86_regs+10h+18h] + cmp word [esp+v86_regs.eip], ax + jnz @f + shr eax, 16 + cmp word [esp+v86_regs.cs], ax + jz .done +@@: +; Various system events, which must be handled, result in #GP + cmp bl, 13 + jnz .nogp +; If faulted EIP exceeds 0xFFFF, we have #GP and it is an error + cmp word [esp+v86_regs.eip+2], 0 + jnz .nogp +; Otherwise we can safely access byte at CS:IP +; (because it is #GP, not #PF handler) +; Если бы мы могли схлопотать исключение только из-за чтения байтов кода, +; мы бы его уже схлопотали и это было бы не #GP + movzx esi, word [esp+v86_regs.cs] + shl esi, 4 + add esi, [esp+v86_regs.eip] + lodsb + cmp al, 0xCD ; int xx command = CD xx + jz .handle_int + cmp al, 0xCF + jz .handle_iret + cmp al, 0xF3 + jz .handle_rep + cmp al, 0xEC + jz .handle_in + cmp al, 0xED + jz .handle_in_word + cmp al, 0xEE + jz .handle_out + cmp al, 0xEF + jz .handle_out_word + cmp al, 0xE4 + jz .handle_in_imm + cmp al, 0xE6 + jz .handle_out_imm + cmp al, 0x9C + jz .handle_pushf + cmp al, 0x9D + jz .handle_popf + cmp al, 0xFA + jz .handle_cli + cmp al, 0xFB + jz .handle_sti + cmp al, 0x66 + jz .handle_66 + jmp .nogp +.handle_int: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + xor eax, eax + lodsb +; call sys_msg_board_byte +; simulate INT command +; N.B. It is possible that some checks need to be corrected, +; but at least in case of normal execution the code works. +.simulate_int: + cmp word [esp+v86_regs.esp], 6 + jae @f + mov bl, 12 ; #SS exception + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + push eax + movzx eax, word [esp+4+v86_regs.esp] + sub eax, 6 + add edx, eax + mov eax, edx + mov esi, [esp+4+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + sub word [esp+4+v86_regs.esp], 6 + mov eax, [esp+4+v86_regs.eip] + cmp byte [esp+1], 0 + jnz @f + inc eax + inc eax +@@: + mov word [edx], ax + mov eax, [esp+4+v86_regs.cs] + mov word [edx+2], ax + mov eax, [esp+4+v86_regs.eflags] + mov word [edx+4], ax + pop eax + mov ah, 0 + mov cx, [eax*4] + mov word [esp+v86_regs.eip], cx + mov cx, [eax*4+2] + mov word [esp+v86_regs.cs], cx +; note that interrupts will be disabled globally at IRET + and byte [esp+v86_regs.eflags+1], not 3 ; clear IF and TF flags +; continue V86 execution + popad + iretd +.handle_iret: + cmp word [esp+v86_regs.esp], 0x10000 - 6 + jbe @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov ax, [edx] + mov word [esp+v86_regs.eip], ax + mov ax, [edx+2] + mov word [esp+v86_regs.cs], ax + mov ax, [edx+4] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 6 + popad + iretd +.handle_pushf: + cmp word [esp+v86_regs.esp], 1 + jnz @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + mov eax, [esp+v86_regs.esp] + sub eax, 2 + movzx eax, ax + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+1] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + sub word [esp+v86_regs.esp], 2 + mov eax, [esp+v86_regs.eflags] + mov [edx], ax + inc word [esp+v86_regs.eip] + popad + iretd +.handle_pushfd: + cmp word [esp+v86_regs.esp], 4 + jae @f + mov bl, 12 ; #SS exception + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + sub eax, 4 + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+3] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + sub word [esp+v86_regs.esp], 4 + movzx eax, word [esp+v86_regs.eflags] + mov [edx], eax + add word [esp+v86_regs.eip], 2 + popad + iretd +.handle_popf: + cmp word [esp+v86_regs.esp], 0xFFFF + jnz @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+1] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov ax, [edx] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_popfd: + cmp word [esp+v86_regs.esp], 0x10000 - 4 + jbe @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + lea eax, [edx+3] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov eax, [edx] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 4 + add word [esp+v86_regs.eip], 2 + popad + iretd +.handle_cli: + and byte [esp+v86_regs.eflags+1], not 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_sti: + or byte [esp+v86_regs.eflags+1], 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_rep: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + cmp al, 6Eh + jz .handle_rep_outsb + jmp .nogp +.handle_rep_outsb: +.handle_in: +.handle_out: +.invalid_io_byte: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 1 + jmp .invalid_io +.handle_in_imm: +.handle_out_imm: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + movzx ebx, al + mov ecx, 1 + jmp .invalid_io +.handle_66: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + cmp al, 0x9C + jz .handle_pushfd + cmp al, 0x9D + jz .handle_popfd + cmp al, 0xEF + jz .handle_out_dword + cmp al, 0xED + jz .handle_in_dword + jmp .nogp +.handle_in_word: +.handle_out_word: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 2 + jmp .invalid_io +.handle_in_dword: +.handle_out_dword: +.invalid_io_dword: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 4 +.invalid_io: + mov esi, v86_io_str1 + call sys_msg_board_str + mov eax, ebx + call sys_msg_board_dword + mov esi, v86_io_byte + cmp ecx, 1 + jz @f + mov esi, v86_io_word + cmp ecx, 2 + jz @f + mov esi, v86_io_dword +@@: + call sys_msg_board_str +if DEBUG_SHOW_IO + mov edx, ebx + mov ebx, 200 + call delay_hs + mov esi, [esp+v86_regs.size+10h+4] + mov eax, [esi+V86_machine.iopm] +@@: + btr [eax], edx + inc edx + loop @b + popad + iretd +else + mov eax, 2 + jmp .exit +end if +.nogp: + + mov esi, v86_exc_str1 + call sys_msg_board_str + mov al, bl + call sys_msg_board_byte + mov esi, v86_exc_str2 + call sys_msg_board_str + mov ax, [esp+32+4] + call sys_msg_board_word + mov esi, v86_exc_str3 + call sys_msg_board_str + mov ax, [esp+32] + call sys_msg_board_word + mov esi, v86_exc_str4 + call sys_msg_board_str + mov ecx, 8 + movzx edx, word [esp+32+4] + shl edx, 4 + add edx, [esp+32] +@@: + mov esi, [esp+sizeof.v86_regs+10h+4] + mov eax, edx + call v86_get_lin_addr + cmp eax, 0x1000 + jb .nopage + mov esi, v86_exc_str3-2 + call sys_msg_board_str + mov al, [edx] + call sys_msg_board_byte + inc edx + loop @b + jmp @f +.nopage: + mov esi, v86_exc_str5 + call sys_msg_board_str +@@: + mov esi, v86_newline + call sys_msg_board_str + mov eax, 1 + jmp .exit + +.done: + xor eax, eax + +.exit: + mov [esp+sizeof.v86_regs+10h+1Ch], eax + mov [esp+sizeof.v86_regs+10h+18h], ebx + + mov edx, [esp+sizeof.v86_regs+10h+14h] + cmp edx, -1 + jz @f + dec [v86_irqhooks+edx*8+4] + jnz @f + and [v86_irqhooks+edx*8], 0 +@@: + + mov esi, esp + mov edi, [esi+sizeof.v86_regs+10h+10h] + add edi, sizeof.v86_regs + mov ecx, sizeof.v86_regs/4 + rep movsd + mov esp, esi + + cli + mov ecx, [CURRENT_TASK] + shl ecx, 8 + pop eax + mov [SLOT_BASE+ecx+APPDATA.saved_esp0], eax + mov [tss._esp0], eax + pop eax + mov [SLOT_BASE+ecx+APPDATA.process], eax + pop ebx + mov dword [SLOT_BASE+ecx+APPDATA.io_map+4], ebx + mov dword [page_tabs + (tss._io_map_1 shr 10)], ebx + pop ebx + mov dword [SLOT_BASE+ecx+APPDATA.io_map], ebx + mov dword [page_tabs + (tss._io_map_0 shr 10)], ebx + mov cr3, eax + sti + + popad + ret + +;my05: +; mov dx, 30C2h +; mov cx, 4 +;.0: +; in al, dx +; cmp al, 0FFh +; jz @f +; test al, 4 +; jnz .1 +;@@: +; add dx, 8 +; in al, dx +; cmp al, 0FFh +; jz @f +; test al, 4 +; jnz .1 +;@@: +; loop .0 +; ret +;.1: +; or al, 84h +; out dx, al +;.2: +; mov dx, 30F7h +; in al, dx +; mov byte [BOOT_VAR + 48Eh], 0FFh +; ret + +align 4 +v86_irq: +; push irq/pushad/jmp v86_irq +; ebp = irq + lea esi, [esp+1Ch] + lea edi, [esi+4] + mov ecx, 8 + std + rep movsd + cld + mov edi, ebp + pop eax +v86_irq2: + mov esi, [v86_irqhooks+edi*8] ; get VM handle + mov eax, [esi+V86_machine.pagedir] + call get_pg_addr + mov ecx, [CURRENT_TASK] + shl ecx, 8 + cmp [SLOT_BASE+ecx+APPDATA.process], eax + jnz .notcurrent + lea eax, [edi+8] + cmp al, 10h + mov ah, 1 + jb @f + add al, 60h +@@: + jmp v86_exc_c.simulate_int +.notcurrent: + mov ebx, SLOT_BASE + 0x100 + mov ecx, [TASK_COUNT] +.scan: + cmp [ebx+APPDATA.process], eax + jnz .cont + push ecx + mov ecx, [ebx+APPDATA.saved_esp0] + cmp word [ecx-sizeof.v86_regs+v86_regs.esp], 6 + jb .cont2 + movzx edx, word [ecx-sizeof.v86_regs+v86_regs.ss] + shl edx, 4 + push eax + movzx eax, word [ecx-sizeof.v86_regs+v86_regs.esp] + sub eax, 6 + add edx, eax + mov eax, edx + call v86_get_lin_addr + cmp eax, 0x1000 + jb .cont3 + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jb .cont3 + pop eax + pop ecx + jmp .found +.cont3: + pop eax +.cont2: + pop ecx +.cont: + add ebx, 0x100 + loop .scan + mov ecx, edi + call irq_eoi + popad + iretd +.found: + mov cr3, eax + mov esi, [ebx+APPDATA.saved_esp0] + sub word [esi-sizeof.v86_regs+v86_regs.esp], 6 + mov ecx, [esi-sizeof.v86_regs+v86_regs.eip] + mov word [edx], cx + mov ecx, [esi-sizeof.v86_regs+v86_regs.cs] + mov word [edx+2], cx + mov ecx, [esi-sizeof.v86_regs+v86_regs.eflags] + mov word [edx+4], cx + lea eax, [edi+8] + cmp al, 10h + jb @f + add al, 60h +@@: + mov cx, [eax*4] + mov word [esi-sizeof.v86_regs+v86_regs.eip], cx + mov cx, [eax*4+2] + mov word [esi-sizeof.v86_regs+v86_regs.cs], cx + and byte [esi-sizeof.v86_regs+v86_regs.eflags+1], not 3 + call update_counters + lea edi, [ebx + 0x100000000 - SLOT_BASE] + shr edi, 3 + add edi, CURRENT_TASK + call find_next_task.found + call do_change_task + popad + iretd diff --git a/kernel/branches/kolibri-process/data16.inc b/kernel/branches/kolibri-process/data16.inc new file mode 100644 index 000000000..e7fe8b7da --- /dev/null +++ b/kernel/branches/kolibri-process/data16.inc @@ -0,0 +1,91 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3539 $ + + +flm db 0 +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 + +align 4 +old_ints_h: + dw 0x400 + dd 0 + dw 0 + +if ~ defined extended_primary_loader ; restart from memory is not supported in extended primary loader cfg +kernel_restart_bootblock: + db 1 ; version + dw 1 ; floppy image is in memory + dd 0 ; cannot save parameters +end if + +; table for move to extended memory (int 15h, ah=87h) +align 8 +movedesc: + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + + db 0xff,0xff,0x0,0xa0,0x00,0x93,0x0,0x0 + db 0xff,0xff,0x0,0x00,0x10,0x93,0x0,0x0 + + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + +fwmovedesc: + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + + db 0xff,0xff,0x0,0x00,0x10,0x93,0x0,0x0 + db 0xff,0xff,0x0,0xa0,0x00,0x93,0x0,0x0 + + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + +if defined extended_primary_loader +; look in PrimaryLoader.txt for the description +bootdevice dw 0 ; ax from primary loader +bootfs dw 0 ; bx from primary loader +bootcallback dd 0 ; ds:si from primary loader +; data for configuration file loading, look in PrimaryLoader.txt +config_file_struct: + dw 0, 4000h ; load to 4000:0000 + dw 16 ; read no more than 16*4K = 64K + db 'config.ini',0 +; data for configuration file parsing +macro config_variable string,parser +{ +local len +len dw 0 + db string +store word $ - len - 2 at len + dw parser +} +config_file_variables: + config_variable 'timeout', parse_timeout + config_variable 'resolution', parse_resolution + config_variable 'vbemode', parse_vbemode +; config_variable 'vrr', parse_vrr + config_variable 'biosdisks', parse_biosdisks + config_variable 'imgfrom', parse_imgfrom + dw 0 +; data for image file loading, look in PrimaryLoader.txt +image_file_struct: + dw 0, 4000h ; load to 4000:0000 + dw 16 ; read no more than 16*4K = 64K + db 'kolibri.img',0 +end if diff --git a/kernel/branches/kolibri-process/data32.inc b/kernel/branches/kolibri-process/data32.inc new file mode 100644 index 000000000..2a3fb4f85 --- /dev/null +++ b/kernel/branches/kolibri-process/data32.inc @@ -0,0 +1,593 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4278 $ + + +keymap: + + db '6',27 + db '1234567890-=',8,9 + db 'qwertyuiop[]',13 + db '~asdfghjkl;',39,96,0,'\zxcvbnm,./',0,'45 ' + db '@234567890123',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'AB?',0,'45 ' + db '@234567890123',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'AB>D',255,'FGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + +keymap_alt: + db ' ',27 + db ' @ $ {[]}\ ',8,9 + db ' ',13 + db ' ',0,' ',0,'4',0,' ' + db ' ',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'ABCD',255,'FGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + + +if lang eq ru + 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, 13',0 + boot_disabling_ide cp866 'Запрещение прерываний в контроллере IDE',0 + boot_enabling_ide cp866 'Разрешение прерываний в контроллере IDE',0 + boot_set_int_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_initramdisk 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 cp866 'Все готово для запуска, нажмитре ESC для старта',0 +end if +else if lang eq sp + include 'data32sp.inc' +else if lang eq et + include 'data32et.inc' +else + boot_initirq db 'Initialize IRQ',0 + boot_picinit db 'Initialize PIC',0 + boot_v86machine db 'Initialize system V86 machine',0 + boot_inittimer db 'Initialize system timer (IRQ0)',0 + boot_initramdisk db 'Initialize ramdisk',0 + boot_initapic db 'Try to initialize APIC',0 + boot_enableirq db 'Enable interrupts 2, 13',0 + boot_disabling_ide db 'Disable interrupts in IDE controller',0 + boot_enabling_ide db 'Enable interrupts in IDE controller',0 + boot_set_int_IDE db 'Set handler of interrupts for IDE',0 + boot_detectfloppy db 'Search floppy drives',0 + boot_detecthdcd db 'Search hard drives and ATAPI drives',0 + boot_getcache db 'Get memory for cache',0 + boot_detectpart db 'Search partitions on disk devices',0 + boot_init_sys db 'Initialize system directory /sys',0 + boot_loadlibs db 'Loading librares (.obj)',0 + boot_memdetect db 'Determining amount of memory',0 + boot_tss db 'Setting TSSs',0 + boot_cpuid db 'Reading CPUIDs',0 +; boot_devices db 'Detecting devices',0 + boot_setmouse db 'Setting mouse',0 + boot_windefs db 'Setting window defaults',0 + boot_bgr db 'Calculating background',0 + boot_resirqports db 'Reserving IRQs & ports',0 + boot_setostask db 'Setting OS task',0 + boot_allirqs db 'Unmasking IRQs',0 + boot_tsc db 'Reading TSC',0 + boot_cpufreq db 'CPU frequency is ',' ',' MHz',0 + boot_pal_ega db 'Setting EGA/CGA 320x200 palette',0 + 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 + boot_tasking db 'All set - press ESC to start',0 +end if +end if + +;new_process_loading db 'K : New Process - loading',13,10,0 +;new_process_running db 'K : New Process - done',13,10,0 +start_not_enough_memory db 'K : New Process - not enough memory',13,10,0 + +msg_unresolved db 'unresolved ',0 +msg_module db 'in module ',0 +if ~ lang eq sp +msg_version db 'incompatible driver version',13,10,0 +msg_www db 'please visit www.kolibrios.org',13,10,0 +end if +msg_CR db 13,10,0 + +intel_str db "GenuineIntel",0 +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 + +szSTART db 'START',0 +szEXPORTS db 'EXPORTS',0 +sz_EXPORTS db '_EXPORTS',0 + +szIMPORTS db 'IMPORTS',0 + +read_firstapp db '/sys/' +firstapp db 'LAUNCHER',0 +notifyapp db '@notify',0 +if lang eq ru +ud_user_message cp866 'Ошибка: неподдерживаемая инструкция процессора',0 +else if ~ lang eq sp +ud_user_message db 'Error: unsupported processor instruction',0 +end if + +vmode db '/sys/drivers/VMODE.MDR',0 +;vrr_m db 'VRR_M',0 +kernel_file_load: +; load kernel.mnt to 0x7000:0 + dd 0 ; subfunction + dq 0 ; offset in file + dd 0x30000 ; number of bytes to read + dd OS_BASE + 0x70000 ; buffer for data + db '/RD/1/KERNEL.MNT',0 + +dev_data_path db '/RD/1/DRIVERS/DEVICES.DAT',0 + +align 4 + +shmem_list: + .bk dd shmem_list + .fd dd shmem_list + +dll_list: + .bk dd dll_list + .fd dd dll_list + +pcidev_list: + .bk dd pcidev_list + .fd dd pcidev_list + +MAX_DEFAULT_DLL_ADDR = 0x80000000 +MIN_DEFAULT_DLL_ADDR = 0x70000000 +dll_cur_addr dd MIN_DEFAULT_DLL_ADDR + +; supported videomodes + + +; mike.dld { +;db 0 +;dd servetable-0x10000 +;align 4 +;draw_line dd __sys_draw_line +;draw_pointer dd __sys_draw_pointer +;//mike.dld, 2006-08-02 [ +;;drawbar dd __sys_drawbar +;;drawbar dd __sys_drawbar.forced +;drawbar dd vesa20_drawbar +;//mike.dld, 2006-08-02 ] +;putpixel dd __sys_putpixel +; } mike.dld + + +align 4 +keyboard dd 1 +syslang dd 1 + +boot_y dd 10 + +pci_bios_entry dd 0 + dw pci_code_sel + +if __DEBUG__ eq 1 + include_debug_strings +end if + +IncludeIGlobals + +align 16 +gdts: + + dw gdte-$-1 + dd gdts + dw 0 + +; Attention! Do not change the order of the first four selectors. They are used in Fast System Call +; must be : os_code, os_data, app_code, app_data, .... + +int_code_l: +os_code_l: + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10011010b + db 0x00 + +int_data_l: +os_data_l: + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10010010b + db 0x00 + +app_code_l: + dw 0xFFFF + dw 0 + db 0 + db cpl3 + dw G32+D32+0xF; + +app_data_l: + dw 0xFFFF + dw 0 + db 0 + db drw3 + dw G32+D32+0xF; + +; ------------- PCI BIOS ------------------ + +pci_code_32: + dw 0 ;lim 0-15 + dw 0 ;base 0-15 + db 0 ;base 16-23 + db cpl0 ;type + db D32 ;lim 16-19+props + db 0 ;base 24-31 + +pci_data_32: + dw 0 ;lim 0-15 + dw 0 ;base 0-15 + db 0 ;base 16-23 + db dpl0 ;type + db D32 ;lim 16-19+props + db 0 ;base 24-31 + +; --------------- APM --------------------- +apm_code_32: + dw 0x0f ; limit 64kb + db 0, 0, 0 + dw 11010000b *256 +10011010b + db 0x00 +apm_code_16: + dw 0x0f + db 0, 0, 0 + dw 10010000b *256 +10011010b + db 0x00 +apm_data_16: + dw 0x0f + db 0, 0, 0 + dw 10010000b *256 +10010010b + db 0x00 +; ----------------------------------------- + +graph_data_l: + + dw 0x7ff + dw 0x0000 + db 0x00 + dw 11010000b *256 +11110010b + db 0x00 +tss0_l: + dw sizeof.TSS-1 + dw tss and 0xFFFF + db (tss shr 16) and 0xFF + db 10001001b + dw (tss shr 16) and 0xFF00 + +tls_data_l: + dw 0x0FFF + dw 0 + db 0 + db drw3 + dw D32 + +endofcode: +gdte: + +diff16 "end of .data segment",0,$ + +align 16 +cur_saved_data: + rb 4096 +fpu_data: + rb 512 + +mem_block_list rd 64*2 +mem_used_list rd 64*2 +mem_hash_cnt rd 64 + +thr_slot_map rd 8 + +cpu_freq rq 1 + +heap_mutex MUTEX +heap_size rd 1 +heap_free rd 1 +heap_blocks rd 1 +free_blocks rd 1 + +mem_block_mask rd 2 +next_memblock rd 1 + + +mst MEM_STATE + +page_start rd 1 +page_end rd 1 +sys_page_map rd 1 +os_stack_seg rd 1 + + +srv.fd rd 1 +srv.bk rd 1 + + +align 16 + +_display display_t + +_WinMapAddress rd 1 +_WinMapSize rd 1 + +LFBAddress rd 1 +Screen_Max_X rd 1 +Screen_Max_Y rd 1 + +SCR_MODE rw 2 + +PUTPIXEL rd 1 +GETPIXEL rd 1 + +if VESA_1_2_VIDEO +BANK_SWITCH rd 1 reserved for vesa 1.2 +BANK_RW rd 1 +end if + +REDRAW_BACKGROUND rb 4 + +align 4 +draw_data: rb 32*256 +BPSLine_calc_area rd 1440 +d_width_calc_area rd 1140 + +mouseunder rd 16*24 + +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 + +align 4 +def_cursor rd 1 +def_cursor_clock rd 1 +current_cursor rd 1 +hw_cursor rd 1 +cur_saved_base rd 1 + +cur.lock rd 1 ;1 - lock update, 2- hide +cur.left rd 1 ;cursor clip box +cur.top rd 1 +cur.right rd 1 +cur.bottom rd 1 +cur.w rd 1 +cur.h rd 1 + +ipc_tmp rd 1 +ipc_pdir rd 1 +ipc_ptab rd 1 + +proc_mem_map rd 1 +proc_mem_pdir rd 1 +proc_mem_tab rd 1 + +tmp_task_ptab rd 1 + +default_io_map rd 1 + +LFBSize rd 1 + +current_slot rd 1 + +; status +hd1_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 +midisp rd 1 + +cdbase rd 1 +cdid rd 1 + +hdbase rd 1 ; for boot 0x1f0 +hdid rd 1 +hdpos rd 1 ; for boot 0x1 +label known_part dword +fat32part rd 1 ; for boot 0x1 +cdpos rd 1 + +;CPUID information +cpu_vendor rd 3 +cpu_sign rd 1 +cpu_info rd 1 +cpu_caps rd 4 + + +pg_data PG_DATA +heap_test rd 1 + +buttontype rd 1 +windowtypechanged rd 1 + +hd_entries rd 1 ;unused ? 0xfe10 + +mouse_active rd 1 +mouse_pause rd 1 + +redrawmouse_unconditional rd 1 + +img_background rd 1 +mem_BACKGROUND rd 1 +static_background_data rd 1 + +BgrDrawMode rd 1 +BgrDataWidth rd 1 +BgrDataHeight rd 1 + +skin_data rd 1 + +cache_ide0: +cache_ide0_pointer rd 1 +cache_ide0_size rd 1 ; not use +cache_ide0_data_pointer rd 1 +cache_ide0_system_data_size rd 1 ; not use +cache_ide0_appl_data_size rd 1 ; not use +cache_ide0_system_data rd 1 +cache_ide0_appl_data rd 1 +cache_ide0_system_sad_size rd 1 +cache_ide0_appl_sad_size rd 1 +cache_ide0_search_start rd 1 +cache_ide0_appl_search_start rd 1 + +cache_ide1: +cache_ide1_pointer rd 1 +cache_ide1_size rd 1 ; not use +cache_ide1_data_pointer rd 1 +cache_ide1_system_data_size rd 1 ; not use +cache_ide1_appl_data_size rd 1 ; not use +cache_ide1_system_data rd 1 +cache_ide1_appl_data rd 1 +cache_ide1_system_sad_size rd 1 +cache_ide1_appl_sad_size rd 1 +cache_ide1_search_start rd 1 +cache_ide1_appl_search_start rd 1 + +cache_ide2: +cache_ide2_pointer rd 1 +cache_ide2_size rd 1 ; not use +cache_ide2_data_pointer rd 1 +cache_ide2_system_data_size rd 1 ; not use +cache_ide2_appl_data_size rd 1 ; not use +cache_ide2_system_data rd 1 +cache_ide2_appl_data rd 1 +cache_ide2_system_sad_size rd 1 +cache_ide2_appl_sad_size rd 1 +cache_ide2_search_start rd 1 +cache_ide2_appl_search_start rd 1 + +cache_ide3: +cache_ide3_pointer rd 1 +cache_ide3_size rd 1 ; not use +cache_ide3_data_pointer rd 1 +cache_ide3_system_data_size rd 1 ; not use +cache_ide3_appl_data_size rd 1 ; not use +cache_ide3_system_data rd 1 +cache_ide3_appl_data rd 1 +cache_ide3_system_sad_size rd 1 +cache_ide3_appl_sad_size rd 1 +cache_ide3_search_start rd 1 +cache_ide3_appl_search_start rd 1 + +debug_step_pointer rd 1 + +lba_read_enabled rd 1 ; 0 = disabled , 1 = enabled +pci_access_enabled rd 1 ; 0 = disabled , 1 = enabled + +hdd_appl_data rb 1 ; 0 = system cache, 1 - application cache +cd_appl_data rb 1 ; 0 = system cache, 1 - application cache + +timer_ticks_enable rb 1 ; for cd driver + +align 4 +NumBiosDisks rd 1 +BiosDisksData rb 200h +BiosDiskCaches rb 80h*(cache_ide1-cache_ide0) +BiosDiskPartitions rd 80h + +align 16 +DRIVE_DATA: rb DRIVE_DATA_SIZE + +IncludeUGlobals + +uglobals_size = $ - endofcode + +if ~ lang eq sp +diff16 "end of .bss",0,$ +end if + +org (OS_BASE+0x0100000) + +RAMDISK: rb 2880*512 + rb 2856*4 ; not used + +_CLEAN_ZONE: + +align 4096 +_IDE_DMA rb 16*512 +BgrAuxTable rb 32768 +BUTTON_INFO rb 64*1024 +RESERVED_PORTS: rb 64*1024 +FLOPPY_BUFF: rb 18*512 ;one track + +sys_pgmap: rb 1024*1024/8 diff --git a/kernel/branches/kolibri-process/data32et.inc b/kernel/branches/kolibri-process/data32et.inc new file mode 100644 index 000000000..63d9573f2 --- /dev/null +++ b/kernel/branches/kolibri-process/data32et.inc @@ -0,0 +1,38 @@ + boot_initirq latin1 'Algväärtustan IRQ',0 + boot_picinit latin1 'Algväärtustan PIC',0 + boot_v86machine latin1 'Algväärtustan süsteemi V86 masinat',0 + boot_inittimer latin1 'Algväärtustan süsteemi taimerit (IRQ0)',0 + boot_initramdisk latin1 'Initialize ramdisk',0 + boot_initapic latin1 'Proovin Algväärtustada APIC',0 + boot_enableirq latin1 'Luban katkestused 2, 13',0 + boot_disabling_ide latin1 'Keelan IDE kontrolleri katkestused',0 + boot_enabling_ide latin1 'Luban IDE kontrolleri katkestused',0 + boot_set_int_IDE latin1 'Määran IDE kontrolleri halduri',0 + boot_detectfloppy latin1 'Otsin floppi kettaid',0 + boot_detecthdcd latin1 'Otsin kõvakettaid ja ATAPI seadmeid',0 + boot_getcache latin1 'Küsin puhvri mälu',0 + boot_detectpart latin1 'Otsin kettaseadmete partitsioone',0 + boot_init_sys latin1 'Algväärtustan süsteemi kataloogi /sys',0 + boot_loadlibs latin1 'Laadin mooduleid (.obj)',0 + boot_memdetect latin1 'Avastan mälu mahtu',0 + boot_tss latin1 'Määran TSSe',0 + boot_cpuid latin1 'Loen CPUIDd',0 +; boot_devices db 'Detecting devices',0 + boot_setmouse latin1 'Seadistan hiirt',0 + boot_windefs latin1 'Seadistan akende vaikeväärtusi',0 + boot_bgr latin1 'Kalkuleerin tausta',0 + boot_resirqports latin1 'Reserveerin IRQsi ja porte',0 + boot_setostask latin1 'Seadistan OS protsessi',0 + boot_allirqs latin1 'Unmasking IRQs',0 + boot_tsc latin1 'Loen TSC',0 + boot_cpufreq latin1 'CPU sagedus on ',' ',' MHz',0 + boot_pal_ega latin1 'Seadistan EGA/CGA 320x200 paletti',0 + boot_pal_vga latin1 'Seadistan VGA 640x480 paletti',0 + boot_failed latin1 'Esimese programmi käivitamine ebaõnnestus',0 + boot_mtrr latin1 'Määran MTRR',0 + + boot_APIC_found latin1 'APIC aktiveeritud', 0 + boot_APIC_nfound latin1 'APIC ei leitud', 0 +if preboot_blogesc + boot_tasking latin1 'Kõik valmis - vajuta ESC alustamiseks',0 +end if \ No newline at end of file diff --git a/kernel/branches/kolibri-process/data32sp.inc b/kernel/branches/kolibri-process/data32sp.inc new file mode 100644 index 000000000..a0b25b4e9 --- /dev/null +++ b/kernel/branches/kolibri-process/data32sp.inc @@ -0,0 +1,43 @@ + 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_initramdisk cp850 'Initialize ramdisk',0 + boot_initapic: cp850 'Prueba inicializar APIC',0 + boot_enableirq: cp850 'Habilitar interrupciones 2, 13',0 + boot_disabling_ide:cp850 'Habiliar interrupciones en controladores IDE',0 + boot_enabling_ide:cp850 'Habilitar interrupciones en controladores IDE',0 + boot_set_int_IDE: cp850 'Configuración del controlador de interrupciones para el 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: cp850 'APIC habilitado', 0 + boot_APIC_nfound: cp850 'APIC no encontrado', 0 +if preboot_blogesc + boot_tasking: cp850 'Todo configurado - presiona ESC para iniciar',0 +end if + +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:cp850 'Error: instrucción no soportada por el procesador',0 diff --git a/kernel/branches/kolibri-process/detect/biosdisk.inc b/kernel/branches/kolibri-process/detect/biosdisk.inc new file mode 100644 index 000000000..24bbd433a --- /dev/null +++ b/kernel/branches/kolibri-process/detect/biosdisk.inc @@ -0,0 +1,109 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2008-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Detect all BIOS hard drives. +; diamond, 2008 +; Do not include USB mass storages. CleverMouse, 2013 + + xor cx, cx + mov es, cx + mov di, 0x9080 + mov byte [es:di-1], cl + cmp [preboot_biosdisk], 1 + jnz bdde + mov dl, 80h +bdds: + mov ah, 15h + push cx dx di + int 13h + pop di dx cx + jc bddc + 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], 4Ah + mov ah, 48h + int 13h + pop ds + jc bddc2 + cmp word [es:si], 1Eh + jb .noide + cmp word [es:si+1Ah], 0xFFFF + jz .noide + inc byte [es:0x907F] + mov al, dl + stosb + push ds + lds si, [es:si+1Ah] + mov al, [si+6] + and al, 0xF + stosb + mov al, byte [si+4] + shr al, 4 + and ax, 1 + cmp word [si], 1F0h + jz @f + inc ax + inc ax + cmp word [si], 170h + jz @f + or ax, -1 +; mov ax, -1 +@@: + stosw + pop ds + jmp bddc2 +.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 + stosb + dec ax + stosw +; mov al, 0 +; stosb +; mov ax, -1 +; stosw +bddc2: + cmp cl, [es:0x475] + jae bdde +bddc: + inc dl + jnz bdds +bdde: diff --git a/kernel/branches/kolibri-process/detect/biosmem.inc b/kernel/branches/kolibri-process/detect/biosmem.inc new file mode 100644 index 000000000..85cf0d1f4 --- /dev/null +++ b/kernel/branches/kolibri-process/detect/biosmem.inc @@ -0,0 +1,43 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2009-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Query physical memory map from BIOS. +; diamond, 2009 + + push ds +; first call to fn E820 + mov eax, 0xE820 + xor ebx, ebx + mov es, bx + mov ds, bx + mov di, 0x9104 + mov [di-4], ebx ; no blocks yet + mov ecx, 20 + mov edx, 0x534D4150 + int 15h + jc no_E820 + cmp eax, 0x534D4150 + jnz no_E820 +e820_mem_loop: +; cmp byte [di+16], 1 ; ignore non-free areas +; jnz e820_mem_next + inc byte [0x9100] + add di, 20 +e820_mem_next: +; consequent calls to fn E820 + test ebx, ebx + jz e820_test_done + cmp byte [0x9100], 32 + jae e820_test_done + mov eax, 0xE820 + int 15h + jc e820_test_done + jmp e820_mem_loop +no_E820: +; let's hope for mem_test from init.inc +e820_test_done: + pop ds diff --git a/kernel/branches/kolibri-process/detect/dev_fd.inc b/kernel/branches/kolibri-process/detect/dev_fd.inc new file mode 100644 index 000000000..5bfc7ea79 --- /dev/null +++ b/kernel/branches/kolibri-process/detect/dev_fd.inc @@ -0,0 +1,38 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + + +;*************************************************** +; предварительная очистка области таблицы +; поиск и занесение в таблицу приводов FDD +; автор Mario79 +;*************************************************** + xor eax, eax + mov edi, DRIVE_DATA + mov ecx, DRIVE_DATA_SIZE/4 + cld + rep stosd + + mov al, 0x10 + out 0x70, al + mov cx, 0xff +wait_cmos: + dec cx + test cx, cx + jnz wait_cmos + in al, 0x71 + mov [DRIVE_DATA], al + test al, al + jz @f + + stdcall attach_int_handler, 6, FDCInterrupt, 0 + DEBUGF 1, "K : Set IDE IRQ6 return code %x\n", eax + call floppy_init +@@: + diff --git a/kernel/branches/kolibri-process/detect/dev_hdcd.inc b/kernel/branches/kolibri-process/detect/dev_hdcd.inc new file mode 100644 index 000000000..0bc6bb703 --- /dev/null +++ b/kernel/branches/kolibri-process/detect/dev_hdcd.inc @@ -0,0 +1,423 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3881 $ + + +;****************************************************** +; поиск приводов HDD и CD +; автор исходного текста Кулаков Владимир Геннадьевич. +; адаптация и доработка Mario79 +;****************************************************** + +;**************************************************** +;* ПОИСК HDD и CD * +;**************************************************** + cmp [IDEContrProgrammingInterface], 0 + je EndFindHDD + +FindHDD: + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + call FindHDD_3 +; mov ax,[Sector512+176] +; mov [DRIVE_DATA+6],ax +; mov ax,[Sector512+126] +; mov [DRIVE_DATA+8],ax +; mov ax,[Sector512+128] +; mov [DRIVE_DATA+8],ax + mov [DiskNumber], 1 + call FindHDD_3 +; mov al,[Sector512+176] +; mov [DRIVE_DATA+7],al + inc [ChannelNumber] + mov [DiskNumber], 0 + call FindHDD_3 +; mov al,[Sector512+176] +; mov [DRIVE_DATA+8],al + mov [DiskNumber], 1 + call FindHDD_1 +; mov al,[Sector512+176] +; mov [DRIVE_DATA+9],al + + jmp EndFindHDD + +FindHDD_1: + DEBUGF 1, "K : Channel %d ",[ChannelNumber]:2 + DEBUGF 1, "Disk %d\n",[DiskNumber]:1 + call ReadHDD_ID + cmp [DevErrorCode], 0 + jne FindHDD_2 + cmp [Sector512+6], word 16 + ja FindHDD_2 + cmp [Sector512+12], word 255 + ja FindHDD_2 + inc byte [DRIVE_DATA+1] + jmp Print_Device_Name + FindHDD_2: + call DeviceReset + cmp [DevErrorCode], 0 + jne FindHDD_2_2 + call ReadCD_ID + cmp [DevErrorCode], 0 + jne FindHDD_2_2 + inc byte [DRIVE_DATA+1] + inc byte [DRIVE_DATA+1] +Print_Device_Name: + pushad + pushfd + mov esi, Sector512+27*2 + mov edi, dev_name + mov ecx, 20 + cld +@@: + lodsw + xchg ah, al + stosw + loop @b + popfd + popad + DEBUGF 1, "K : Dev: %s \n", dev_name + + xor eax, eax + mov ax, [Sector512+64*2] + DEBUGF 1, "K : PIO mode %x\n", eax + mov ax, [Sector512+63*2] + DEBUGF 1, "K : Multiword DMA mode %x\n", eax + mov ax, [Sector512+88*2] + DEBUGF 1, "K : Ultra DMA mode %x\n", eax + FindHDD_2_2: + ret + +FindHDD_3: + call FindHDD_1 + shl byte [DRIVE_DATA+1], 2 + ret + +; Адрес считываемого сектора в режиме LBA +uglobal +SectorAddress DD ? +dev_name: + rb 41 +endg +;************************************************* +;* ЧТЕНИЕ ИДЕНТИФИКАТОРА ЖЕСТКОГО ДИСКА * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* ChannelNumber - номер канала (1 или 2); * +;* DiskNumber - номер диска на канале (0 или 1). * +;* Идентификационный блок данных считывается * +;* в массив Sector512. * +;************************************************* +ReadHDD_ID: +; Задать режим CHS + mov [ATAAddressMode], 0 +; Послать команду идентификации устройства + mov [ATAFeatures], 0 + mov [ATAHead], 0 + mov [ATACommand], 0ECh + call SendCommandToHDD + cmp [DevErrorCode], 0;проверить код ошибки + jne @@End ;закончить, сохранив код ошибки + mov DX, [ATABasePortAddr] + add DX, 7 ;адрес регистра состояни + mov ecx, 0xffff +@@WaitCompleet: + ; Проверить время выполнения команды + dec ecx +; cmp ecx,0 + jz @@Error1 ;ошибка тайм-аута + ; Проверить готовность + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitCompleet + test AL, 1 ;состояние сигнала ERR + jnz @@Error6 + 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 ;принять блок данных + ret +; Записать код ошибки +@@Error1: + mov [DevErrorCode], 1 + ret +@@Error6: + mov [DevErrorCode], 6 +@@End: + ret + + +iglobal +; Стандартные базовые адреса каналов 1 и 2 +StandardATABases DW 1F0h, 170h +endg +uglobal +; Номер канала +ChannelNumber DW ? +; Номер диска +DiskNumber DB ? +; Базовый адрес группы портов контроллера 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 - ошибка при выполнении +; команды) +DevErrorCode dd ? +endg +;**************************************************** +;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* 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 к приему команды + ; Выбрать нужный диск + mov DX, [ATABasePortAddr] + add DX, 6 ;адрес регистра головок + mov AL, [DiskNumber] + 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 ;ошибка тайм-аута + ; Прочитать регистр состояни + in AL, DX + ; Проверить состояние сигнала BSY + test AL, 80h + jnz @@WaitHDReady + ; Проверить состояние сигнала DRQ + test AL, 08h + jnz @@WaitHDReady +; Загрузить команду в регистры контроллера + cli + mov DX, [ATABasePortAddr] + inc DX ;регистр "особенностей" + mov AL, [ATAFeatures] + out DX, AL + inc DX ;счетчик секторов + mov AL, [ATASectorCount] + out DX, AL + inc DX ;регистр номера сектора + mov AL, [ATASectorNumber] + out DX, AL + inc DX ;номер цилиндра (младший байт) + mov AX, [ATACylinder] + out DX, AL + inc DX ;номер цилиндра (старший байт) + mov AL, AH + out DX, AL + inc DX ;номер головки/номер диска + mov AL, [DiskNumber] + shl AL, 4 + cmp [ATAHead], 0Fh;проверить номер головки + ja @@Err5 + or AL, [ATAHead] + or AL, 10100000b + mov AH, [ATAAddressMode] + shl AH, 6 + or AL, AH + out DX, AL +; Послать команду + mov AL, [ATACommand] + inc DX ;регистр команд + out DX, AL + sti +; Сбросить признак ошибки + mov [DevErrorCode], 0 + ret +; Записать код ошибки +@@Err1: + mov [DevErrorCode], 1 + ret +@@Err2: + mov [DevErrorCode], 2 + ret +@@Err3: + mov [DevErrorCode], 3 + ret +@@Err4: + mov [DevErrorCode], 4 + ret +@@Err5: + mov [DevErrorCode], 5 +; Завершение работы программы + ret + +;************************************************* +;* ЧТЕНИЕ ИДЕНТИФИКАТОРА УСТРОЙСТВА ATAPI * +;* Входные параметры передаются через глобальные * +;* перменные: * +;* ChannelNumber - номер канала; * +;* DiskNumber - номер диска на канале. * +;* Идентификационный блок данных считывается * +;* в массив Sector512. * +;************************************************* +ReadCD_ID: +; Задать режим CHS + mov [ATAAddressMode], 0 +; Послать команду идентификации устройства + mov [ATAFeatures], 0 + mov [ATASectorCount], 0 + mov [ATASectorNumber], 0 + mov [ATACylinder], 0 + mov [ATAHead], 0 + mov [ATACommand], 0A1h + call SendCommandToHDD + cmp [DevErrorCode], 0;проверить код ошибки + jne @@End_1 ;закончить, сохранив код ошибки +; Ожидать готовность данных HDD + mov DX, [ATABasePortAddr] + add DX, 7 ;порт 1х7h + mov ecx, 0xffff +@@WaitCompleet_1: + ; Проверить врем + dec ecx +; cmp ecx,0 + jz @@Error1_1 ;ошибка тайм-аута + ; Проверить готовность + in AL, DX + test AL, 80h ;состояние сигнала BSY + jnz @@WaitCompleet_1 + test AL, 1 ;состояние сигнала ERR + jnz @@Error6_1 + 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;число считываемых слов + rep insw + ret +; Записать код ошибки +@@Error1_1: + mov [DevErrorCode], 1 + ret +@@Error6_1: + mov [DevErrorCode], 6 +@@End_1: + ret + +;************************************************* +;* СБРОС УСТРОЙСТВА * +;* Входные параметры передаются через глобальные * +;* переменные: * +;* 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 ;адрес регистра головок + mov AL, [DiskNumber] + cmp AL, 1 ;проверить номера диска + ja @@Err4_2 + shl AL, 4 + or AL, 10100000b + out DX, AL +; Послать команду "Сброс" + mov AL, 08h + inc DX ;регистр команд + out DX, AL + mov ecx, 0x80000 +@@WaitHDReady_1: + ; Проверить время ожидани + dec ecx +; cmp ecx,0 + je @@Err1_2 ;ошибка тайм-аута + ; Прочитать регистр состояни + in AL, DX + ; Проверить состояние сигнала BSY + test AL, 80h + jnz @@WaitHDReady_1 +; Сбросить признак ошибки + mov [DevErrorCode], 0 + ret +; Обработка ошибок +@@Err1_2: + mov [DevErrorCode], 1 + ret +@@Err3_2: + mov [DevErrorCode], 3 + ret +@@Err4_2: + mov [DevErrorCode], 4 +; Записать код ошибки + ret + +EndFindHDD: + diff --git a/kernel/branches/kolibri-process/detect/disks.inc b/kernel/branches/kolibri-process/detect/disks.inc new file mode 100644 index 000000000..54a67d9ef --- /dev/null +++ b/kernel/branches/kolibri-process/detect/disks.inc @@ -0,0 +1,15 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +include 'dev_fd.inc' +include 'dev_hdcd.inc' +include 'getcache.inc' +include 'sear_par.inc' + diff --git a/kernel/branches/kolibri-process/detect/getcache.inc b/kernel/branches/kolibri-process/detect/getcache.inc new file mode 100644 index 000000000..1893ebcfe --- /dev/null +++ b/kernel/branches/kolibri-process/detect/getcache.inc @@ -0,0 +1,130 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3742 $ + + pusha + + mov eax, [pg_data.pages_free] +; 1/32 + shr eax, 5 +; round off up to 8 pages + shr eax, 3 + shl eax, 3 +; translate pages in butes *4096 + shl eax, 12 +; check a upper size of the cache, no more than 1 Mb on the physical device + cmp eax, 1024*1024 + jbe @f + mov eax, 1024*1024 + jmp .continue +@@: +; check a lower size of the cache, not less than 128 Kb on the physical device + cmp eax, 128*1024 + jae @f + mov eax, 128*1024 +@@: +.continue: + mov [cache_ide0_size], eax + mov [cache_ide1_size], eax + mov [cache_ide2_size], eax + mov [cache_ide3_size], eax + xor eax, eax + mov [hdd_appl_data], 1;al + mov [cd_appl_data], 1 + + test byte [DRIVE_DATA+1], 2 + je .ide2 + mov esi, cache_ide3 + call get_cache_ide +.ide2: + test byte [DRIVE_DATA+1], 8 + je .ide1 + mov esi, cache_ide2 + call get_cache_ide +.ide1: + test byte [DRIVE_DATA+1], 0x20 + je .ide0 + mov esi, cache_ide1 + call get_cache_ide +.ide0: + test byte [DRIVE_DATA+1], 0x80 + je @f + mov esi, cache_ide0 + call get_cache_ide +@@: + jmp end_get_cache + +get_cache_ide: + and [esi+cache_ide0_search_start-cache_ide0], 0 + and [esi+cache_ide0_appl_search_start-cache_ide0], 0 + push ecx + stdcall kernel_alloc, [esi+cache_ide0_size-cache_ide0] + mov [esi+cache_ide0_pointer-cache_ide0], eax + pop ecx + mov edx, eax + mov eax, [esi+cache_ide0_size-cache_ide0] + shr eax, 3 + mov [esi+cache_ide0_system_data_size-cache_ide0], eax + mov ebx, eax + imul eax, 7 + mov [esi+cache_ide0_appl_data_size-cache_ide0], eax + add ebx, edx + mov [esi+cache_ide0_data_pointer-cache_ide0], ebx + +.cd: + push ecx + mov eax, [esi+cache_ide0_system_data_size-cache_ide0] + call calculate_for_cd + add eax, [esi+cache_ide0_pointer-cache_ide0] + mov [esi+cache_ide0_system_data-cache_ide0], eax + mov [esi+cache_ide0_system_sad_size-cache_ide0], ecx + + push edi + mov edi, [esi+cache_ide0_pointer-cache_ide0] + call clear_ide_cache + pop edi + + mov eax, [esi+cache_ide0_appl_data_size-cache_ide0] + call calculate_for_cd + add eax, [esi+cache_ide0_data_pointer-cache_ide0] + mov [esi+cache_ide0_appl_data-cache_ide0], eax + mov [esi+cache_ide0_appl_sad_size-cache_ide0], ecx + + push edi + mov edi, [esi+cache_ide0_data_pointer-cache_ide0] + call clear_ide_cache + pop edi + + pop ecx + ret + +calculate_for_cd: + push eax + mov ebx, eax + shr eax, 11 + shl eax, 3 + sub ebx, eax + shr ebx, 11 + mov ecx, ebx + shl ebx, 11 + pop eax + sub eax, ebx + dec ecx + ret + +clear_ide_cache: + push eax + shl ecx, 1 + xor eax, eax + cld + rep stosd + pop eax + ret + +end_get_cache: + popa diff --git a/kernel/branches/kolibri-process/detect/sear_par.inc b/kernel/branches/kolibri-process/detect/sear_par.inc new file mode 100644 index 000000000..e89d99c89 --- /dev/null +++ b/kernel/branches/kolibri-process/detect/sear_par.inc @@ -0,0 +1,139 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3742 $ + +search_partitions: +; 1. Fill missing parameters in HD_DATA structures. + mov eax, [hd_address_table] + mov [hd0_data.hdbase], eax ;0x1f0 + mov [hd1_data.hdbase], eax + mov eax, [hd_address_table+16] + mov [hd2_data.hdbase], eax + mov [hd3_data.hdbase], eax +; 2. Notify the system about /hd* disks. +; For every existing disk, call ide_disk_add with correct parameters. +; Generate name "hdN" on the stack; this is 4 bytes including terminating zero. +; 2a. /hd0: exists if mask 0x40 in [DRIVE_DATA+1] is set, +; data: hd0_data, +; number of partitions: [DRIVE_DATA+2] + test [DRIVE_DATA+1], byte 0x40 + jz @f + push 'hd0' + mov eax, esp ; name + mov edx, hd0_data + call ide_disk_add + mov [DRIVE_DATA+2], al + pop ecx ; restore the stack +@@: +; 2b. /hd1: exists if mask 0x10 in [DRIVE_DATA+1] is set, +; data: hd1_data, +; number of partitions: [DRIVE_DATA+3] + test [DRIVE_DATA+1], byte 0x10 + jz @f + push 'hd1' + mov eax, esp + mov edx, hd1_data + call ide_disk_add + mov [DRIVE_DATA+3], al + pop ecx +@@: +; 2c. /hd2: exists if mask 4 in [DRIVE_DATA+1] is set, +; data: hd2_data, +; number of partitions: [DRIVE_DATA+4] + test [DRIVE_DATA+1], byte 4 + jz @f + push 'hd2' + mov eax, esp + mov edx, hd2_data + call ide_disk_add + mov [DRIVE_DATA+4], al + pop ecx +@@: +; 2d. /hd3: exists if mask 1 in [DRIVE_DATA+1] is set, +; data: hd3_data, +; number of partitions: [DRIVE_DATA+5] + test [DRIVE_DATA+1], byte 1 + jz @f + push 'hd3' + mov eax, esp + mov edx, hd3_data + call ide_disk_add + mov [DRIVE_DATA+5], al + pop ecx +@@: +; 3. Notify the system about /bd* disks. +; 3a. Check whether there are BIOS disks. If no, skip step 3. + xor esi, esi + cmp esi, [NumBiosDisks] + jz .nobd +; Loop over all disks. + push 0 + push 'bd' +.bdloop: +; 3b. Get the drive number for using in /bd* name. + movzx eax, byte [BiosDisksData+esi*4] + sub al, 80h +; 3c. Convert eax to decimal and store starting with [esp+3]. +; First 2 bytes in [esp] are "bd". + lea edi, [esp+2] +; store digits in the stack, ending with -'0' + push -'0' +@@: + xor edx, edx +iglobal +align 4 +_10 dd 10 +endg + div [_10] + push edx + test eax, eax + jnz @b +; restore digits from the stack, this reverses the order; +; add '0', stop, when zero is reached +@@: + pop eax + add al, '0' + stosb + jnz @b +; 3e. Call the API with userdata = 80h + ecx. + mov eax, esp + lea edx, [esi+80h] + stdcall disk_add, bd_callbacks, eax, edx, 0 + test eax, eax + jz @f + stdcall disk_media_changed, eax, 1 +@@: +; 3f. Continue the loop. + inc esi + cmp esi, [NumBiosDisks] + jnz .bdloop + pop ecx ecx ; restore stack after name +.nobd: + jmp end_search_partitions + +; Helper procedure for search_partitions, adds one IDE disk. +; For compatibility, number of partitions for IDE disks is kept in a separate variable, +; so the procedure returns number of partitions. +; eax -> name, edx -> disk data +proc ide_disk_add + stdcall disk_add, ide_callbacks, eax, edx, 0 + test eax, eax + jz @f + push eax + stdcall disk_media_changed, eax, 1 + pop eax + mov eax, [eax+DISK.NumPartitions] + cmp eax, 255 + jbe @f + mov eax, 255 +@@: + ret +endp + + end_search_partitions: + diff --git a/kernel/branches/kolibri-process/detect/vortex86.inc b/kernel/branches/kolibri-process/detect/vortex86.inc new file mode 100644 index 000000000..2fca0a6d1 --- /dev/null +++ b/kernel/branches/kolibri-process/detect/vortex86.inc @@ -0,0 +1,158 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; 20/11/2013 yogev_ezra: Initial version (Vortex86 SoC type detection) +; 26/11/2013 yogev_ezra: Added CPU speed modifier and MMX support flag detection +; Thanks for help to: dunkaist, eAndrew, hidnplayr, Mario + +$Revision: 4310 $ + +VORTEX86DEBUG = 0 ; For testing in emulators and in non-Vortex86 CPU computers, set this to 1 +VORTEX86DEBUGVALUE = 0x35504d44 ; FAKE port output = used for testing +NORTH_BRIDGE = 0x80000000 ; Base address of Vortex86 PCI North Bridge +SOUTH_BRIDGE = 0x80003800 ; Base address of Vortex86 PCI South Bridge + +; Detect Vortex86 CPU and generate CPU name in string format (PCI address at 93H~90H in Vortex86 North Bridge contains SoC type) +; Available Vortex86 CPU codes taken from Coreboot project. New codes should be added to "Vortex86SoClist" below +; #define DMP_CPUID_SX 0x31504d44 ("DMP1") +; #define DMP_CPUID_DX 0x32504d44 ("DMP2") +; #define DMP_CPUID_MX 0x33504d44 ("DMP3") +; #define DMP_CPUID_DX2 0x34504d44 ("DMP4") +; #define DMP_CPUID_MX_PLUS 0x35504d44 ("DMP5") +; #define DMP_CPUID_EX 0x37504d44 ("DMP7") + +iglobal +Vortex86CPUcode dd ? ; Vortex86 CPU code in HEX format (4 bytes), can be shown as string if converted to ASCII characters +Vortex86CPUid db 0 ; Vortex86 CPU id in integer format (1=Vortex86SX, 2=Vortex86DX, ...) +Vortex86SoCname db 'Vortex86 ',0 ; This variable will hold the full name of Vortex86 SoC +Vortex86SoClist: ; List of Vortex86 CPUs known today. Add new record to this list when new CPU becomes available + db 0x31, 'SX ' ; id=1 + db 0x32, 'DX ' ; id=2 + db 0x33, 'MX ' ; id=3 MMX is available starting from CPU code 'MX' (id=3) + db 0x34, 'DX2' ; id=4 + db 0x35, 'MX+' ; id=5 + db 0x37, 'EX ' ; id=7 +Vortex86SoCnum = ($ - Vortex86SoClist) / 4 ; Calculate the total number of known Vortex86 CPUs +endg + +; When in debug mode, perform SoC detection regardless of the actual CPU vendor (even for vendors other than DMP) +; When in normal (not debug) mode, check the CPU vendor first, and perform SoC detection only if vendor is 'Vortex86 SoC' +if ~ VORTEX86DEBUG + cmp [cpu_vendor], 'Vort' + jnz .Vortex86end ; If the CPU vendor is not 'Vortex86 SoC', skip the SoC detection +end if + + mov eax, NORTH_BRIDGE+0x90 ; 0x80000090 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get the CPU code from Vortex86 SoC North Bridge PCI register (Register Offset: 93H~90H) + +if VORTEX86DEBUG ; When in debug mode, pretend that we received port output equal to "VORTEX86DEBUGVALUE" + mov eax, VORTEX86DEBUGVALUE +end if + + DEBUGF 1, "K : Vortex86 SoC type register (93H~90H) returned 0x" + test eax, eax ; Check whether the port output was '\0' + jz .nullPCIoutput ; In case the result is '\0' (NULL), skip further testing and exit + mov [Vortex86CPUcode], eax ; Save HEX CPU code to Vortex86CPUcode (so it can be used later) + DEBUGF 1, "%x (%s): ", eax, Vortex86CPUcode ; Print the CPU code (as HEX and as string) to debug log + + mov ebx, 0x444d5000 ; Apply Vortex86 CPU code mask (all Vortex86 SoC have ID in form of "0xNN504d44") + bswap eax ; Assumed it is Vortex86 SoC, the highest byte identifies the exact CPU, so move it to the lowest byte + mov bl, al ; Copy SoC type to BL since EAX (that includes AL) is used implicitly in "LODSD" command below + cmp eax, ebx ; Now see whether the 3 higher bytes were "0x504d44" (which means it's Vortex86) + jnz .notVortex86 ; If it's not Vortex86 - go say so and exit + + sub al, 0x30 ; Current Vortex86 CPU codes are in the range of 31h-37h, so convert them to integer (1,2,...) + mov [Vortex86CPUid], al ; Save the CPUid (1=Vortex86SX, 2=Vortex86DX, ..., 7=Vortex86EX, ...) + + mov esi, Vortex86SoClist ; ESI points to the start of Vortex86SoClist (used implicitly in "LODSD" command below) + xor ecx, ecx ; Zero ECX (it is used as counter) + cld ; Clears the DF flag in the EFLAGS register (DF=0 --> String operations increment ESI) +@@: + inc ecx ; Increment our counter + cmp ecx, Vortex86SoCnum ; Check if we iterated Vortex86SoCnum times already (i.e. went over the entire Vortex86SoClist) + ja .unknownVortex86 ; If the entire list was tested and our CPU is not in that list, it is unknown Vortex86 SoC + lodsd ; Load DWORD at address DS:ESI into EAX (puts 1 line from Vortex86SoClist into EAX, then increments ESI) + cmp bl, al ; Check if our CPU matches the current record in the list + jne @b ; No match --> repeat with next record + + shr eax, 8 ; Match found --> drop the SoC type code from Vortex86SoClist name and replace it with \0 + mov dword [Vortex86SoCname+8], eax ; Concatenate it with prefix to receive complete SoC name (\0 is string termination) + + DEBUGF 1, "%s (id=%d)\n", Vortex86SoCname, [Vortex86CPUid]:1 ; Say what we have found (CPU name and id) + jmp .Vortex86 + +.notVortex86: ; In case this register is used by other CPUs for other purpose, it's interesting what it contains + DEBUGF 1, "not a Vortex86 CPU\n" + jmp .Vortex86end + +.unknownVortex86: ; It is Vortex86 CPU, but it's not in the list above + DEBUGF 1, "unknown Vortex86 CPU (id=%d)\n", [Vortex86CPUid]:1 ; Inform the user that the CPU is Vortex86 but name is unknown + +.Vortex86: + mov eax, NORTH_BRIDGE+0x60 ; 0x80000060 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge STRAP Register (Register Offset: 63h~60h) + DEBUGF 1, "K : Vortex86 STRAP Register (63h~60h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xC0 ; 0x800038C0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register (Register Offset: C3h~C0h) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register (C3h~C0h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xCC ; 0x800038CC = PCI Configuration Address Register to read from (8-bit register - accessed as BYTE) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register III (Register Offset: CCh) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register III (CCh) returned 0x%x\n",al + + mov eax, NORTH_BRIDGE+0xA0 ; 0x800000A0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge Host Control Register (Register Offset: A3h~A0h) + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) returned 0x%x: CPU speed is ",eax + mov bl, al ; The lower byte of Vortex86 Host Control Register contains CPU speed modifier and MMX support status + mov bh, al ; Backup the current AL value, so later we can test whether the value has changed + and bl, 00000111b ; CPU speed modifier is stored in bits 0-2. Value=0 means MAX speed, other values - speed reduction + jz .Vortex86CPUspeedMAX ; 0s in bits 0-2: CPU is at MAX speed (no need to modify) + inc ebx ; The actual value is 1 less than 'Divide by' setting (value '001' means 'Divide by 2', etc.) + DEBUGF 1, "reduced (divide by %d).\nK : Vortex86 changing CPU speed to ", bl ; Print the current CPU speed modifier to the log + and al, 11111000b ; At least one of the bits 0-2 contains 1: CPU is at reduced speed. Set bits 0-2 to 0s to change to MAX +.Vortex86CPUspeedMAX: + DEBUGF 1, "MAX\n" ; Now the CPU should be running at MAX speed (don't write the value to PCI port yet) + + cmp [Vortex86CPUid], 3 ; MMX is available starting from CPU code 'MX' (id=3) + jb .skipVortex86MMX ; No MMX support - skip MMX support status detection (for id=1,2) + DEBUGF 1, "K : Vortex86 MMX support status: MMX is " ; Bits 5-6 in Host Control Register contain MMX status + test al, 100000b ; On MMX-capable Vortex86 SoC, Bit5 = is MMX enabled? (1=Yes/0=No) + jnz .Vortex86MMXenabled ; MMX is already enabled (Bit5=1) + DEBUGF 1, "DISABLED - enabling it for this session\n" ; Print to the log that MMX is disabled + or al, 100000b ; Enable MMX support (don't write the value to PCI port yet) + jmp .AfterMMXenabled +.Vortex86MMXenabled: + DEBUGF 1, "ENABLED\n" ; Print to the log that MMX is enabled +.AfterMMXenabled: + DEBUGF 1, "K : Vortex86 MMX report to CPUID: " ; Print to the log what CPUID command knowns about MMX support + test al, 1000000b ; On MMX-capable Vortex86 SoC, Bit6 = report MMX support to CPUID? (1=Yes/0=No) + jnz .Vortex86MMXreported ; MMX is already reported to CPUID (Bit6=1) + DEBUGF 1, "OFF - turning it ON for this session\n" ; Print to the log that MMX will now be reported to CPUID + or al, 1000000b ; Turn on MMX reporting to CPUID (don't write the value to PCI port yet) + jmp .skipVortex86MMX +.Vortex86MMXreported: + DEBUGF 1, "ON\n" ; Print to the log that MMX reporting to CPUID is enabled + +.skipVortex86MMX: + cmp bh, al ; Check whether AL has changed before (if it did, we need to write it back to PCI port) + jz .Vortex86end ; No change - no need to write to the port + out dx, al ; Write the changed data to PCI port + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) new value is 0x%x\n",eax + jmp .Vortex86end + +.Vortex86PCIreg: ; Procedure receives input register value in EAX, and returns the output value also in EAX + mov dx, 0xcf8 ; CF8h = Vortex86 PCI Configuration Address port + out dx, eax ; Send request to PCI address port to retrieve data from this address + mov dl, 0xfc ; CFCh = Vortex86 PCI Configuration Data port + in eax, dx ; Read data from PCI data port + ret + +.nullPCIoutput: ; Emulators and non-Vortex86 CPU computers will usually return \0 in this register + DEBUGF 1, "0 (NULL)\n" + +.Vortex86end: \ No newline at end of file diff --git a/kernel/branches/kolibri-process/encoding.inc b/kernel/branches/kolibri-process/encoding.inc new file mode 100644 index 000000000..c492d6af8 --- /dev/null +++ b/kernel/branches/kolibri-process/encoding.inc @@ -0,0 +1,167 @@ +; 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 +} + +struc cp866 [arg] +{ +common + cp866 arg +} + +; 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 +} + +struc latin1 [arg] +{ +common + latin1 arg +} + +; 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 +} + +struc cp850 [arg] +{ +common + cp850 arg +} diff --git a/kernel/branches/kolibri-process/fdo.inc b/kernel/branches/kolibri-process/fdo.inc new file mode 100644 index 000000000..a9bdc49ae --- /dev/null +++ b/kernel/branches/kolibri-process/fdo.inc @@ -0,0 +1,441 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3614 $ + +_esp equ esp + +; +; Formatted Debug Output (FDO) +; Copyright (c) 2005-2006, mike.dld +; Created: 2005-01-29, Changed: 2006-11-10 +; +; For questions and bug reports, mail to mike.dld@gmail.com +; +; Available format specifiers are: %s, %d, %u, %x (with partial width support) +; + +; to be defined: +; __DEBUG__ equ 1 +; __DEBUG_LEVEL__ equ 5 + +macro debug_func name { + if used name + name@of@func equ name +} + +macro debug_beginf { + align 4 + name@of@func: +} + +debug_endf fix end if + +macro DEBUGS _sign,[_str] { + common + local tp + tp equ 0 + match _arg:_num,_str \{ + DEBUGS_N _sign,_num,_arg + tp equ 1 + \} + match =0 _arg,tp _str \{ + DEBUGS_N _sign,,_arg + \} +} + +macro DEBUGS_N _sign,_num,[_str] { + common + pushf + pushad + local ..str,..label,is_str + is_str = 0 + forward + if _str eqtype '' + is_str = 1 + end if + common + if is_str = 1 + jmp ..label + ..str db _str,0 + ..label: + mov edx, ..str + else +esp equ esp+4*8+4 + mov edx, _str +esp equ _esp + end if + if ~_num eq + if _num eqtype eax + if _num in + mov esi, _num + else if ~_num eq esi + movzx esi, _num + end if + else if _num eqtype 0 + mov esi, _num + else + local tp + tp equ 0 + match [_arg],_num \{ + mov esi, dword[_arg] + tp equ 1 + \} + match =0 =dword[_arg],tp _num \{ + mov esi, dword[_arg] + tp equ 1 + \} + match =0 =word[_arg],tp _num \{ + movzx esi, word[_arg] + tp equ 1 + \} + match =0 =byte[_arg],tp _num \{ + movzx esi, byte[_arg] + tp equ 1 + \} + match =0,tp \{ + 'Error: specified string width is incorrect' + \} + end if + else + mov esi, 0x7FFFFFFF + end if + call fdo_debug_outstr + popad + popf +} + +macro DEBUGD _sign,_dec { + local tp + tp equ 0 + match _arg:_num,_dec \{ + DEBUGD_N _sign,_num,_arg + tp equ 1 + \} + match =0 _arg,tp _dec \{ + DEBUGD_N _sign,,_arg + \} +} + +macro DEBUGD_N _sign,_num,_dec { + pushf + pushad + if (~_num eq) + if (_dec eqtype eax | _dec eqtype 0) + 'Error: precision allowed only for in-memory variables' + end if + if (~_num in <1,2,4>) + if _sign + 'Error: 1, 2 and 4 are only allowed for precision in %d' + else + 'Error: 1, 2 and 4 are only allowed for precision in %u' + end if + end if + end if + if _dec eqtype eax + if _dec in + mov eax, _dec + else if ~_dec eq eax + if _sign = 1 + movsx eax, _dec + else + movzx eax, _dec + end if + end if + else if _dec eqtype 0 + mov eax, _dec + else +; add esp,4*8+4 +esp equ esp+4*8+4 + if _num eq + mov eax, dword _dec + else if _num = 1 + if _sign = 1 + movsx eax, byte _dec + else + movzx eax, byte _dec + end if + else if _num = 2 + if _sign = 1 + movsx eax, word _dec + else + movzx eax, word _dec + end if + else + mov eax, dword _dec + end if +esp equ _esp +; sub esp,4*8+4 + end if + mov cl, _sign + call fdo_debug_outdec + popad + popf +} + +macro DEBUGH _sign,_hex { + local tp + tp equ 0 + match _arg:_num,_hex \{ + DEBUGH_N _sign,_num,_arg + tp equ 1 + \} + match =0 _arg,tp _hex \{ + DEBUGH_N _sign,,_arg + \} +} + +macro DEBUGH_N _sign,_num,_hex { + pushf + pushad + if (~_num eq) & (~_num in <1,2,3,4,5,6,7,8>) + 'Error: 1..8 are only allowed for precision in %x' + end if + if _hex eqtype eax + if _hex in + if ~_hex eq eax + mov eax, _hex + end if + mov edx, 8 + else if _hex in + if ~_hex eq ax + movzx eax, _hex + end if + if (_num eq) + mov edx, 4 + end if + else if _hex in + if ~_hex eq al + movzx eax, _hex + end if + if (_num eq) + mov edx, 2 + end if + end if + else if _hex eqtype 0 + mov eax, _hex + else +; add esp,4*8+4 +esp equ esp+4*8+4 + mov eax, dword _hex +esp equ _esp +; sub esp,4*8+4 + end if + if ~_num eq + mov edx, _num + else + if ~_hex eqtype eax + mov edx, 8 + end if + end if + call fdo_debug_outhex + popad + popf +} + +;----------------------------------------------------------------------------- + +debug_func fdo_debug_outchar +debug_beginf + pushad + movzx ecx, al + mov ebx, 1 + call sys_msg_board + popad + ret +debug_endf + +debug_func fdo_debug_outstr +debug_beginf + mov ebx, 1 + .l1: + dec esi + js .l2 + movzx ecx, byte[edx] + or cl, cl + jz .l2 + call sys_msg_board + inc edx + jmp .l1 + .l2: + ret +debug_endf + +debug_func fdo_debug_outdec +debug_beginf + or cl, cl + jz @f + or eax, eax + jns @f + neg eax + push eax + mov al, '-' + call fdo_debug_outchar + pop eax + @@: + movi ecx, 10 + push -'0' + .l1: + xor edx, edx + div ecx + push edx + test eax, eax + jnz .l1 + .l2: + pop eax + add al, '0' + jz .l3 + call fdo_debug_outchar + jmp .l2 + .l3: + ret +debug_endf + +debug_func fdo_debug_outhex + __fdo_hexdigits db '0123456789ABCDEF' +debug_beginf + mov cl, dl + neg cl + add cl, 8 + shl cl, 2 + rol eax, cl + .l1: + rol eax, 4 + push eax + and eax, 0x0000000F + mov al, [__fdo_hexdigits+eax] + call fdo_debug_outchar + pop eax + dec edx + jnz .l1 + ret +debug_endf + +;----------------------------------------------------------------------------- + +macro DEBUGF _level,_format,[_arg] { + common + if __DEBUG__ = 1 & _level >= __DEBUG_LEVEL__ + local ..f1,f2,a1,a2,c1,c2,c3,..lbl + _debug_str_ equ __debug_str_ # a1 + a1 = 0 + c2 = 0 + c3 = 0 + f2 = 0 + repeat ..lbl-..f1 + virtual at 0 + db _format,0,0 + load c1 word from %-1 + end virtual + if c1 = '%s' + virtual at 0 + db _format,0,0 + store word 0 at %-1 + load c1 from f2-c2 + end virtual + if c1 <> 0 + DEBUGS 0,_debug_str_+f2-c2 + end if + c2 = c2 + 1 + f2 = %+1 + DEBUGF_HELPER S,a1,0,_arg + else if c1 = '%x' + virtual at 0 + db _format,0,0 + store word 0 at %-1 + load c1 from f2-c2 + end virtual + if c1 <> 0 + DEBUGS 0,_debug_str_+f2-c2 + end if + c2 = c2 + 1 + f2 = %+1 + DEBUGF_HELPER H,a1,0,_arg + else if c1 = '%d' | c1 = '%u' + local c4 + if c1 = '%d' + c4 = 1 + else + c4 = 0 + end if + virtual at 0 + db _format,0,0 + store word 0 at %-1 + load c1 from f2-c2 + end virtual + if c1 <> 0 + DEBUGS 0,_debug_str_+f2-c2 + end if + c2 = c2 + 1 + f2 = %+1 + DEBUGF_HELPER D,a1,c4,_arg + else if c1 = '\n' + c3 = c3 + 1 + end if + end repeat + virtual at 0 + db _format,0,0 + load c1 from f2-c2 + end virtual + if (c1<>0)&(f2<>..lbl-..f1-1) + DEBUGS 0,_debug_str_+f2-c2 + end if + virtual at 0 + ..f1 db _format,0 + ..lbl: + __debug_strings equ __debug_strings,_debug_str_,<_format>,..lbl-..f1-1-c2-c3 + end virtual + end if +} + +macro __include_debug_strings dummy,[_id,_fmt,_len] { + common + local c1,a1,a2 + forward + if defined _len & ~_len eq + _id: + a1 = 0 + a2 = 0 + repeat _len + virtual at 0 + db _fmt,0,0 + load c1 word from %+a2-1 + end virtual + if (c1='%s')|(c1='%x')|(c1='%d')|(c1='%u') + db 0 + a2 = a2 + 1 + else if (c1='\n') + dw $0A0D + a1 = a1 + 1 + a2 = a2 + 1 + else + db c1 and 0x0FF + end if + end repeat + db 0 + end if +} + +macro DEBUGF_HELPER _letter,_num,_sign,[_arg] { + common + local num + num = 0 + forward + if num = _num + DEBUG#_letter _sign,_arg + end if + num = num+1 + common + _num = _num+1 +} + +macro include_debug_strings { + if __DEBUG__ = 1 + match dbg_str,__debug_strings \{ + __include_debug_strings dbg_str + \} + end if +} diff --git a/kernel/branches/kolibri-process/fs/ext2/blocks.inc b/kernel/branches/kolibri-process/fs/ext2/blocks.inc new file mode 100644 index 000000000..f9d11aacb --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ext2/blocks.inc @@ -0,0 +1,409 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 block handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Write ext2 block from memory to disk. +; Input: eax = i_block (block number in ext2 terms); +; ebx = buffer address +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_write: + push edx ebx ecx + + mov edx, fs_write32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Read ext2 block from disk to memory. +; Input: eax = i_block (block number in ext2 terms); +; ebx = address of where to read block +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_read: + push edx ebx ecx + + mov edx, fs_read32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Modify ext2 block. +; Input: eax = i_block (block number in ext2 terms); +; ebx = I/O buffer address; +; edx = fs_read/write32_sys +; ebp = pointer to EXTFS +; edx, ebx, ecx on stack. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_modify: + ; Get block number in hard-disk terms in eax. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + mov ecx, eax + push [ebp + EXTFS.count_block_in_block] + + @@: + mov eax, ecx + call edx + test eax, eax + jnz .fail + + inc ecx + add ebx, 512 + dec dword[esp] + jnz @B + + xor eax, eax + @@: + pop ecx + pop ecx ebx edx + ret + + .fail: + mov eax, ERROR_DEVICE + jmp @B + +;--------------------------------------------------------------------- +; Zeroes a block. +; Input: ebx = block ID. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_block_zero: + push ebx + + mov eax, ebx + mov ebx, [ebp + EXTFS.ext2_temp_block] + + call ext2_block_read + test eax, eax + jnz .return + + push edi ecx + xor eax, eax + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_temp_block] + rep stosb + pop ecx edi + + mov eax, [esp] + call ext2_block_write + + .return: + pop ebx + ret + +;--------------------------------------------------------------------- +; Allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_count] + push EXT2_BLOCK_GROUP_DESC.free_blocks_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + push ebx + + push ext2_bg_read_blk_bitmap + + call ext2_resource_alloc + + ret + +;--------------------------------------------------------------------- +; Zero-allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_calloc: + call ext2_block_alloc + test eax, eax + jnz @F + + call ext2_block_zero + @@: + ret + +;--------------------------------------------------------------------- +; Frees a block. +; Input: eax = block ID. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_block_free: + push edi ecx + + mov edi, ext2_bg_read_blk_bitmap + xor ecx, ecx + call ext2_resource_free + + pop ecx edi + ret + +;--------------------------------------------------------------------- +; Find parent from file path in block. +; Input: esi = file path. +; ebx = pointer to directory block. +; ebp = pointer to EXTFS structure. +; Output: esi = name without parent, or not changed. +; ebx = directory record matched. +;--------------------------------------------------------------------- +ext2_block_find_parent: + sub esp, 256 ; Space for EXT2 filename. + mov edx, ebx + add edx, [ebp + EXTFS.block_size] ; Save block end. + + .start_rec: + cmp [ebx + EXT2_DIR_STRUC.inode], 0 + jz .next_rec + + mov edi, esp + push esi + movzx ecx, [ebx + EXT2_DIR_STRUC.name_len] + lea esi, [ebx + EXT2_DIR_STRUC.name] + call utf8_to_cp866 + + mov ecx, edi + lea edi, [esp + 4] + sub ecx, edi ; Number of bytes in resulting string. + + mov esi, [esp] + + ; esi: original file path. + ; edi: converted string stored on stack. + ; ecx: size of converted string. + @@: + ; If no bytes left in resulting string, test it. + jecxz .test_find + dec ecx + + lodsb + call char_toupper + + mov ah, [edi] + inc edi + xchg al, ah + call char_toupper + + ; If both are same, check next byte. + cmp al, ah + je @B + @@: ; Doesn't match. + pop esi + + .next_rec: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, eax ; Go to next record. + cmp ebx, edx ; Check if this is the end. + jb .start_rec + + add esp, 256 + ret + + .test_find: + cmp byte [esi], 0 + je .ret ; The end reached. + cmp byte [esi], '/' ; If not end of directory name, not matched. + jne @B + inc esi + + .ret: + add esp, 256 + 4 + ret + +;--------------------------------------------------------------------- +; Finds free space in a directory block, modifying last entry appropriately. +; Input: ebp = pointer to EXTFS. +; ecx = size of free space required. +; [EXTFS.ext2_temp_block] contains the block relevant. +; Output: edi = free entry. +; rec_len of free entry is set. +; eax = error code; if the block doesn't link to the next one, this is 0x00000001 on failure. +; ; else, 0xFFFFFFFF. +;--------------------------------------------------------------------- +ext2_block_find_fspace: + push ebx edx + + mov edi, [ebp + EXTFS.ext2_temp_block] + mov edx, edi + add edx, [ebp + EXTFS.block_size] + + @@: + movzx eax, [edi + EXT2_DIR_STRUC.rec_len] + test eax, eax + jz .zero_len + + cmp [edi + EXT2_DIR_STRUC.inode], 0 + je .unused_entry + + ; It's a used entry, so see if we can fit it between current one and next. + ; Subtract the size used by the name and the structure from rec_len. + movzx ebx, [edi + EXT2_DIR_STRUC.name_len] + add ebx, 8 + 3 + and ebx, 0xfffffffc ; Align it on the next 4-byte boundary. + + sub eax, ebx + add edi, ebx + cmp eax, ecx + jb .next_iter + + sub edi, ebx + mov [edi + EXT2_DIR_STRUC.rec_len], bx ; Make previous entry point to us. + add edi, ebx + + mov [edi + EXT2_DIR_STRUC.rec_len], ax ; Make current entry point to next one. + jmp .found + + .unused_entry: + ; It's an unused inode. + cmp eax, ecx + jge .found + + .next_iter: + add edi, eax + cmp edi, edx + jb @B + + .not_found: + xor eax, eax + not eax + jmp .ret + + ; Zero length entry means we have the rest of the block for us. + .zero_len: + mov eax, edx + sub eax, edi + + ; Point to next block. + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + cmp eax, ecx + jge .fits + + mov [edi + EXT2_DIR_STRUC.inode], 0 + + ; It doesn't fit, but the block doesn't link to the next block. + xor eax, eax + inc eax + jmp .ret + + .fits: + mov [edi + EXT2_DIR_STRUC.rec_len], cx + + .found: + xor eax, eax + + .ret: + pop edx ebx + ret + +;--------------------------------------------------------------------- +; Gets the block group's descriptor. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; [EXTFS.ext2_temp_block] contains relevant block. +; ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +ext2_bg_read_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, ebx + + .return: + pop ebx edx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Writes a block group's descriptor. +; Input: eax = block group. +; [EXTFS.ext2_temp_data] contains the block relevant. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_bg_write_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + + .return: + pop ebx edx + ret + +;--------------------------------------------------------------------- +; Gets the block group's block bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = block bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_blk_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.block_bitmap] ; Block number of block group bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Updates superblock, plus backups. +; Input: ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_sb_update: + push ebx + + mov eax, 2 + lea ebx, [ebp + EXTFS.superblock] + call fs_write32_sys + + pop ebx + ret diff --git a/kernel/branches/kolibri-process/fs/ext2/ext2.asm b/kernel/branches/kolibri-process/fs/ext2/ext2.asm new file mode 100644 index 000000000..a8f518ae6 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ext2/ext2.asm @@ -0,0 +1,1718 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 initialization, plus syscall handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +include 'ext2.inc' +include 'blocks.inc' +include 'inode.inc' +include 'resource.inc' + +iglobal +align 4 +ext2_user_functions: + dd ext2_free + dd (ext2_user_functions_end - ext2_user_functions - 4) / 4 + dd ext2_Read + dd ext2_ReadFolder + dd ext2_Rewrite + dd ext2_Write + dd ext2_SetFileEnd + dd ext2_GetFileInfo + dd ext2_SetFileInfo + dd 0 + dd ext2_Delete + dd ext2_CreateFolder +ext2_user_functions_end: +endg + +;--------------------------------------------------------------------- +; Locks up an ext2 partition. +; Input: ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_lock + lea ecx, [ebp + EXTFS.lock] + jmp mutex_lock +endp + +;--------------------------------------------------------------------- +; Unlocks up an ext2 partition. +; Input: ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_unlock + lea ecx, [ebp + EXTFS.lock] + jmp mutex_unlock +endp + +;--------------------------------------------------------------------- +; Check if it's a valid ext* superblock. +; Input: ebp: first three fields of PARTITION structure. +; ebx + 512: points to 512-bytes buffer that can be used for anything. +; Output: eax: clear if can't create partition; set to EXTFS otherwise. +;--------------------------------------------------------------------- +proc ext2_create_partition + push ebx + + mov eax, 2 ; Superblock starts at 1024-bytes. + add ebx, 512 ; Get pointer to fs-specific buffer. + call fs_read32_sys + test eax, eax + jnz .fail + + ; Allowed 1KiB, 2KiB, 4KiB, 8KiB. + cmp [ebx + EXT2_SB_STRUC.log_block_size], 3 + ja .fail + + cmp [ebx + EXT2_SB_STRUC.magic], EXT2_SUPER_MAGIC + jne .fail + + cmp [ebx + EXT2_SB_STRUC.state], EXT2_VALID_FS + jne .fail + + ; Can't have no inodes per group. + cmp [ebx + EXT2_SB_STRUC.inodes_per_group], 0 + je .fail + + ; If incompatible features required, unusable superblock. + mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] + test eax, not EXT4_FEATURE_INCOMPAT_SUPP + jz .setup + + .fail: + ; Not a (valid/usable) EXT2 superblock. + pop ebx + xor eax, eax + ret + + .setup: + movi eax, sizeof.EXTFS + call malloc + test eax, eax + jz ext2_create_partition.fail + + ; Store the first sector field. + mov ecx, dword[ebp + PARTITION.FirstSector] + mov dword[eax + EXTFS.FirstSector], ecx + mov ecx, dword [ebp + PARTITION.FirstSector+4] + mov dword [eax + EXTFS.FirstSector+4], ecx + + ; The length field. + mov ecx, dword[ebp + PARTITION.Length] + mov dword[eax + EXTFS.Length], ecx + mov ecx, dword[ebp + PARTITION.Length+4] + mov dword[eax + EXTFS.Length+4], ecx + + ; The disk field. + mov ecx, [ebp + PARTITION.Disk] + mov [eax + EXTFS.Disk], ecx + + mov [eax + EXTFS.FSUserFunctions], ext2_user_functions + + push ebp esi edi + + mov ebp, eax + lea ecx, [eax + EXTFS.lock] + call mutex_init + + ; Copy superblock from buffer to reserved memory. + mov esi, ebx + lea edi, [ebp + EXTFS.superblock] + mov ecx, 512/4 + rep movsd + + ; Get total groups. + mov eax, [ebx + EXT2_SB_STRUC.blocks_count] + sub eax, [ebx + EXT2_SB_STRUC.first_data_block] + dec eax + xor edx, edx + div [ebx + EXT2_SB_STRUC.blocks_per_group] + inc eax + mov [ebp + EXTFS.groups_count], eax + + ; Get log(block_size), such that 1,2,3,4 equ 1KiB,2KiB,4KiB,8KiB. + mov ecx, [ebx + EXT2_SB_STRUC.log_block_size] + inc ecx + mov [ebp + EXTFS.log_block_size], ecx + + ; 512-byte blocks in ext2 blocks. + mov eax, 1 + shl eax, cl + mov [ebp + EXTFS.count_block_in_block], eax + + ; Get block_size/4 (we'll find square later). + shl eax, 7 + mov [ebp + EXTFS.count_pointer_in_block], eax + mov edx, eax + + ; Get block size. + shl eax, 2 + mov [ebp + EXTFS.block_size], eax + + ; Save block size for 2 kernel_alloc calls. + push eax eax + + mov eax, edx + mul edx + mov [ebp + EXTFS.count_pointer_in_block_square], eax + + ; Have temporary block storage for get_inode procedure, and one for global procedure. + KERNEL_ALLOC [ebp + EXTFS.ext2_save_block], .error + KERNEL_ALLOC [ebp + EXTFS.ext2_temp_block], .error + + mov [ebp + EXTFS.partition_flags], 0x00000000 + mov eax, [ebx + EXT2_SB_STRUC.feature_ro_compat] + and eax, not EXT2_FEATURE_RO_COMPAT_SUPP + jnz .read_only + + mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] + and eax, EXT4_FEATURE_INCOMPAT_W_NOT_SUPP + jz @F + + .read_only: + ; Mark as read-only. + or [ebp + EXTFS.partition_flags], EXT2_RO + @@: + mov ecx, [ebx + EXT2_SB_STRUC.blocks_per_group] + mov [ebp + EXTFS.blocks_per_group], ecx + + movzx ecx, word[ebx + EXT2_SB_STRUC.inode_size] + mov [ebp + EXTFS.inode_size], ecx + + ; Allocate for three inodes (loop would be overkill). + push ecx ecx ecx + + KERNEL_ALLOC [ebp + EXTFS.ext2_save_inode], .error + KERNEL_ALLOC [ebp + EXTFS.ext2_temp_inode], .error + KERNEL_ALLOC [ebp + EXTFS.root_inode], .error + + ; Read root inode. + mov ebx, eax + mov eax, EXT2_ROOT_INO + call ext2_inode_read + + test eax, eax + jnz .error + + ;call ext2_sb_update + ; Sync the disk. + ;mov esi, [ebp + PARTITION.Disk] + ;call disk_sync ; eax contains error code, if any. + + mov eax, ebp ; Return pointer to EXTFS. + pop edi esi ebp ebx + ret + + ; Error in setting up. + .error: + ; Free save block. + KERNEL_FREE [ebp + EXTFS.ext2_save_block], .fail + + ; Temporary block. + KERNEL_FREE [ebp + EXTFS.ext2_temp_block], .fail + + ; All inodes. + KERNEL_FREE [ebp + EXTFS.ext2_save_inode], .fail + KERNEL_FREE [ebp + EXTFS.ext2_temp_inode], .fail + KERNEL_FREE [ebp + EXTFS.root_inode], .fail + + mov eax, ebp + call free + + jmp .fail +endp + +; FUNCTIONS PROVIDED BY SYSCALLS. + +;--------------------------------------------------------------------- +; Frees up all ext2 structures. +; Input: eax = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_free + push ebp + + xchg ebp, eax + stdcall kernel_free, [ebp+EXTFS.ext2_save_block] + stdcall kernel_free, [ebp+EXTFS.ext2_temp_block] + stdcall kernel_free, [ebp+EXTFS.ext2_save_inode] + stdcall kernel_free, [ebp+EXTFS.ext2_temp_inode] + stdcall kernel_free, [ebp+EXTFS.root_inode] + + xchg ebp, eax + call free + + pop ebp + ret +endp + +;--------------------------------------------------------------------- +; Read disk folder. +; Input: ebp = pointer to EXTFS structure. +; esi + [esp + 4] = file name. +; ebx = pointer to parameters from sysfunc 70. +; Output: ebx = blocks read (or 0xFFFFFFFF, folder not found) +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_ReadFolder: + ;DEBUGF 1, "Reading folder.\n" + call ext2_lock + cmp byte [esi], 0 + jz .root_folder + + push ebx + stdcall ext2_inode_find, [esp + 4 + 4] ; Get inode. + pop ebx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jnz .error_ret + + ; If not a directory, then return with error. + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jz .error_not_found + jmp @F + + .root_folder: + mov esi, [ebp + EXTFS.root_inode] + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jz .error_root + + ; Copy the inode. + mov edi, [ebp + EXTFS.ext2_save_inode] + mov ecx, [ebp + EXTFS.inode_size] + shr ecx, 2 + + push edi + rep movsd + pop esi + + @@: + cmp [esi + EXT2_INODE_STRUC.i_size], 0 ; Folder is empty. + je .error_empty_dir + + mov edx, [ebx + 16] + push edx ; Result address [edi + 28]. + push 0 ; End of the current block in folder [edi + 24] + push dword[ebx + 12] ; Blocks to read [edi + 20] + push dword[ebx + 4] ; The first wanted file [edi + 16] + push dword[ebx + 8] ; Flags [edi + 12] + push 0 ; Read files [edi + 8] + push 0 ; Files in folder [edi + 4] + push 0 ; Number of blocks read in dir (and current block index) [edi] + + ; Fill header with zeroes. + mov edi, edx + mov ecx, 32/4 + rep stosd + + mov edi, esp ; edi = pointer to local variables. + add edx, 32 ; edx = mem to return. + + xor ecx, ecx ; Get number of first block. + call ext2_inode_get_block + test eax, eax + jnz .error_get_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read ; Read the block. + test eax, eax + jnz .error_get_block + + mov eax, ebx ; esi: current directory record + add eax, [ebp + EXTFS.block_size] + + mov [edi + 24], eax + + mov ecx, [edi + 16] ; ecx = first wanted (flags ommited) + + .find_wanted_start: + jecxz .find_wanted_end + + .find_wanted_cycle: + cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; Don't count unused inode in total files. + jz @F + + inc dword [edi + 4] ; EXT2 files in folder. + dec ecx + @@: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + + cmp eax, 12 ; Minimum record length. + jb .error_bad_len + test eax, 0x3 ; Record length must be divisible by four. + jnz .error_bad_len + + sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract "processed record" length directly from inode. + add ebx, eax ; Go to next record. + cmp ebx, [edi + 24] ; If not reached the next block, continue. + jb .find_wanted_start + + push .find_wanted_start + .end_block: ; Get the next block. + cmp [esi + EXT2_INODE_STRUC.i_size], 0 + jle .end_dir + + inc dword [edi] ; Number of blocks read. + + ; Read the next block. + push ecx + mov ecx, [edi] + call ext2_inode_get_block + test eax, eax + jnz .error_get_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .error_get_block + pop ecx + + mov eax, ebx + add eax, [ebp + EXTFS.block_size] + mov [edi + 24], eax ; Update the end of the current block variable. + ret + + .wanted_end: + loop .find_wanted_cycle ; Skip files till we reach wanted one. + + ; First requisite file. + .find_wanted_end: + mov ecx, [edi + 20] + .wanted_start: ; Look for first_wanted + count. + jecxz .wanted_end + + cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; if (inode == 0): not used; + jz .empty_rec + + ; Increment "files in dir" and "read files" count. + inc dword [edi + 8] + inc dword [edi + 4] + + push edi ecx + mov edi, edx ; Zero out till the name field. + xor eax, eax + mov ecx, 40 / 4 + rep stosd + pop ecx edi + + push ebx edi edx + mov eax, [ebx + EXT2_DIR_STRUC.inode] ; Get the child inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error_read_subinode + + lea edi, [edx + 8] + + mov eax, [ebx + EXT2_INODE_STRUC.i_ctime] ; Convert time in NTFS format. + xor edx, edx + add eax, 3054539008 ; (369 * 365 + 89) * 24 * 3600 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [ebx + EXT2_INODE_STRUC.i_atime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [ebx + EXT2_INODE_STRUC.i_mtime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + pop edx + test [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR ; If folder, don't report size. + jnz @F + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] ; Low size + stosd + mov eax, [ebx + EXT2_INODE_STRUC.i_dir_acl] ; High size + stosd + + xor dword [edx], FS_FT_DIR ; Mark as file. + @@: + xor dword [edx], FS_FT_DIR ; Mark as directory. + + ; Copy name after converting from UTF-8 to CP866. + push ecx esi + mov esi, [esp + 12] + movzx ecx, [esi + EXT2_DIR_STRUC.name_len] + lea edi, [edx + 40] + lea esi, [esi + EXT2_DIR_STRUC.name] + call utf8_to_cp866 + and byte [edi], 0 + pop esi ecx edi ebx + + cmp byte [edx + 40], '.' ; If it begins with ".", mark it as hidden. + jne @F + or dword [edx], FS_FT_HIDDEN + + @@: + add edx, 40 + 264 ; Go to next record. + dec ecx + .empty_rec: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + + cmp eax, 12 ; Illegal length. + jb .error_bad_len + test eax, 0x3 ; Not a multiple of four. + jnz .error_bad_len + + sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract directly from the inode. + add ebx, eax + cmp ebx, [edi + 24] ; Are we at the end of the block? + jb .wanted_start + + push .wanted_start + jmp .end_block + + .end_dir: ; End of the directory. + call ext2_unlock + mov edx, [edi + 28] ; Address of where to return data. + mov ebx, [edi + 8] ; EXT2_read_in_folder + mov ecx, [edi + 4] ; EXT2_files_in_folder + mov dword [edx], 1 ; Version + mov [edx + 4], ebx + mov [edx + 8], ecx + + lea esp, [edi + 32] + + xor eax, eax ; Reserved in current implementation. + lea edi, [edx + 12] + mov ecx, 20 / 4 + rep stosd + + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + + .error_bad_len: + mov eax, ERROR_FS_FAIL + + .error_read_subinode: + .error_get_block: + ; Fix the stack. + lea esp, [edi + 32] + + .error_ret: + or ebx, -1 + push eax + call ext2_unlock + pop eax + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + + .error_empty_dir: ; inode of folder without blocks. + .error_root: ; Root has to be a folder. + mov eax, ERROR_FS_FAIL + jmp .error_ret + + .error_not_found: ; Directory not found. + mov eax, ERROR_FILE_NOT_FOUND + jmp .error_ret + +;--------------------------------------------------------------------- +; Read file from the hard disk. +; Input: esi + [esp + 4] = points to file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: ebx = bytes read (0xFFFFFFFF -> file not found) +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_Read: + ;DEBUGF 1, "Attempting read.\n" + call ext2_lock + cmp byte [esi], 0 + jnz @F + + .this_is_nofile: + call ext2_unlock + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret + + @@: + push ebx + stdcall ext2_inode_find, [esp + 4 + 4] + pop ebx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jz @F + + call ext2_unlock + or ebx, -1 + mov eax, ERROR_FILE_NOT_FOUND + ret + + @@: + mov ax, [esi + EXT2_INODE_STRUC.i_mode] + and ax, EXT2_S_IFMT ; Leave the file format in AX. + + ; Check if file. + cmp ax, EXT2_S_IFREG + jne .this_is_nofile + + mov edi, [ebx + 16] + mov ecx, [ebx + 12] + + mov eax, [ebx + 4] + mov edx, [ebx + 8] ; edx:eax = start byte number. + + ; Check if file is big enough for us. + cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx + ja .size_greater + jb .size_less + + cmp [esi + EXT2_INODE_STRUC.i_size], eax + ja .size_greater + + .size_less: + call ext2_unlock + xor ebx, ebx + mov eax, ERROR_END_OF_FILE + ret + + @@: + .size_greater: + add eax, ecx ; Get last byte. + adc edx, 0 + + ; Check if we've to read whole file, or till requested. + cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx + ja .read_till_requested + jb .read_whole_file + cmp [esi + EXT2_INODE_STRUC.i_size], eax + jae .read_till_requested + + .read_whole_file: + push 1 ; Read till the end of file. + mov ecx, [esi + EXT2_INODE_STRUC.i_size] + sub ecx, [ebx + 4] ; To read = (size - starting byte) + jmp @F + + .read_till_requested: + push 0 ; Read as much as requested. + + @@: + ; ecx = bytes to read. + ; edi = return memory + ; [esi] = starting byte. + + push ecx ; Number of bytes to read. + + ; Get part of the first block. + mov edx, [ebx + 8] + mov eax, [ebx + 4] + div [ebp + EXTFS.block_size] + + push eax ; Save block counter to stack. + + push ecx + mov ecx, eax + call ext2_inode_get_block + test eax, eax + jnz .error_at_first_block + + mov ebx, [ebp + EXTFS.ext2_save_block] + mov eax, ecx + call ext2_block_read + test eax, eax + jnz .error_at_first_block + + pop ecx + ; Get index inside block. + add ebx, edx + + neg edx + add edx, [ebp + EXTFS.block_size] ; Get number of bytes in this block. + + ; If it's smaller than total bytes to read, then only one block. + cmp ecx, edx + jbe .only_one_block + + mov eax, ecx + sub eax, edx + mov ecx, edx + + push esi + mov esi, ebx + rep movsb ; Copy part of 1st block. + pop esi + + ; eax -> bytes to read. + .calc_blocks_count: + mov ebx, edi ; Read the block in ebx. + xor edx, edx + div [ebp + EXTFS.block_size] ; Get number of bytes in last block in edx. + mov edi, eax ; Get number of blocks in edi. + + @@: + ; Test if all blocks are done. + test edi, edi + jz .finish_block + + inc dword [esp] + mov ecx, [esp] + call ext2_inode_get_block + + test eax, eax + jnz .error_at_read_cycle + + mov eax, ecx ; ebx already contains desired values. + call ext2_block_read + + test eax, eax + jnz .error_at_read_cycle + + add ebx, [ebp + EXTFS.block_size] + + dec edi + jmp @B + + ; In edx -- number of bytes in the last block. + .finish_block: + test edx, edx + jz .end_read + + pop ecx ; Pop block counter in ECX. + inc ecx + call ext2_inode_get_block + + test eax, eax + jnz .error_at_finish_block + + mov edi, ebx + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + + test eax, eax + jnz .error_at_finish_block + + mov ecx, edx + mov esi, ebx + rep movsb ; Copy last piece of block. + jmp @F + + .end_read: + pop ecx ; Pop block counter in ECX. + @@: + pop ebx ; Number of bytes read. + call ext2_unlock + pop eax ; If we were asked to read more, say EOF. + test eax, eax + jz @F + + mov eax, ERROR_END_OF_FILE + ret + @@: + xor eax, eax + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + + .only_one_block: + mov esi, ebx + rep movsb ; Copy last piece of block. + jmp .end_read + + .error_at_first_block: + pop edx + .error_at_read_cycle: + pop ebx + .error_at_finish_block: + pop ecx edx + or ebx, -1 + push eax + call ext2_unlock + pop eax + + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + +;--------------------------------------------------------------------- +; Read file information from block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_GetFileInfo: + ;DEBUGF 1, "Calling for file info, for: %s.\n", esi + call ext2_lock + mov edx, [ebx + 16] + cmp byte [esi], 0 + jz .is_root + + push edx + stdcall ext2_inode_find, [esp + 4 + 4] + mov ebx, edx + pop edx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jz @F + + push eax + call ext2_unlock + pop eax + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + + .is_root: + xor ebx, ebx ; Clear out first char, since we don't want to set hidden flag on root. + mov esi, [ebp + EXTFS.root_inode] + + @@: + xor eax, eax + mov edi, edx + mov ecx, 40/4 + rep stosd ; Zero fill buffer. + + cmp bl, '.' + jne @F + or dword [edx], FS_FT_HIDDEN + + @@: + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jnz @F ; If a directory, don't put in file size. + + mov eax, [esi + EXT2_INODE_STRUC.i_size] ; Low file size. + mov ebx, [esi + EXT2_INODE_STRUC.i_dir_acl] ; High file size. + mov dword [edx+32], eax + mov dword [edx+36], ebx + + xor dword [edx], FS_FT_DIR ; Next XOR will clean this, to mark it as a file. + @@: + xor dword [edx], FS_FT_DIR ; Mark as directory. + + lea edi, [edx + 8] + + ; Store all time. + mov eax, [esi + EXT2_INODE_STRUC.i_ctime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [esi + EXT2_INODE_STRUC.i_atime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [esi + EXT2_INODE_STRUC.i_mtime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + call ext2_unlock + xor eax, eax + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_SetFileInfo: + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push edx esi edi ebx + call ext2_lock + mov edx, [ebx + 16] + + ; Is this read-only? + test [ebp + EXTFS.partition_flags], EXT2_RO + jnz .fail + + ; Not supported for root. + cmp byte [esi], 0 + je .fail + + .get_inode: + push edx + stdcall ext2_inode_find, [esp + 4 + 20] + pop edx + + test eax, eax + jnz @F + + ; Save inode number. + push esi + mov esi, [ebp + EXTFS.ext2_save_inode] + + ; From the BDFE, we ignore read-only file flags, hidden file flags; + ; We ignore system file flags, file was archived or not. + + ; Also ignored is file creation time. ext2 stores "inode modification" + ; time in the ctime field, which is updated by the respective inode_write + ; procedure, and any writes on it would be overwritten anyway. + + ; Access time. + lea edi, [esi + EXT2_INODE_STRUC.i_atime] + lea esi, [edx + 16] + call bdfe_to_unix_time + + ; Modification time. + add esi, 8 + add edi, 8 + call bdfe_to_unix_time + + mov ebx, [ebp + EXTFS.ext2_save_inode] ; Get address of inode into ebx. + pop eax ; Get inode number in eax. + call ext2_inode_write ; eax contains error code, if any. + test eax, eax + jnz @F + + call ext2_sb_update + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + @@: + push eax + call ext2_unlock + pop eax + + pop ebx edi esi edx + ret + + .fail: + call ext2_sb_update + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_UNSUPPORTED_FS + jmp @B + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_Delete: + ;DEBUGF 1, "Attempting Delete.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't delete root. + cmp byte [esi], 0 + jz .error_access_denied + + push esi + stdcall ext2_inode_find, 0 + mov ebx, esi + pop esi + + test eax, eax + jnz .error_access_denied + + mov edx, [ebp + EXTFS.ext2_save_inode] + movzx edx, [edx + EXT2_INODE_STRUC.i_mode] + and edx, EXT2_S_IFMT ; Get the mask. + cmp edx, EXT2_S_IFDIR + jne @F ; If not a directory, we don't need to check if it's empty. + + call ext2_dir_empty ; 0 means directory is empty. + + test eax, eax + jnz .error_access_denied + + @@: + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error_access_denied + mov eax, esi + + ; Save file/dir & parent inode. + push ebx eax + + cmp edx, EXT2_S_IFDIR + jne @F + + ; Unlink '.' + mov eax, [esp + 4] + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack8 + + ; Unlink '..' + mov eax, [esp + 4] + mov ebx, [esp] + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack8 + + @@: + pop eax + mov ebx, [esp] + ; Unlink the inode. + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack4 + + ; If hardlinks aren't zero, shouldn't completely free. + test eax, eax + jz @F + + add esp, 4 + jmp .disk_sync + + @@: + ; Read the inode. + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_read + test eax, eax + jnz .error_stack4 + + ; Free inode data. + mov esi, [ebp + EXTFS.ext2_save_inode] + xor ecx, ecx + + @@: + push ecx + call ext2_inode_get_block + test eax, eax + jnz .error_stack8 + mov eax, ecx + pop ecx + + ; If 0, we're done. + test eax, eax + jz @F + + call ext2_block_free + test eax, eax + jnz .error_stack4 + + inc ecx + jmp @B + + @@: + ; Free indirect blocks. + call ext2_inode_free_indirect_blocks + test eax, eax + jnz .error_stack4 + + ; Clear the inode, and add deletion time. + mov edi, [ebp + EXTFS.ext2_save_inode] + xor eax, eax + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_save_inode] + add edi, EXT2_INODE_STRUC.i_dtime + call current_unix_time + + ; Write the inode. + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_write + test eax, eax + jnz .error_stack4 + + ; Check if directory. + cmp edx, EXT2_S_IFDIR + jne @F + + ; If it is, decrement used_dirs_count. + + ; Get block group. + mov eax, [esp] + dec eax + xor edx, edx + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + push eax + call ext2_bg_read_desc + test eax, eax + jz .error_stack8 + + dec [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] + + pop eax + call ext2_bg_write_desc + + @@: + pop eax + call ext2_inode_free + test eax, eax + jnz .error_access_denied + + .disk_sync: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ;DEBUGF 1, "And returning with: %x.\n", eax + ret + + .error_stack8: + add esp, 4 + .error_stack4: + add esp, 4 + .error_access_denied: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_ACCESS_DENIED + jmp .return + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_CreateFolder: + ;DEBUGF 1, "Attempting to create folder.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't create root, but for CreateFolder already existing directory is success. + cmp byte [esi], 0 + jz .success + + push esi + stdcall ext2_inode_find, 0 + pop esi + + ; If the directory is there, we've succeeded. + test eax, eax + jz .success + + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error + + ; Inode ID for preference. + mov eax, esi + call ext2_inode_alloc + test eax, eax + jnz .error_full + + ; Save allocated inode in EDX; filename is in EDI; parent ID in ESI. + mov edx, ebx + + push edi + + xor al, al + mov edi, [ebp + EXTFS.ext2_temp_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_temp_inode] + add edi, EXT2_INODE_STRUC.i_atime + call current_unix_time + + add edi, 8 + call current_unix_time + + pop edi + + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR or PERMISSIONS + mov eax, edx + call ext2_inode_write + test eax, eax + jnz .error + + ; Link to self. + push edx esi + + mov eax, edx + mov ebx, eax + mov dl, EXT2_FT_DIR + mov esi, self_link + call ext2_inode_link + + pop esi edx + + test eax, eax + jnz .error + + ; Link to parent. + push edx esi + + mov eax, ebx + mov ebx, esi + mov dl, EXT2_FT_DIR + mov esi, parent_link + call ext2_inode_link + + pop esi edx + + test eax, eax + jnz .error + + ; Link parent to child. + mov eax, esi + mov ebx, edx + mov esi, edi + mov dl, EXT2_FT_DIR + call ext2_inode_link + test eax, eax + jnz .error + + ; Get block group descriptor for allocated inode's block. + mov eax, ebx + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + mov edx, eax + + call ext2_bg_read_desc + test eax, eax + jz .error + + inc [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] + mov eax, edx + call ext2_bg_write_desc + test eax, eax + jnz .error + + .success: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ;DEBUGF 1, "Returning with: %x.\n", eax + ret + + .error: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_full: + mov eax, ERROR_DISK_FULL + jmp .return + +self_link db ".", 0 +parent_link db "..", 0 + +;--------------------------------------------------------------------- +; Rewrite a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +; ebx = bytes written. +;--------------------------------------------------------------------- +ext2_Rewrite: + ;DEBUGF 1, "Attempting Rewrite.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ecx edx esi edi + pushad + + call ext2_lock + + add esi, [esp + 16 + 32 + 4] + ; Can't create root. + cmp byte [esi], 0 + jz .error_access_denied + + push esi + stdcall ext2_inode_find, 0 + pop esi + + ; If the file is there, delete it. + test eax, eax + jnz @F + + pushad + + push eax + call ext2_unlock + pop eax + + push dword 0x00000000 + call ext2_Delete + add esp, 4 + + push eax + call ext2_lock + pop eax + + test eax, eax + jnz .error_access_denied_delete + + popad + @@: + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error_access_denied + + ; Inode ID for preference. + mov eax, esi + call ext2_inode_alloc + test eax, eax + jnz .error_full + + ; Save allocated inode in EDX; filename is in EDI; parent ID in ESI. + mov edx, ebx + + push edi + + xor al, al + mov edi, [ebp + EXTFS.ext2_temp_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_temp_inode] + add edi, EXT2_INODE_STRUC.i_atime + call current_unix_time + + add edi, 8 + call current_unix_time + + pop edi + + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG or PERMISSIONS + mov eax, edx + call ext2_inode_write + test eax, eax + jnz .error + + ; Link parent to child. + mov eax, esi + mov ebx, edx + mov esi, edi + mov dl, EXT2_FT_REG_FILE + call ext2_inode_link + test eax, eax + jnz .error + + popad + push eax + call ext2_unlock + pop eax + + push dword 0x00000000 + call ext2_Write + add esp, 4 + + push eax + call ext2_lock + pop eax + + .success: + push eax + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + pop eax + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx + + ;DEBUGF 1, "And returning with: %x.\n", eax + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .success + + .error_access_denied_delete: + popad + + .error_access_denied: + popad + xor ebx, ebx + + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_full: + popad + xor ebx, ebx + + mov eax, ERROR_DISK_FULL + jmp .return + +;--------------------------------------------------------------------- +; Write to a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +; ebx = number of bytes written. +;--------------------------------------------------------------------- +ext2_Write: + ;DEBUGF 1, "Attempting write, " + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ecx edx esi edi + call ext2_lock + + add esi, [esp + 16 + 4] + + ; Can't write to root. + cmp byte [esi], 0 + jz .error + + push ebx ecx edx + stdcall ext2_inode_find, 0 + pop edx ecx ebx + ; If file not there, error. + xor ecx, ecx + test eax, eax + jnz .error_file_not_found + + ; Save the inode. + push esi + + ; Check if it's a file. + mov edx, [ebp + EXTFS.ext2_save_inode] + test [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG + jz .error + + mov eax, esi + mov ecx, [ebx + 4] + + call ext2_inode_extend + xor ecx, ecx + test eax, eax + jnz .error_device + + ; ECX contains the size to write, and ESI points to it. + mov ecx, [ebx + 0x0C] + mov esi, [ebx + 0x10] + + ; Save the size of the inode. + mov eax, [edx + EXT2_INODE_STRUC.i_size] + push eax + + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov ebx, [ebp + EXTFS.block_size] + sub ebx, edx + + cmp ebx, ecx + jbe @F + + ; If the size to copy fits in current block, limit to that, instead of the entire block. + mov ebx, ecx + + @@: + ; Copy EBX bytes, in EAX indexed block. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + push ecx + + mov ecx, ebx + mov edi, ebx + add edi, edx + rep movsb + + pop ecx + + ; Write the block. + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], ebx + sub ecx, ebx + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + push eax + mov edx, [esp + 8] + call ext2_inode_blank_entry + test eax, eax + pop eax + jnz .error_inode_size + + push ecx + + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_save_block] + rep movsb + + pop ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + add [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jz @F + + push eax + mov edx, [esp + 8] + + call ext2_inode_blank_entry + test eax, eax + pop eax + jnz .error_inode_size + + @@: + push ecx + mov edi, [ebp + EXTFS.ext2_save_block] + rep movsb + pop ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], ecx + xor ecx, ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + + call ext2_inode_write + test eax, eax + jnz .error_device + + .success: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + add esp, 4 + + mov ebx, [esp + 12] + sub ebx, ecx + pop edi esi edx ecx + + ;DEBUGF 1, "and returning with: %x.\n", eax + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_file_not_found: + mov eax, ERROR_FILE_NOT_FOUND + jmp .return + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + + call ext2_inode_write + + .error_device: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_DEVICE + jmp .return + +;--------------------------------------------------------------------- +; Set the end of a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_SetFileEnd: + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't write to root. + cmp byte [esi], 0 + jz .error + + stdcall ext2_inode_find, 0 + ; If file not there, error. + test eax, eax + jnz .error_file_not_found + + ; Check if it's a file. + mov edx, [ebp + EXTFS.ext2_save_inode] + cmp [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG + jne .error + + mov eax, esi + mov ecx, [ebx + 4] + call ext2_inode_extend + test eax, eax + jnz .error_disk_full + + mov eax, esi + call ext2_inode_truncate + test eax, eax + jnz .error_disk_full + + mov eax, esi + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_write + + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_file_not_found: + mov eax, ERROR_FILE_NOT_FOUND + jmp .return + + .error_disk_full: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_DISK_FULL + jmp .return diff --git a/kernel/branches/kolibri-process/fs/ext2/ext2.inc b/kernel/branches/kolibri-process/fs/ext2/ext2.inc new file mode 100644 index 000000000..e2b9aea19 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ext2/ext2.inc @@ -0,0 +1,670 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 structures, and macros. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Future jobs for driver, in order of preference: +; * clean up existing extents support. +; * add b-tree directories support. +; * add long file support. +; * add journal support. +; * add minor features that come with ext3/4. + +; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course). + +;--------------------------------------------------------------------- +; Clears a bit. +; Input: eax = index into bitmap. +; [EXTFS.ext2_save_block] = address of bitmap. +; ebp = address of EXTFS. +; Output: Bit cleared. +; eax = non-zero, if already cleared. +;--------------------------------------------------------------------- +bitmap_clear_bit: + push ebx ecx edx + + xor edx, edx + mov ecx, 8 + div ecx + + add eax, [ebp + EXTFS.ext2_save_block] + + ; Get the mask. + mov ebx, 1 + mov ecx, edx + shl ebx, cl + + test [eax], ebx + jz .cleared + + not ebx + and [eax], ebx + + xor eax, eax + .return: + pop edx ecx ebx + ret + + ; Already cleared. + .cleared: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Finds free bit in the bitmap. +; Input: ecx = number of bits in the bitmap. +; [EXTFS.ext2_save_block] = address of bitmap. +; ebp = address of EXTFS. +; Output: eax = index of free bit in the bitmap; marked set. +; 0xFFFFFFFF if no free bit found. +;--------------------------------------------------------------------- +ext2_find_free_bit: +bitmap_find_free_bit: + push esi ebx ecx edx + mov esi, [ebp + EXTFS.ext2_save_block] + + ; Get total DWORDS in eax; total bits in last dword, if any, in edx. + xor edx, edx + mov eax, ecx + mov ecx, 32 + div ecx + + mov ecx, eax + xor eax, eax + push edx + + test ecx, ecx + jz .last_bits + + ; Check in the DWORDS. + .dwords: + mov ebx, [esi] + not ebx + + bsf edx, ebx + + ; If 0, then the original value would be 0xFFFFFFFF, hence no free bits. + jz @F + + ; We found the value. Let's return with it. + add esp, 4 + + add eax, edx + jmp .return + + @@: + add esi, 4 + add eax, 32 + loop .dwords + + .last_bits: + ; Check in the last few bits. + pop ecx + test ecx, ecx + jz @F + + mov ebx, [esi] + not ebx + bsf ebx, edx + + ; If 0, no free bits. + jz @F + + ; If free bit is greater than the last known bit, then error. + cmp edx, ecx + jg @F + + add eax, edx + jmp .return + + @@: + ; Didn't find any free bits. + xor eax, eax + not eax + jmp @F + + .return: + mov ecx, edx + mov edx, 1 + shl edx, cl + or [esi], edx + + @@: + pop edx ecx ebx esi + ret + +; Recommended move to some kernel-wide string handling code. +;--------------------------------------------------------------------- +; Find the length of a string. +; Input: esi = source. +; Output: length in ecx +;--------------------------------------------------------------------- +strlen: + push eax esi + xor ecx, ecx + + @@: + lodsb + test al, al + jz .ret + + inc ecx + jmp @B + + .ret: + pop esi eax + ret + +;--------------------------------------------------------------------- +; Convert UTF-8 string to ASCII-string (codepage 866) +; Input: esi = source. +; edi = buffer. +; ecx = length of source. +; Output: destroys eax, esi, edi +;--------------------------------------------------------------------- +utf8_to_cp866: + ; Check for zero-length string. + jecxz .return + + .start: + lodsw + cmp al, 0x80 + jb .ascii + + xchg al, ah ; Big-endian. + cmp ax, 0xd080 + jz .yo1 + + cmp ax, 0xd191 + jz .yo2 + + cmp ax, 0xd090 + jb .unk + + cmp ax, 0xd180 + jb .rus1 + + cmp ax, 0xd190 + jb .rus2 + + .unk: + mov al, '_' + jmp .doit + + .yo1: + mov al, 0xf0 ; Ё capital. + jmp .doit + + .yo2: + mov al, 0xf1 ; ё small. + jmp .doit + + .rus1: + sub ax, 0xd090 - 0x80 + jmp .doit + + .rus2: + sub ax, 0xd18f - 0xEF + + .doit: + stosb + sub ecx, 2 + ja .start + ret + + .ascii: + stosb + dec esi + dec ecx + jnz .start + + .return: + ret + +; Recommended move to some kernel-wide time handling code. + +; Total cumulative seconds till each month. +cumulative_seconds_in_month: + .january: dd 0 * (60 * 60 * 24) + .february: dd 31 * (60 * 60 * 24) + .march: dd 59 * (60 * 60 * 24) + .april: dd 90 * (60 * 60 * 24) + .may: dd 120 * (60 * 60 * 24) + .june: dd 151 * (60 * 60 * 24) + .july: dd 181 * (60 * 60 * 24) + .august: dd 212 * (60 * 60 * 24) + .september: dd 243 * (60 * 60 * 24) + .october: dd 273 * (60 * 60 * 24) + .november: dd 304 * (60 * 60 * 24) + .december: dd 334 * (60 * 60 * 24) + +current_bdfe_time: + dd 0 +current_bdfe_date: + dd 0 + +;--------------------------------------------------------------------- +; Stores current unix time. +; Input: edi = buffer to output Unix time. +;--------------------------------------------------------------------- +current_unix_time: + push eax esi + mov esi, current_bdfe_time + + ; Just a small observation: + ; The CMOS is a pretty bad source to get time from. One shouldn't rely on it, + ; since it messes up the time by tiny bits. Of course, this is all technical, + ; but one can look it up on the osdev wiki. What is better is to get the time + ; from CMOS during boot, then update system time using a more accurate timer. + ; I'll probably add that after the Summer of Code, so TODO! TODO! TODO!. + + ; Get time from CMOS. + ; Seconds. + mov al, 0x00 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 0], al + + ; Minute. + mov al, 0x02 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 1], al + + ; Hour. + mov al, 0x04 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 2], al + + ; Get date. + + ; Day. + mov al, 0x7 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 4], al + + ; Month. + mov al, 0x8 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 5], al + + ; Year. + mov al, 0x9 + out 0x70, al + in al, 0x71 + call bcd2bin + add ax, 2000 ; CMOS only returns last two digits. + ; Note that everywhere in KolibriOS this is used. + ; This is hacky, since the RTC can be incorrectly set + ; to something before 2000. + mov [esi + 6], ax + + call bdfe_to_unix_time + pop esi eax + ret + +;--------------------------------------------------------------------- +; Convert time+date from BDFE to Unix time. +; Input: esi = pointer to BDFE time+date. +; edi = buffer to output Unix time. +;--------------------------------------------------------------------- +bdfe_to_unix_time: + push eax ebx ecx edx + mov dword[edi], 0x00000000 + + ; The minimum representable time is 1901-12-13. + cmp word[esi + 6], 1901 + jb .ret + jg .max + + cmp byte[esi + 5], 12 + jb .ret + + cmp byte[esi + 4], 13 + jbe .ret + jg .convert + + ; Check if it is more than the maximum representable time. + .max: + ; The maximum representable time is 2038-01-19. + cmp word[esi + 6], 2038 + jg .ret + jb .convert + + cmp byte[esi + 5], 1 + jg .ret + + cmp byte[esi + 4], 19 + jge .ret + + ; Convert the time. + .convert: + ; Get if current year is leap year in ECX. + xor ecx, ecx + mov ebx, 4 + xor edx, edx + + cmp word[esi + 6], 1970 + jb .negative + + movzx eax, word[esi + 6] ; Year. + cmp byte[esi + 5], 3 ; If the month is less than March, than that year doesn't matter. + jge @F + + test eax, 3 + ; Not a leap year. + jnz @F + + inc ecx + @@: + ; Number of leap years between two years = ((end date - 1)/4) - (1970/4) + dec eax + div ebx + sub eax, 1970/4 + + ; EAX is the number of leap years. + add eax, ecx + mov ecx, (60 * 60 * 24) ; Seconds in a day. + mul ecx + + ; Account for leap years, i.e., one day extra for each. + add [edi], eax + + ; Get total days in EAX. + movzx eax, byte[esi + 4] + dec eax + mul ecx + + ; Account for days. + add [edi], eax + + ; Account for month. + movzx eax, byte[esi + 5] + dec eax + mov eax, [cumulative_seconds_in_month + (eax * 4)] + add [edi], eax + + ; Account for year. + movzx eax, word[esi + 6] + sub eax, 1970 + mov ecx, (60 * 60 * 24) * 365 ; Seconds in a year. + mul ecx + add [edi], eax + + ; Seconds. + movzx eax, byte[esi + 0] + add [edi], eax + + ; Minutes. + movzx eax, byte[esi + 1] + mov ecx, 60 + mul ecx + add [edi], eax + + ; Hours. + movzx eax, byte[esi + 2] + mov ecx, (60 * 60) + mul ecx + add [edi], eax + + ; The time wanted is before the epoch; handle it here. + .negative: + ; TODO. + + .ret: + pop edx ecx ebx eax + ret + +; Recommended move to some kernel-wide alloc handling code. +macro KERNEL_ALLOC store, label +{ + call kernel_alloc + mov store, eax + test eax, eax + jz label +} + +macro KERNEL_FREE data, label +{ + cmp data, 0 + jz label + push data + call kernel_free +} + +struct EXTFS PARTITION + lock MUTEX + partition_flags dd ? + log_block_size dd ? + block_size dd ? + count_block_in_block dd ? + blocks_per_group dd ? + global_desc_table dd ? + root_inode dd ? ; Pointer to root inode in memory. + 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 ? ; Block for 1 global procedure. + ext2_temp_block dd ? ; Block for small procedures. + ext2_save_inode dd ? ; inode for global procedures. + ext2_temp_inode dd ? ; inode for small procedures. + groups_count dd ? + superblock rd 1024/4 +ends + +; EXT2 revisions. +EXT2_GOOD_OLD_REV = 0 + +; For fs_type. +FS_TYPE_UNDEFINED = 0 +FS_TYPE_EXT = 2 + +; Some set inodes. +EXT2_BAD_INO = 1 +EXT2_ROOT_INO = 2 +EXT2_ACL_IDX_INO = 3 +EXT2_ACL_DATA_INO = 4 +EXT2_BOOT_LOADER_INO = 5 +EXT2_UNDEL_DIR_INO = 6 + +; EXT2_SUPER_MAGIC. +EXT2_SUPER_MAGIC = 0xEF53 +EXT2_VALID_FS = 1 + +; Flags defining i_mode values. +EXT2_S_IFMT = 0xF000 ; Mask for file type. + +EXT2_S_IFREG = 0x8000 ; Regular file. +EXT2_S_IFDIR = 0x4000 ; Directory. + +EXT2_S_IRUSR = 0x0100 ; User read +EXT2_S_IWUSR = 0x0080 ; User write +EXT2_S_IXUSR = 0x0040 ; User execute +EXT2_S_IRGRP = 0x0020 ; Group read +EXT2_S_IWGRP = 0x0010 ; Group write +EXT2_S_IXGRP = 0x0008 ; Group execute +EXT2_S_IROTH = 0x0004 ; Others read +EXT2_S_IWOTH = 0x0002 ; Others write +EXT2_S_IXOTH = 0x0001 ; Others execute + +PERMISSIONS = EXT2_S_IRUSR or EXT2_S_IWUSR \ + or EXT2_S_IRGRP or EXT2_S_IWGRP \ + or EXT2_S_IROTH or EXT2_S_IWOTH + +; File type defining values in directory entry. +EXT2_FT_REG_FILE = 1 ; Regular file. +EXT2_FT_DIR = 2 ; Directory. + +; Flags used by KolibriOS. +FS_FT_HIDDEN = 2 +FS_FT_DIR = 0x10 ; Directory. + +; ext2 partition flags. +EXT2_RO = 0x01 + +FS_FT_ASCII = 0 ; Name in ASCII. +FS_FT_UNICODE = 1 ; Name in Unicode. + +EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 ; Have file type in directory entry. +EXT4_FEATURE_INCOMPAT_EXTENTS = 0x0040 ; Extents. +EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x0200 ; Flexible block groups. + +EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001 ; Sparse Superblock +EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x0002 ; Large file support (64-bit file size) + +; Implemented ext[2,3,4] features. +EXT4_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE \ + or EXT4_FEATURE_INCOMPAT_EXTENTS \ + or EXT4_FEATURE_INCOMPAT_FLEX_BG + +; Implemented features which otherwise require "read-only" mount. +EXT2_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER \ + or EXT2_FEATURE_RO_COMPAT_LARGE_FILE + +; ext4 features not support for write. +EXT4_FEATURE_INCOMPAT_W_NOT_SUPP = EXT4_FEATURE_INCOMPAT_EXTENTS \ + or EXT4_FEATURE_INCOMPAT_FLEX_BG + +; Flags specified in i_flags. +EXT2_EXTENTS_FL = 0x00080000 ; Extents. + +struct EXT2_INODE_STRUC + i_mode dw ? + i_uid dw ? + i_size dd ? + i_atime dd ? + i_ctime dd ? + i_mtime dd ? + i_dtime dd ? + i_gid dw ? + i_links_count dw ? + i_blocks dd ? + i_flags dd ? + i_osd1 dd ? + i_block rd 15 + i_generation dd ? + i_file_acl dd ? + i_dir_acl dd ? + i_faddr dd ? + i_osd2 dd ? ; 12 bytes. +ends + +struct EXT2_DIR_STRUC + inode dd ? + rec_len dw ? + name_len db ? + file_type db ? + name db ? ; 255 (max) bytes. +ends + +struct EXT2_BLOCK_GROUP_DESC + block_bitmap dd ? ; +0 + inode_bitmap dd ? ; +4 + inode_table dd ? ; +8 + free_blocks_count dw ? ; +12 + free_inodes_count dw ? ; +14 + used_dirs_count dw ? ; +16 + pad dw ? ; +18 + reserved rb 12 ; +20 +ends + +struct EXT2_SB_STRUC + inodes_count dd ? ; +0 + blocks_count dd ? ; +4 + r_block_count dd ? ; +8 + free_block_count dd ? ; +12 + free_inodes_count dd ? ; +16 + first_data_block dd ? ; +20 + log_block_size dd ? ; +24 + log_frag_size dd ? ; +28 + blocks_per_group dd ? ; +32 + frags_per_group dd ? ; +36 + inodes_per_group dd ? ; +40 + mtime dd ? ; +44 + wtime dd ? ; +48 + mnt_count dw ? ; +52 + max_mnt_count dw ? ; +54 + magic dw ? ; +56 + state dw ? ; +58 + errors dw ? ; +60 + minor_rev_level dw ? ; +62 + lastcheck dd ? ; +64 + check_intervals dd ? ; +68 + creator_os dd ? ; +72 + rev_level dd ? ; +76 + def_resuid dw ? ; +80 + def_resgid dw ? ; +82 + first_ino dd ? ; +84 + inode_size dw ? ; +88 + block_group_nr dw ? ; +90 + feature_compat dd ? ; +92 + feature_incompat dd ? ; +96 + feature_ro_compat dd ? ; +100 + uuid rb 16 ; +104 + volume_name rb 16 ; +120 + last_mounted rb 64 ; +136 + algo_bitmap dd ? ; +200 + prealloc_blocks db ? ; +204 + preallock_dir_blocks db ? ; +205 + reserved_gdt_blocks dw ? ; +206 + journal_uuid rb 16 ; +208 + journal_inum dd ? ; +224 + journal_dev dd ? ; +228 + last_orphan dd ? ; +232 + hash_seed rd 4 ; +236 + def_hash_version db ? ; +252 + reserved rb 3 ; +253 (reserved) + default_mount_options dd ? ; +256 + first_meta_bg dd ? ; +260 + mkfs_time dd ? ; +264 + jnl_blocks rd 17 ; +268 + blocks_count_hi dd ? ; +336 + r_blocks_count_hi dd ? ; +340 + free_blocks_count_hi dd ? ; +344 + min_extra_isize dw ? ; +348 + want_extra_isize dw ? ; +350 + flags dd ? ; +352 + raid_stride dw ? ; +356 + mmp_interval dw ? ; +358 + mmp_block dq ? ; +360 + raid_stripe_width dd ? ; +368 + log_groups_per_flex db ? ; +372 +ends + +; Header block extents. +struct EXT4_EXTENT_HEADER + eh_magic dw ? ; Magic value of 0xF30A, for ext4. + eh_entries dw ? ; Number of blocks covered by the extent. + eh_max dw ? ; Capacity of entries. + eh_depth dw ? ; Tree depth (if 0, extents in the array are not extent indexes) + eh_generation dd ? ; ??? +ends + +; Extent. +struct EXT4_EXTENT + ee_block dd ? ; First logical block extent covers. + ee_len dw ? ; Number of blocks covered by extent. + ee_start_hi dw ? ; Upper 16 bits of 48-bit address (unused in KOS) + ee_start_lo dd ? ; Lower 32 bits of 48-bit address. +ends + +; Index on-disk structure; pointer to block of extents/indexes. +struct EXT4_EXTENT_IDX + ei_block dd ? ; Covers logical blocks from here. + ei_leaf_lo dd ? ; Lower 32-bits of pointer to the physical block of the next level. + ei_leaf_hi dw ? ; Higher 16-bits (unused in KOS). + ei_unused dw ? ; Reserved. +ends \ No newline at end of file diff --git a/kernel/branches/kolibri-process/fs/ext2/inode.inc b/kernel/branches/kolibri-process/fs/ext2/inode.inc new file mode 100644 index 000000000..a743064fa --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ext2/inode.inc @@ -0,0 +1,1850 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 inode handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Receives block number from extent-based inode. +; Input: ecx = number of block in inode +; esi = address of extent header +; ebp = pointer to EXTFS +; Output: ecx = address of next block, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext4_block_recursive_search: + cmp word [esi + EXT4_EXTENT_HEADER.eh_magic], 0xF30A ;EXT4_EXT_MAGIC + jne .fail + + movzx ebx, [esi + EXT4_EXTENT_HEADER.eh_entries] + add esi, sizeof.EXT4_EXTENT_HEADER + cmp word [esi - sizeof.EXT4_EXTENT_HEADER + EXT4_EXTENT_HEADER.eh_depth], 0 + je .leaf_block ;листовой ли это блок? + + ;не листовой блок, а индексный ; eax - ext4_extent_idx + test ebx, ebx + jz .fail ;пустой индексный блок -> ошибка + + ;цикл по индексам экстентов + @@: + cmp ebx, 1 ;у индексов не хранится длина, + je .end_search_index ;поэтому, если остался последний - то это нужный + + cmp ecx, [esi + EXT4_EXTENT_IDX.ei_block] + jb .fail + + cmp ecx, [esi + sizeof.EXT4_EXTENT_IDX + EXT4_EXTENT_IDX.ei_block] ;блок слeдующего индекса + jb .end_search_index ;следующий дальше - значит текущий, то что нам нужен + + add esi, sizeof.EXT4_EXTENT_IDX + dec ebx + jmp @B + + .end_search_index: + ;ebp указывает на нужный extent_idx, считываем следующий блок + mov ebx, [ebp + EXTFS.ext2_temp_block] + mov eax, [esi + EXT4_EXTENT_IDX.ei_leaf_lo] + call ext2_block_read + test eax, eax + jnz .fail + mov esi, ebx + jmp ext4_block_recursive_search ;рекурсивно прыгаем в начало + + .leaf_block: ;листовой блок esi - ext4_extent + ;цикл по экстентам + @@: + test ebx, ebx + jz .fail ;ни один узел не подошел - ошибка + + mov edx, [esi + EXT4_EXTENT.ee_block] + cmp ecx, edx + jb .fail ;если меньше, значит он был в предыдущих блоках -> ошибка + + movzx edi, [esi + EXT4_EXTENT.ee_len] + add edx, edi + cmp ecx, edx + jb .end_search_extent ;нашли нужный блок + + add esi, sizeof.EXT4_EXTENT + dec ebx + jmp @B + + .end_search_extent: + mov edx, [esi + EXT4_EXTENT.ee_start_lo] + sub ecx, [esi + EXT4_EXTENT.ee_block] ;разница в ext4 блоках + add ecx, edx + xor eax, eax + ret + + .fail: + mov eax, ERROR_FS_FAIL + ret + +;--------------------------------------------------------------------- +; Frees triply indirect block. +; Input: eax = triply indirect block. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free_triply_indirect: + push ebx edx + + test eax, eax + jz .success + push eax + ; Read the triple indirect block. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + pop eax + jnz .fail + + ; Free the triple indirect block. + call ext2_block_free + test eax, eax + jnz .fail + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + @@: + mov eax, [ebx] + test eax, eax + jz .success + + call ext2_inode_free_doubly_indirect + cmp eax, 1 + je .success + cmp eax, 0xFFFFFFFF + je .fail + + add ebx, 4 + cmp ebx, edx + jb @B + + .success: + xor eax, eax + .ret: + pop edx ebx + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees double indirect block. +; Input: eax = double indirect block. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code, 1 implies finished, ~0 implies error +;--------------------------------------------------------------------- +ext2_inode_free_doubly_indirect: + push ebx edx + + test eax, eax + jz .complete + push eax + ; Read the double indirect block. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + pop eax + jnz .fail + + call ext2_block_free + test eax, eax + jnz .fail + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + @@: + mov eax, [ebx] + test eax, eax + jz .complete + + call ext2_block_free + test eax, eax + jnz .fail + + add ebx, 4 + cmp ebx, edx + jb @B + + .success: + xor eax, eax + .ret: + pop edx ebx + ret + + .complete: + xor eax, eax + inc eax + jmp .ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees all indirect blocks. +; Input: ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_free_indirect_blocks: + push edi + + mov edi, [ebp + EXTFS.ext2_save_inode] + + ; Free indirect block. + mov eax, [edi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jz .success + + call ext2_block_free + test eax, eax + jnz .fail + + mov eax, [edi + EXT2_INODE_STRUC.i_block + 13*4] + call ext2_inode_free_doubly_indirect + cmp eax, 1 + je .success + cmp eax, 0xFFFFFFFF + je .fail + + mov eax, [edi + EXT2_INODE_STRUC.i_block + 14*4] + call ext2_inode_free_triply_indirect + test eax, eax + jnz .fail + + .success: + xor eax, eax + .ret: + pop edi + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Allocates block for inode. +; Input: esi = address of inode +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_calloc_block: + push ecx + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + .ret: + pop ecx + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Sets block ID for indirect-addressing inode. +; Input: ecx = index of block in inode +; edi = block ID to set to +; esi = address of inode +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_set_block: + push ebx ecx edx + + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 14*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + lea ecx, [ebx + eax*4] + mov eax, [ebx + eax*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc_4 + + mov [ecx], ebx + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_4 + + mov eax, [ecx] + @@: + mov [esp], eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, edx + jmp @F + + .double_indirect_block: + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + test eax, eax + jnz .double_indirect_present + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 13*4], ebx + mov eax, ebx + + .double_indirect_present: + ; Save block we're at. + push eax + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + lea ecx, [ebx + edx*4] + push ecx + + lea ecx, [ebx + eax*4] + cmp dword[ecx], 0 + jne @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc_8 + + mov [ecx], ebx + + mov eax, [esp + 4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_8 + + @@: + mov eax, [ecx] + push eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_12 + + pop eax + pop ecx + mov [ecx], edi + call ext2_block_write + + add esp, 4 + jmp .return + + .indirect_block: + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 12*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get the block ID. + mov [ebx + ecx*4], edi + pop eax + call ext2_block_write + jmp .return + + .direct_block: + mov [esi + EXT2_INODE_STRUC.i_block + ecx*4], edi + xor eax, eax + + .return: + pop edx ecx ebx + ret + + .fail_alloc: + xor eax, eax + not eax + jmp .return + + .fail_alloc_12: + add esp, 4 + .fail_alloc_8: + add esp, 4 + .fail_alloc_4: + add esp, 4 + jmp .fail_alloc + +;--------------------------------------------------------------------- +; Receives block ID from indirect-addressing inode. +; Input: ecx = index of block in inode +; esi = address of inode +; ebp = pointer to EXTFS +; Output: ecx = block ID, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_get_block: + ; If inode is extent-based, use ext4_block_recursive_search. + test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL + jz @F + + pushad + + ; Get extent header in EBP. + add esi, EXT2_INODE_STRUC.i_block + call ext4_block_recursive_search + mov PUSHAD_ECX, ecx + mov PUSHAD_EAX, eax + + popad + ret + + @@: + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .get_direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .get_indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .get_double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + push edx ebx + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + mov eax, [ebx + eax*4] + test eax, eax + jz .fail_triple_indirect_block + + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, edx + jmp @F + + .get_double_indirect_block: + push edx ebx + + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + test eax, eax + jz .fail_double_indirect_block + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + mov eax, [ebx + eax*4] + test eax, eax + jz .fail_double_indirect_block + + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [ebx + edx*4] + .fail: + pop ebx edx + + ret + + .get_indirect_block: + push ebx + + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jz .fail_indirect_block + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz @F + + mov ecx, [ebx + ecx*4] + @@: + pop ebx + + ret + + .get_direct_block: + mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4] + xor eax, eax + + ret + + .fail_indirect_block: + pop ebx + + .fail_triple_indirect_block: + xor eax, eax + xor ecx, ecx + ret + + .fail_double_indirect_block: + pop ebx edx + jmp .fail_triple_indirect_block + +;--------------------------------------------------------------------- +; Get block containing inode. +; Input: eax = inode number. +; ebp = pointer to EXTFS. +; Output: ebx = block (hard disk) containing inode. +; edx = index inside block. +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_read_block_of_inode: + pushad + + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + push edx ; Index in group. + + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: inode group offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .return + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ; Block number of inode table - in ext2 terms. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + + ; eax: points to inode table on HDD. + mov esi, eax + + ; Add local address of inode. + pop eax + mov ecx, [ebp + EXTFS.inode_size] + mul ecx ; (index * inode_size) + + mov ebp, 512 + div ebp ; Divide by hard disk block size. + + add eax, esi ; Found block to read. + mov ebx, eax ; Get it inside ebx. + + xor eax, eax + .return: + mov PUSHAD_EAX, eax + mov PUSHAD_EBX, ebx + mov PUSHAD_EDX, edx + + popad + ret + +;--------------------------------------------------------------------- +; Sets content of inode by number. +; Input: eax = inode number. +; ebx = address from where to write inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_write: + push edx edi esi ecx ebx + mov esi, ebx + + ; Ext2 actually stores time of modification of inode in ctime. + lea edi, [ebx + EXT2_INODE_STRUC.i_ctime] + call current_unix_time + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + + mov ecx, eax ; Save block. + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov eax, ecx + mov ecx, [ebp + EXTFS.inode_size] + mov edi, edx ; The index into the block. + add edi, ebx + rep movsb + + ; Write the block. + call fs_write32_sys + + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Get content of inode by number. +; Input: eax = inode number. +; ebx = address where to store inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_read: + push edx edi esi ecx ebx + mov edi, ebx + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov ecx, [ebp + EXTFS.inode_size] + mov esi, edx ; The index into the inode. + add esi, ebx + rep movsb + + xor eax, eax + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Seek inode from the path. +; Input: esi + [esp + 4] = name. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +; esi = inode number. +; dl = first byte of file/folder name. +; [ext2_data.ext2_save_inode] stores the inode. +;--------------------------------------------------------------------- +ext2_inode_find: + mov edx, [ebp + EXTFS.root_inode] + + ; Check for empty root. + cmp [edx + EXT2_INODE_STRUC.i_blocks], 0 + je .error_empty_root + + ; Check for root. + cmp byte[esi], 0 + jne .next_path_part + + push edi ecx + mov esi, [ebp + EXTFS.root_inode] + mov edi, [ebp + EXTFS.ext2_save_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep movsb + pop ecx edi + + xor eax, eax + xor dl, dl + mov esi, EXT2_ROOT_INO + ret 4 + + .next_path_part: + push [edx + EXT2_INODE_STRUC.i_blocks] + xor ecx, ecx + + .folder_block_cycle: + push ecx + xchg esi, edx + call ext2_inode_get_block + xchg esi, edx + test eax, eax + jnz .error_get_inode_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] ; Get directory records from directory. + call ext2_block_read + test eax, eax + jnz .error_get_block + + push esi + push edx + call ext2_block_find_parent + pop edx + pop edi ecx + + cmp edi, esi ; Did something match? + je .next_folder_block ; No, move to next block. + + cmp byte [esi], 0 ; Reached the "end" of path successfully. + jnz @F + cmp dword[esp + 8], 0 + je .get_inode_ret + mov esi, [esp + 8] + mov dword[esp + 8], 0 + + @@: + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_read + test eax, eax + jnz .error_get_inode + + movzx eax, [ebx + EXT2_INODE_STRUC.i_mode] + and eax, EXT2_S_IFMT ; Get the mask. + cmp eax, EXT2_S_IFDIR + jne .not_found ; Matched till part, but directory entry we got doesn't point to folder. + + pop ecx ; Stack top contains number of blocks. + mov edx, ebx + jmp .next_path_part + + .next_folder_block: + ; Next block in current folder. + pop eax ; Get blocks counter. + sub eax, [ebp + EXTFS.count_block_in_block] + jle .not_found + + push eax + inc ecx + jmp .folder_block_cycle + + .not_found: + mov eax, ERROR_FILE_NOT_FOUND + ret 4 + + .get_inode_ret: + pop ecx ; Stack top contains number of blocks. + + mov dl, [ebx + EXT2_DIR_STRUC.name] ; First character of file-name. + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + mov esi, eax + + ; If we can't get the inode, eax contains the error. + call ext2_inode_read + ret 4 + + .error_get_inode_block: + .error_get_block: + pop ecx + .error_get_inode: + pop ebx + .error_empty_root: + mov eax, ERROR_FS_FAIL + ret 4 + +;--------------------------------------------------------------------- +; Seeks parent inode from path. +; Input: esi = path. +; ebp = pointer to EXTFS. +; Output: eax = error code. +; esi = inode. +; edi = pointer to file name. +;--------------------------------------------------------------------- +ext2_inode_find_parent: + push esi + xor edi, edi + + .loop: + cmp byte[esi], '/' + jne @F + + mov edi, esi + inc esi + jmp .loop + + @@: + inc esi + cmp byte[esi - 1], 0 + jne .loop + + ; If it was just a filename (without any additional directories), + ; use the last byte as "parent path". + cmp edi, 0 + jne @F + + pop edi + dec esi + jmp .get_inode + + ; It had some additional directories, so handle it that way. + @@: + mov byte[edi], 0 + inc edi + pop esi + + .get_inode: + push ebx edx + stdcall ext2_inode_find, 0 + pop edx ebx + + .return: + ret + +;--------------------------------------------------------------------- +; Link an inode. +; Input: eax = inode on which to link. +; ebx = inode to link. +; dl = file type. +; esi = name. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_link: + push eax + push esi edi ebx ecx edx + + ; Get string length, and then directory entry structure size. + call strlen + add ecx, 8 + + push esi ebx ecx + + xor ecx, ecx + mov esi, [ebp + EXTFS.ext2_temp_inode] + mov ebx, esi + + call ext2_inode_read + test eax, eax + jnz .error_inode_read + + ; Get the maximum addressible i_block index by (i_blocks/(2 << s_log_block_size)). + ; Note that i_blocks contains number of reserved 512B blocks, which is why we've to + ; find out the ext2 blocks. + mov eax, 2 + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + shl eax, cl + mov ecx, eax + + mov eax, [esi + EXT2_INODE_STRUC.i_blocks] + xor edx, edx + + div ecx + + ; EAX is the maximum index inside i_block we can go. + push eax + push dword 0 + + ; ECX contains the "block inside i_block" index. + xor ecx, ecx + @@: + call ext2_inode_get_block + test eax, eax + jnz .error_get_inode_block + test ecx, ecx + jz .alloc_block ; We've got no block here, so allocate one. + + push ecx ; Save block number. + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + ; Try to find free space in current block. + mov ecx, [esp + 8] + call ext2_block_find_fspace + test eax, eax + jz .found + + cmp eax, 0x00000001 + jne .next_iter + + ; This block wasn't linking to the next block, so fix that, and use the next one. + ; Write the block. + pop eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_get_inode_block + + inc dword [esp] + mov ecx, [esp] + call ext2_inode_get_block + test eax, eax + jnz .error_get_inode_block + + test ecx, ecx + jz .alloc_block + + ; If there was a block there, prepare it for our use! + push ecx + jmp .prepare_block + + .next_iter: + add esp, 4 + + inc dword [esp] + mov ecx, [esp] + cmp ecx, [esp + 4] + jbe @B + + .alloc_block: + mov eax, [esp + 12] ; Get inode ID of what we're linking. + call ext2_block_calloc + test eax, eax + jnz .error_get_inode_block + + mov ecx, [esp] ; Get the index of it inside the inode. + mov edi, ebx ; And what to set to. + call ext2_inode_set_block + test eax, eax + jnz .error_get_inode_block + + ; Update i_size. + mov eax, [ebp + EXTFS.block_size] + add [esi + EXT2_INODE_STRUC.i_size], eax + + ; Update i_blocks. + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + ; Write the inode. + mov eax, [esp + 40] + mov ebx, esi + call ext2_inode_write + test eax, eax + jnz .error_get_inode_block + + push edi ; Save the block we just allocated. + + ; If we've allocated/using-old-block outside of loop, prepare it. + .prepare_block: + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + mov edi, ebx + mov eax, [ebp + EXTFS.block_size] + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + .found: + pop edx + add esp, 8 + pop ecx ebx esi + + push ebx + mov [edi], ebx ; Save inode. + + mov eax, [esp + 4] ; Get EDX off the stack -- contains the file_type. + cmp [ebp + EXTFS.superblock + EXT2_SB_STRUC.rev_level], EXT2_GOOD_OLD_REV + je .name + + ; Set the file-type. + mov [edi + EXT2_DIR_STRUC.file_type], al + + .name: + ; Save name. + sub ecx, 8 + mov [edi + EXT2_DIR_STRUC.name_len], cl + add edi, 8 + rep movsb + + ; Write block. + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_block_write + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error_block_write + + pop eax + inc [ebx + EXT2_INODE_STRUC.i_links_count] + call ext2_inode_write + test eax, eax + jnz .error + + xor eax, eax + .ret: + pop edx ecx ebx edi esi + add esp, 4 + ret + + .error_block_read: + add esp, 4 + .error_get_inode_block: + add esp, 8 + .error_inode_read: + add esp, 8 + .error_block_write: + add esp, 4 + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Unlink an inode. +; Input: eax = inode from which to unlink. +; ebx = inode to unlink. +; ebp = pointer to EXTFS. +; Output: eax = number of links to inode, after unlinking (0xFFFFFFFF implies error) +;--------------------------------------------------------------------- +ext2_inode_unlink: + push ebx ecx edx esi edi + + push ebx + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + + test eax, eax + jnz .fail_get_inode + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_temp_inode] + + .loop: + mov ecx, [esp] + call ext2_inode_get_block + + test eax, eax + jnz .fail_loop + test ecx, ecx + jz .fail_loop + + mov eax, ecx + mov edi, eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_loop + + ; edi -> block. + .first_dir_entry: + mov eax, [esp + 4] + cmp [ebx], eax + jne @F + + mov dword[ebx], 0 ; inode. + mov word[ebx + 6], 0 ; name_len + file_type. + jmp .write_block + + @@: + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + push edx + + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + cmp [ebx], eax + jne @F + + mov cx, [ebx + EXT2_DIR_STRUC.rec_len] + add [edx + EXT2_DIR_STRUC.rec_len], cx + add esp, 4 + jmp .write_block + + @@: + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + + ; If it's a zero length entry, error. + test ecx, ecx + jz .fail_inode + + add ebx, ecx + + cmp ebx, [esp] + jb .dir_entry + + add esp, 4 + inc dword[esp] + jmp .loop + + .write_block: + mov eax, edi + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_loop + + add esp, 4 + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov eax, [esp] + call ext2_inode_read + test eax, eax + jnz .fail_get_inode + + dec word[ebx + EXT2_INODE_STRUC.i_links_count] + movzx eax, word[ebx + EXT2_INODE_STRUC.i_links_count] + push eax + + mov eax, [esp + 4] + call ext2_inode_write + test eax, eax + jnz .fail_loop + + pop eax + add esp, 4 + .return: + pop edi esi edx ecx ebx + ret + + .fail_inode: + add esp, 4 + + .fail_loop: + add esp, 4 + + .fail_get_inode: + add esp, 4 + + .fail: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Checks if a directory is empty. +; Input: ebx = inode to check. +; ebp = pointer to EXTFS. +; [EXTFS.ext2_save_inode] = points to saved inode. +; Output: eax = 0 signifies empty directory. +;--------------------------------------------------------------------- +ext2_dir_empty: + push ebx ecx edx + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_save_inode] + + .loop: + mov ecx, [esp] + call ext2_inode_get_block + + ; Treat a failure as not-empty. + test eax, eax + jnz .not_empty + test ecx, ecx + jz .empty + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .not_empty + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + ; Process entry. + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 1 + jne @F + + cmp byte[ebx + EXT2_DIR_STRUC.name], '.' + jne .not_empty + + @@: + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 2 + jne .not_empty + + cmp word[ebx + EXT2_DIR_STRUC.name], '..' + jne .not_empty + + @@: + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + cmp ebx, edx + jb .dir_entry + + inc dword[esp] + jmp .loop + + .empty: + xor eax, eax + .return: + add esp, 4 + pop edx ecx ebx + ret + + .not_empty: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Gets the block group's inode bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = inode bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_inode_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.inode_bitmap] ; Block number of inode bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a inode. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Inode marked as set in inode group. +; eax = error code. +; ebx = inode ID. +;--------------------------------------------------------------------- +ext2_inode_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_count] + push EXT2_BLOCK_GROUP_DESC.free_inodes_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_inodes_count] + push ebx + + push ext2_bg_read_inode_bitmap + + call ext2_resource_alloc + + ; Inode table starts with 1. + inc ebx + + ret + +;--------------------------------------------------------------------- +; Frees a inode. +; Input: eax = inode ID. +; ebp = pointer to EXTFS. +; Output: inode marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free: + push edi ecx + + ; Inode table starts with 1. + dec eax + + mov edi, ext2_bg_read_inode_bitmap + xor ecx, ecx + inc cl + call ext2_resource_free + + pop ecx edi + ret + +;--------------------------------------------------------------------- +; Blanks a particular entry in an inode. +; Input: eax = index into block. +; edx = inode. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_blank_entry: + push ebx ecx edx edi esi + + mov edi, eax + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .allocate + + mov edx, ecx + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_temp_block] + xor eax, eax + rep stosb + + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error + + jmp .success + + ; Need to allocate a block. + .allocate: + mov eax, edx + call ext2_block_calloc + test eax, eax + jnz .error + + mov ecx, edi + mov edi, ebx + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_set_block + test eax, eax + jnz .error + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + + .ret: + pop esi edi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees a particular entry in an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free_entry: + push ebx ecx edi esi + + mov edi, eax + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .success + + mov eax, ecx + call ext2_block_free + test eax, eax + jnz .error + + mov ecx, edi + xor edi, edi + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_set_block + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + sub [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + + .ret: + pop esi edi ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Reads a particular entry from an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +; [ebp + EXTFS.ext2_save_block] = the read block. +;--------------------------------------------------------------------- +ext2_inode_read_entry: + push ebx ecx edx esi + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .error + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .error + + .ret: + pop esi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Writes a particular entry from an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; [ebp + EXTFS.ext2_save_block] = the block to write. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_write_entry: + push ebx ecx edx esi + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .error + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .error + + .ret: + pop esi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Extends inode to said size. +; Input: eax = inode ID. +; ecx = size to extend to. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_extend: + push ebx ecx edx esi edi + + ; Save the inode. + push eax + + ; Read the inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] + cmp eax, ecx + jge .success + + ; Save the size of the inode. + push eax + + ; ECX contains the size we've to write. + sub ecx, eax + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov esi, [ebp + EXTFS.block_size] + sub esi, edx + + cmp esi, ecx + jbe @F + + ; If the size to entend to fits in current block, limit to that. + mov esi, ecx + + @@: + ; Clear ESI bytes, in EAX indexed block. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + push eax ecx + + xor eax, eax + mov ecx, esi + mov edi, ebx + add edi, edx + + rep stosb + + pop ecx eax + + ; Write the block. + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], esi + sub ecx, esi + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + mov edx, [esp + 4] + call ext2_inode_blank_entry + + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + add [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + mov edx, [esp + 4] + call ext2_inode_blank_entry + + test eax, eax + jnz .error_inode_size + add [esp], ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + test eax, eax + jnz .error + + .success: + xor eax, eax + + .ret: + add esp, 4 + + pop edi esi edx ecx ebx + ret + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Truncates inode to said size. +; Input: eax = inode ID. +; ecx = size to truncate to. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_truncate: + push ebx ecx edx esi edi + + ; Save the inode. + push eax + + ; Read the inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] + cmp ecx, eax + jge .success + + ; Save the size of the inode. + push eax + + ; ECX contains the size we've to truncate. + sub ecx, eax + not ecx + inc ecx + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov esi, edx + + cmp esi, ecx + jbe @F + + ; If the size to truncate is smaller than the un-aligned bytes + ; we're going to have to mark neccessary bytes from the EOF + ; as 0. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + mov edi, [ebp + EXTFS.ext2_save_block] + sub edx, ecx + add edi, edx + + push ecx eax + xor eax, eax + rep stosb + pop eax ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + sub [esp], ecx + jmp .write_inode + + @@: + ; Since ECX is greater than or equal to the bytes here un-aligned + ; just free the block. + call ext2_inode_free_entry + + sub [esp], esi + sub ecx, esi + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + dec eax + + call ext2_inode_free_entry + + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + sub [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + dec eax + + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + mov edi, [ebp + EXTFS.ext2_save_block] + mov edx, [ebp + EXTFS.block_size] + sub edx, ecx + add edi, edx + + push ecx eax + xor eax, eax + rep stosb + pop eax ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + sub [esp], ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + test eax, eax + jnz .error + + .success: + xor eax, eax + + .ret: + add esp, 4 + + pop edi esi edx ecx ebx + ret + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + .error: + xor eax, eax + not eax + jmp .ret diff --git a/kernel/branches/kolibri-process/fs/ext2/resource.inc b/kernel/branches/kolibri-process/fs/ext2/resource.inc new file mode 100644 index 000000000..663632e36 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ext2/resource.inc @@ -0,0 +1,223 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains common resource allocation + freeing code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Frees a resource (block/inode). +; Input: eax = resource ID. +; edi = function pointer of ext2_bg_*_bitmap form, to +; get bitmap of resource. +; ecx = 0, block; 1, inode. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_resource_free: + push ebx edx esi + + ; Get block group. + sub eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + xor edx, edx + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + push eax edx + + call edi + test eax, eax + jz .fail + mov esi, eax + + ; Read the bitmap. + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + pop eax + ; Mark bit free. + call bitmap_clear_bit + test eax, eax + jz @F + + ; No need to save anything. + xor eax, eax + + add esp, 4 + jmp .return + + @@: + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; Read the descriptor. + mov eax, [esp] + call ext2_bg_read_desc + test eax, eax + jz .fail_bg_desc_read + + lea eax, [eax + EXT2_BLOCK_GROUP_DESC.free_blocks_count] + shl ecx, 1 + add eax, ecx + inc word[eax] + + lea eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + shl ecx, 1 + add eax, ecx + inc dword[eax] + + pop eax + call ext2_bg_write_desc + + .return: + pop esi edx ebx + ret + + .fail: + add esp, 4 + .fail_bg_desc_read: + add esp, 4 + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a resource. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; [esp + 4], func pointer to ext2_bg_*_bitmap +; [esp + 8], pointer to free_*_count in SB. +; [esp + 12], *_per_group +; [esp + 16], offset to free_*_count in bg descriptor. +; [esp + 20], *_count +; Output: Resource marked as set in block group. +; eax = error code. +; ebx = resource ID. +;--------------------------------------------------------------------- +ext2_resource_alloc: + ; Block allocation is a pretty serious area, since bad allocation + ; can lead to fragmentation. Thus, the best way to allocate that + ; comes to mind is to allocate around an inode as much as possible. + ; On the other hand, this isn't about a single inode/file/directory, + ; and focusing just around the preferred inode would lead to + ; congestion. Thus, after much thought, the chosen allocation algorithm + ; is to search forward, then backward. + push ecx edx esi edi + + cmp dword[esp + 16 + 8], 0 + jnz @F + + ; No free blocks. + xor eax, eax + not eax + pop edi esi edx ecx + ret 20 + + @@: + ; Calculate which block group the preferred inode belongs to. + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + push eax + push eax + + mov edi, .forward + + .test_block_group: + call dword[esp + 16 + 8 + 4] + test eax, eax + jz .fail + mov esi, eax + + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [esp + 16 + 8 + 12] + call ext2_find_free_bit + cmp eax, 0xFFFFFFFF + jne @F + + mov eax, edi + jmp eax + + @@: + mov ecx, eax + + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; ecx: the index of the matched entry. + ; [esp]: block group where we found. + ; [esp + 4]: starting block group. + ; esi: block group descriptor. + mov eax, [esp] ; Index of block group in which we found. + mul dword[esp + 16 + 8 + 12] + add eax, ecx + mov ebx, eax + + mov eax, [esp + 16 + 8 + 8] + dec dword[eax] + + mov eax, esi + add eax, [esp + 16 + 8 + 16] + dec word[eax] + + pop eax + call ext2_bg_write_desc + + add esp, 4 + jmp .return + + ; Continue forward. + .forward: + inc dword[esp] + mov eax, [esp] + mul dword[esp + 16 + 8 + 12] + cmp eax, [esp + 16 + 8 + 20] + jbe @F + + ; We need to go backward. + mov eax, [esp + 4] + mov [esp], eax + mov edi, .backward + jmp .backward + + @@: + mov eax, [esp] + jmp .test_block_group + + ; Continue backward. + .backward: + cmp dword[esp], 0 + je .fail + + dec dword[esp] + mov eax, [esp] + jmp .test_block_group + + .return: + pop edi esi edx ecx + ret 20 + + .fail: + add esp, 8 + xor eax, eax + not eax + jmp .return \ No newline at end of file diff --git a/kernel/branches/kolibri-process/fs/fat.inc b/kernel/branches/kolibri-process/fs/fat.inc new file mode 100644 index 000000000..aa9f4b8a6 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/fat.inc @@ -0,0 +1,3705 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; FAT32.INC ;; +;; ;; +;; FAT functions for KolibriOS ;; +;; ;; +;; Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it ;; +;; ;; +;; See file COPYING for details ;; +;; 04.02.2007 LFN create folder - diamond ;; +;; 08.10.2006 LFN delete file/folder - diamond ;; +;; 20.08.2006 LFN set file size (truncate/extend) - diamond ;; +;; 17.08.2006 LFN write/append to file - diamond ;; +;; 23.06.2006 LFN start application - diamond ;; +;; 15.06.2006 LFN get/set file/folder info - diamond ;; +;; 27.05.2006 LFN create/rewrite file - diamond ;; +;; 04.05.2006 LFN read folder - diamond ;; +;; 29.04.2006 Elimination of hangup after the ;; +;; expiration hd_wait_timeout - Mario79 ;; +;; 23.04.2006 LFN read file - diamond ;; +;; 28.01.2006 find all Fat16/32 partition in all input point ;; +;; to MBR, see file part_set.inc - Mario79 ;; +;; 15.01.2005 get file size/attr/date, file_append - ATV ;; +;; 04.12.2004 skip volume label, file delete bug fixed - ATV ;; +;; 29.11.2004 get_free_FAT changed, append dir bug fixed - ATV ;; +;; 23.11.2004 don't allow overwrite dir with file - ATV ;; +;; 18.11.2004 get_disk_info and more error codes - ATV ;; +;; 17.11.2004 set_FAT/get_FAT and disk cache rewritten - ATV ;; +;; 10.11.2004 removedir clear whole directory structure - ATV ;; +;; 08.11.2004 rename - ATV ;; +;; 30.10.2004 file_read return also dirsize in bytes - ATV ;; +;; 20.10.2004 Makedir/Removedir - ATV ;; +;; 14.10.2004 Partition chain/Fat16 - ATV (thanks drh3xx) ;; +;; 06.9.2004 Fix free space by Mario79 added - MH ;; +;; 24.5.2004 Write back buffer for File_write -VT ;; +;; 20.5.2004 File_read function to work with syscall 58 - VT ;; +;; 30.3.2004 Error parameters at function return - VT ;; +;; 01.5.2002 Bugfix in device write - VT ;; +;; 20.5.2002 Hd status check - VT ;; +;; 29.6.2002 Improved fat32 verification - VT ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4273 $ + + +cache_max equ 1919 ; max. is 1919*512+0x610000=0x6ffe00 + +PUSHAD_EAX equ [esp+28] +PUSHAD_ECX equ [esp+24] +PUSHAD_EDX equ [esp+20] +PUSHAD_EBX equ [esp+16] +PUSHAD_EBP equ [esp+8] +PUSHAD_ESI equ [esp+4] +PUSHAD_EDI equ [esp+0] + +; Internal data for every FAT partition. +struct FAT PARTITION +fs_type db ? +fat16_root db 0 ; flag for fat16 rootdir +fat_change db 0 ; 1=fat has changed + db ? ; alignment +Lock MUTEX ? ; currently operations with one partition + ; can not be executed in parallel since the + ; legacy code is not ready; this mutex guards + ; all operations +SECTORS_PER_FAT dd 0x1f3a +NUMBER_OF_FATS dd 0x2 +SECTORS_PER_CLUSTER dd 0x8 +BYTES_PER_SECTOR dd 0x200 ; Note: if BPS <> 512 need lots of changes +ROOT_CLUSTER dd 2 ; first rootdir cluster +FAT_START dd 0 ; start of fat table +ROOT_START dd 0 ; start of rootdir (only fat16) +ROOT_SECTORS dd 0 ; count of rootdir sectors (only fat16) +DATA_START dd 0 ; start of data area (=first cluster 2) +LAST_CLUSTER dd 0 ; last availabe cluster +ADR_FSINFO dd 0 ; used only by fat32 + +fatRESERVED dd 0x0FFFFFF6 +fatBAD dd 0x0FFFFFF7 +fatEND dd 0x0FFFFFF8 +fatMASK dd 0x0FFFFFFF + +fatStartScan dd 2 + +cluster_tmp dd 0 ; used by analyze_directory + ; and analyze_directory_to_write + +longname_sec1 dd 0 ; used by analyze_directory to save 2 previous +longname_sec2 dd 0 ; directory sectors for delete long filename + +fat_in_cache dd -1 + +; For FAT16/FAT32, this points to 512-byte buffer for the current sector of FAT. +; For FAT12, the entire FAT structure is read +; and unpacked from 12bit per cluster to word per cluster. +; +; Note: work with unpacked copy of FAT12 means +; additional memory and additional code for packing/unpacking. +; I'm not sure that the economy justifies the cost, but anyway, +; there is how work was done before my edits, and I'm just keeping the principle. +fat_cache_ptr dd ? +fat12_unpacked_ptr dd ? +buffer rb 512 +fsinfo_buffer rb 512 +ends + +uglobal +align 4 +partition_count dd 0 ; partitions found by set_FAT32_variables + +hd_error dd 0 ; set by wait_for_sector_buffer +hd_setup dd 0 +hd_wait_timeout dd 0 + +cache_search_start dd 0 ; used by find_empty_slot +endg + +uglobal +align 4 + Sector512: ; label for dev_hdcd.inc + buffer: + times 512 db 0 +endg + +iglobal +align 4 +fat_user_functions: + dd fat_free + dd (fat_user_functions_end - fat_user_functions - 4) / 4 + dd fat_Read + dd fat_ReadFolder + dd fat_Rewrite + dd fat_Write + dd fat_SetFileEnd + dd fat_GetFileInfo + dd fat_SetFileInfo + dd 0 + dd fat_Delete + dd fat_CreateFolder +fat_user_functions_end: +endg + +; these labels are located before the main function to make +; most of jumps to these be short +fat_create_partition.free_return0: + mov eax, ebp + call free + pop ebp +fat_create_partition.return0: + xor eax, eax + ret +fat_create_partition: +; bootsector must have been successfully read + cmp dword [esp+4], 0 + jnz .return0 +; bootsector signature must be correct + cmp word [ebx+0x1fe], 0xaa55 + jnz .return0 +; sectors per cluster must be nonzero + cmp byte [ebx+0xd], 0 + jz .return0 +; bytes per sector must be 0x200 + cmp word [ebx+0xb], 0x200 + jnz .return0 +; number of fats must be nonzero + cmp byte [ebx+0x10], 0 + jz .return0 +; The only reason to be invalid partition now is FAT12. Since the test for +; FAT size requires knowledge of some calculated values, which are also used +; in the normal operation, let's hope for the best and allocate data now; if +; it will prove wrong, just deallocate it. + movi eax, sizeof.FAT + call malloc + test eax, eax + jz .return0 + mov ecx, dword [ebp+PARTITION.FirstSector] + mov dword [eax+FAT.FirstSector], ecx + mov ecx, dword [ebp+PARTITION.FirstSector+4] + mov dword [eax+FAT.FirstSector+4], ecx + mov ecx, dword [ebp+PARTITION.Length] + mov dword [eax+FAT.Length], ecx + mov ecx, dword [ebp+PARTITION.Length+4] + mov dword [eax+FAT.Length+4], ecx + mov ecx, [ebp+PARTITION.Disk] + mov [eax+FAT.Disk], ecx + mov [eax+FAT.FSUserFunctions], fat_user_functions + or [eax+FAT.fat_in_cache], -1 + mov [eax+FAT.fat_change], 0 + push ebp + mov ebp, eax + + lea ecx, [ebp+FAT.Lock] + call mutex_init + + movzx eax, word [ebx+0xe] ; sectors reserved + mov [ebp+FAT.FAT_START], eax + + movzx eax, byte [ebx+0xd] ; sectors per cluster + mov [ebp+FAT.SECTORS_PER_CLUSTER], eax + + movzx ecx, word [ebx+0xb] ; bytes per sector + mov [ebp+FAT.BYTES_PER_SECTOR], ecx + + movzx eax, word [ebx+0x11] ; count of rootdir entries (=0 fat32) + shl eax, 5 ; mul 32 + dec ecx + add eax, ecx ; round up if not equal count + inc ecx ; bytes per sector + xor edx, edx + div ecx + mov [ebp+FAT.ROOT_SECTORS], eax ; count of rootdir sectors + + movzx eax, word [ebx+0x16] ; sectors per fat <65536 + test eax, eax + jnz @f + mov eax, [ebx+0x24] ; sectors per fat +@@: + mov [ebp+FAT.SECTORS_PER_FAT], eax + + movzx eax, byte [ebx+0x10] ; number of fats + mov [ebp+FAT.NUMBER_OF_FATS], eax + mul [ebp+FAT.SECTORS_PER_FAT] + test edx, edx + jnz .free_return0 + add eax, [ebp+FAT.FAT_START] + jc .free_return0 + mov [ebp+FAT.ROOT_START], eax ; rootdir = fat_start + fat_size * fat_count + add eax, [ebp+FAT.ROOT_SECTORS] ; rootdir sectors should be 0 on fat32 + jc .free_return0 + mov [ebp+FAT.DATA_START], eax ; data area = rootdir + rootdir_size + + movzx eax, word [ebx+0x13] ; total sector count <65536 + test eax, eax + jnz @f + mov eax, [ebx+0x20] ; total sector count +@@: +; total sector count must not exceed partition size + cmp dword [ebp+FAT.Length+4], 0 + jnz @f + cmp eax, dword [ebp+FAT.Length] + ja .free_return0 +@@: + mov dword [ebp+FAT.Length], eax + and dword [ebp+FAT.Length+4], 0 + sub eax, [ebp+FAT.DATA_START] ; eax = count of data sectors + jc .free_return0 + xor edx, edx + div [ebp+FAT.SECTORS_PER_CLUSTER] + inc eax + mov [ebp+FAT.LAST_CLUSTER], eax + dec eax ; cluster count + jz .free_return0 + mov [ebp+FAT.fatStartScan], 2 + + ; limits by Microsoft Hardware White Paper v1.03 + cmp eax, 4085 ; 0xff5 + jb .fat12 + cmp eax, 65525 ; 0xfff5 + jb .fat16 +.fat32: + mov eax, [ebx+0x2c] ; rootdir cluster + mov [ebp+FAT.ROOT_CLUSTER], eax + movzx eax, word [ebx+0x30] + mov [ebp+FAT.ADR_FSINFO], eax + push ebx + add ebx, 512 + call fs_read32_sys + test eax, eax + jnz @f + mov eax, [ebx+0x1ec] + cmp eax, -1 + jz @f + mov [ebp+FAT.fatStartScan], eax +@@: + pop ebx + mov [ebp+FAT.fatRESERVED], 0x0FFFFFF6 + mov [ebp+FAT.fatBAD], 0x0FFFFFF7 + mov [ebp+FAT.fatEND], 0x0FFFFFF8 + mov [ebp+FAT.fatMASK], 0x0FFFFFFF + mov al, 32 +.fat_not_12_finalize: + mov [ebp+FAT.fs_type], al +; For FAT16 and FAT32, allocate 512 bytes for FAT cache. + mov eax, 512 + call malloc + test eax, eax + jz .free_return0 + mov [ebp+FAT.fat_cache_ptr], eax + mov eax, ebp + pop ebp + ret +.fat16: + and [ebp+FAT.ROOT_CLUSTER], 0 + mov [ebp+FAT.fatRESERVED], 0x0000FFF6 + mov [ebp+FAT.fatBAD], 0x0000FFF7 + mov [ebp+FAT.fatEND], 0x0000FFF8 + mov [ebp+FAT.fatMASK], 0x0000FFFF + mov al, 16 + jmp .fat_not_12_finalize +.fat12: + and [ebp+FAT.ROOT_CLUSTER], 0 + mov [ebp+FAT.fatRESERVED], 0xFF6 + mov [ebp+FAT.fatBAD], 0xFF7 + mov [ebp+FAT.fatEND], 0xFFF + mov [ebp+FAT.fatMASK], 0xFFF + mov al, 12 + mov [ebp+FAT.fs_type], al +; For FAT12, allocate&read data for entire table: +; calculate A = ALIGN_UP(NUM_CLUSTERS, 8), +; calculatefatchain/restorefatchain will process A items, +; allocate ALIGN_UP(A*3/2, 512) bytes for FAT table plus A*2 bytes for unpacked data. + mov eax, [ebp+FAT.LAST_CLUSTER] + and eax, not 7 + add eax, 8 + mov edx, eax + lea eax, [eax*3] + add eax, 512*2-1 + shr eax, 10 + shl eax, 9 + lea eax, [eax+edx*2] + call malloc + test eax, eax + jz .free_return0 +; Read ALIGN_UP(NUM_CLUSTERS*3/2, 512) bytes. +; Note that this can be less than allocated, this is ok, +; overallocation simplifies calculatefatchain/restorefatchain. + push ebx + mov [ebp+FAT.fat_cache_ptr], eax + mov edx, [ebp+FAT.LAST_CLUSTER] + lea edx, [(edx+1)*3 + 512*2-1] + shr edx, 10 + xchg eax, ebx + xor eax, eax +.read_fat: + push eax + add eax, [ebp+FAT.FAT_START] + call fs_read32_sys + test eax, eax + pop eax + jz @f + dbgstr 'Failed to read FAT table' + mov eax, [ebp+FAT.fat_cache_ptr] + call free + pop ebx + jmp .free_return0 +@@: + add ebx, 512 + inc eax + cmp eax, edx + jb .read_fat + mov [ebp+FAT.fat12_unpacked_ptr], ebx + call calculatefatchain + pop ebx + mov eax, ebp + pop ebp + ret + +fat_free: + push eax + mov eax, [eax+FAT.fat_cache_ptr] + call free + pop eax + jmp free + +calculatefatchain: + + pushad + + mov esi, [ebp+FAT.fat_cache_ptr] + mov edi, [ebp+FAT.fat12_unpacked_ptr] + + mov edx, [ebp+FAT.LAST_CLUSTER] + and edx, not 7 + lea edx, [edi+(edx+8)*2] + push edx + + fcnew: + mov eax, dword [esi] + mov ebx, dword [esi+4] + mov ecx, dword [esi+8] + mov edx, ecx + shr edx, 4;8 ok + shr dx, 4;7 ok + xor ch, ch + shld ecx, ebx, 20;6 ok + shr cx, 4;5 ok + shld ebx, eax, 12 + and ebx, 0x0fffffff;4 ok + shr bx, 4;3 ok + shl eax, 4 + and eax, 0x0fffffff;2 ok + shr ax, 4;1 ok + mov dword [edi], eax + mov dword [edi+4], ebx + mov dword [edi+8], ecx + mov dword [edi+12], edx + add edi, 16 + add esi, 12 + + cmp edi, [esp] + jnz fcnew + pop eax + + popad + ret + + +restorefatchain: ; restore fat chain + + pushad + + mov esi, [ebp+FAT.fat12_unpacked_ptr] + mov edi, [ebp+FAT.fat_cache_ptr] + + mov edx, [ebp+FAT.LAST_CLUSTER] + and edx, not 7 + lea edx, [esi+(edx+8)*2] + + fcnew2: + mov eax, dword [esi] + mov ebx, dword [esi+4] + shl ax, 4 + shl eax, 4 + shl bx, 4 + shr ebx, 4 + shrd eax, ebx, 8 + shr ebx, 8 + mov dword [edi], eax + mov word [edi+4], bx + add edi, 6 + add esi, 8 + + cmp esi, edx + jb fcnew2 + + mov esi, [ebp+FAT.NUMBER_OF_FATS] + mov edx, [ebp+FAT.LAST_CLUSTER] + lea edx, [(edx+1)*3 + 512*2-1] + shr edx, 10 + push [ebp+FAT.FAT_START] + +.write_fats: + xor eax, eax + mov ebx, [ebp+FAT.fat_cache_ptr] +.loop1: + push eax + add eax, [esp+4] + call fs_write32_sys + test eax, eax + pop eax + jnz .fail + add ebx, 512 + inc eax + cmp eax, edx + jb .loop1 + pop eax + add eax, [ebp+FAT.SECTORS_PER_FAT] + push eax + dec esi + jnz .write_fats + pop eax + + popad + ret +.fail: + dbgstr 'Failed to save FAT' + popad + ret + +iglobal +label fat_legal_chars byte +; 0 = not allowed +; 1 = allowed only in long names +; 3 = allowed + times 32 db 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 +; @ 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 +; 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 +; ` 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 +; 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 +endg + +fat_name_is_legal: +; in: esi->(long) name +; out: CF set <=> legal +; destroys 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 +.err: + 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 +.done: + 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 +; tilde is not found, insert "~1" at end + add edi, 6 + cmp word [edi], ' ' + jnz .insert_tilde +@@: + dec edi + cmp byte [edi], ' ' + jz @b + inc edi +.insert_tilde: + mov word [edi], '~1' + popad + clc + ret +.tilde: + 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 +.space: + dec edi + inc ecx + jmp @b +.found: + inc byte [edi] + add dword [esp], 8 + jmp .zerorest +.break: + jecxz .noplace + inc edi + mov al, '1' +@@: + xchg al, [edi] + inc edi + cmp al, ' ' + mov al, '0' + jnz @b +.succ: + 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 +@@: + mov byte [edi], '0' +.zerorest: + inc edi + cmp edi, [esp] + jb @b + pop edi + popad + ;clc ; automatically + ret +.err: + 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 + movi ebx, 8 + 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 +.inv_symbol: + mov al, '_' + or bh, 1 +.symbol: + cmp al, '.' + jz .dot +.normal_symbol: + dec bl + jns .store + mov bl, 0 +.space: + or bh, 1 + jmp .loop +.store: + 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 +.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 +@@: + cmp edi, ecx + jbe .skip +@@: + dec edi + mov al, [edi] + dec ebx + mov [ebx], al + mov byte [edi], ' ' + cmp edi, ecx + ja @b +.skip: + mov bh, 3 + jmp @f +.firstdot: + cmp bl, 8 + jz .space + push edi + or bh, 2 +@@: + mov edi, ecx + mov bl, 3 + jmp .loop +.done: + test bh, 2 + jz @f + pop edi +@@: + lea edi, [ecx-8] + test bh, 1 + jz @f + call fat_next_short_name +@@: + popad + ret + +fat12_free_space: +;--------------------------------------------- +; +; returns free space in edi +; rewr.by Mihasik +;--------------------------------------------- + + push eax ebx ecx + + mov edi, [ebp+FAT.fat12_unpacked_ptr];start of FAT + xor ax, ax;Free cluster=0x0000 in FAT + xor ebx, ebx;counter + mov ecx, [ebp+FAT.LAST_CLUSTER] + inc ecx + cld + 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 + + + +set_FAT: +;-------------------------------- +; input : EAX = cluster +; EDX = value to save +; EBP = pointer to FAT structure +; output : EDX = old value +;-------------------------------- +; out: CF set <=> error + push eax ebx esi + + cmp eax, 2 + jb sfc_error + cmp eax, [ebp+FAT.LAST_CLUSTER] + ja sfc_error + cmp [ebp+FAT.fs_type], 12 + je set_FAT12 + cmp [ebp+FAT.fs_type], 16 + je sfc_1 + add eax, eax + sfc_1: + add eax, eax + mov esi, 511 + and esi, eax ; esi = position in fat sector + shr eax, 9 ; eax = fat sector + add eax, [ebp+FAT.FAT_START] + mov ebx, [ebp+FAT.fat_cache_ptr] + + cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory? + je sfc_in_cache ; yes + + cmp [ebp+FAT.fat_change], 0; is fat changed? + je sfc_no_change ; no + call write_fat_sector; yes. write it into disk + jc sfc_error + + sfc_no_change: + mov [ebp+FAT.fat_in_cache], eax; save fat sector + call fs_read32_sys + test eax, eax + jne sfc_error + + + sfc_in_cache: + cmp [ebp+FAT.fs_type], 16 + jne sfc_test32 + + sfc_set16: + xchg [ebx+esi], dx ; save new value and get old value + jmp sfc_write + + sfc_test32: + mov eax, [ebp+FAT.fatMASK] + + sfc_set32: + and edx, eax + xor eax, -1 ; mask for high bits + and eax, [ebx+esi] ; get high 4 bits + or eax, edx + mov edx, [ebx+esi] ; get old value + mov [ebx+esi], eax ; save new value + + sfc_write: + mov [ebp+FAT.fat_change], 1; fat has changed + + sfc_nonzero: + and edx, [ebp+FAT.fatMASK] + + sfc_return: + pop esi ebx eax + ret + sfc_error: + stc + jmp sfc_return + + set_FAT12: + test edx, 0xF000 + jnz sfc_error + mov ebx, [ebp+FAT.fat12_unpacked_ptr] + xchg [ebx+eax*2], dx + mov [ebp+FAT.fat_change], 1 + pop esi ebx eax + clc + ret + +get_FAT: +;-------------------------------- +; input : EAX = cluster +; EBP = pointer to FAT structure +; output : EAX = next cluster +;-------------------------------- +; out: CF set <=> error + push ebx esi + + cmp [ebp+FAT.fs_type], 12 + je get_FAT12 + + cmp [ebp+FAT.fs_type], 16 + je gfc_1 + add eax, eax + gfc_1: + add eax, eax + mov esi, 511 + and esi, eax ; esi = position in fat sector + shr eax, 9 ; eax = fat sector + add eax, [ebp+FAT.FAT_START] + mov ebx, [ebp+FAT.fat_cache_ptr] + + cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory? + je gfc_in_cache + + cmp [ebp+FAT.fat_change], 0; is fat changed? + je gfc_no_change ; no + call write_fat_sector; yes. write it into disk + jc hd_error_01 + + gfc_no_change: + mov [ebp+FAT.fat_in_cache], eax + call fs_read32_sys + test eax, eax + jne hd_error_01 + + gfc_in_cache: + mov eax, [ebx+esi] + and eax, [ebp+FAT.fatMASK] + gfc_return: + pop esi ebx + ret + hd_error_01: + stc + jmp gfc_return + +get_FAT12: + mov ebx, [ebp+FAT.fat12_unpacked_ptr] + movzx eax, word [ebx+eax*2] + pop esi ebx + clc + ret + + +get_free_FAT: +;----------------------------------------------------------- +; output : if CARRY=0 EAX = # first cluster found free +; if CARRY=1 disk full +; Note : for more speed need to use fat_cache directly +;----------------------------------------------------------- + push ecx + mov ecx, [ebp+FAT.LAST_CLUSTER]; counter for full disk + mov eax, [ebp+FAT.fatStartScan] + cmp [ebp+FAT.fs_type], 12 + jz get_free_FAT12 + dec ecx + cmp eax, 2 + jb gff_reset + + gff_test: + cmp eax, [ebp+FAT.LAST_CLUSTER]; if above last cluster start at cluster 2 + jbe gff_in_range + gff_reset: + mov eax, 2 + + gff_in_range: + push eax + call get_FAT ; get cluster state + jc gff_not_found_1 + + test eax, eax ; is it free? + pop eax + je gff_found ; yes + inc eax ; next cluster + dec ecx ; is all checked? + jnz gff_test ; no + + gff_not_found: + pop ecx ; yes. disk is full + stc + ret + + gff_not_found_1: + pop eax + jmp gff_not_found + + gff_found: + lea ecx, [eax+1] + mov [ebp+FAT.fatStartScan], ecx + pop ecx + clc + ret + +get_free_FAT12: + push edx edi + mov edi, [ebp+FAT.fat12_unpacked_ptr] + cmp eax, 2 + jb .reset + cmp eax, ecx + jbe @f +.reset: + mov eax, 2 +@@: + mov edx, eax + lea edi, [edi+eax*2] + sub ecx, eax + inc ecx + xor eax, eax + repnz scasw + jz .found + cmp edx, 2 + jz .notfound + mov edi, [ebp+FAT.fat12_unpacked_ptr] + lea ecx, [edx-2] + repnz scasw + jnz .notfound +.found: + sub edi, [ebp+FAT.fat12_unpacked_ptr] + shr edi, 1 + mov [ebp+FAT.fatStartScan], edi + lea eax, [edi-1] + pop edi edx ecx + ret +.notfound: + pop edi edx ecx + stc + ret + + +write_fat_sector: +;----------------------------------------------------------- +; write changed fat to disk +;----------------------------------------------------------- + push eax ebx ecx + + mov [ebp+FAT.fat_change], 0 + mov eax, [ebp+FAT.fat_in_cache] + cmp eax, -1 + jz write_fat_not_used + mov ebx, [ebp+FAT.fat_cache_ptr] + mov ecx, [ebp+FAT.NUMBER_OF_FATS] + + write_next_fat: + push eax + call fs_write32_sys + test eax, eax + pop eax + jnz write_fat_not_used + + add eax, [ebp+FAT.SECTORS_PER_FAT] + dec ecx + jnz write_next_fat + + write_fat_not_used: + pop ecx ebx eax + ret + + + + + +bcd2bin: +;---------------------------------- +; input : AL=BCD number (eg. 0x11) +; output : AH=0 +; AL=decimal number (eg. 11) +;---------------------------------- + xor ah, ah + shl ax, 4 + shr al, 4 + aad + ret + + +get_date_for_file: +;----------------------------------------------------- +; Get date from CMOS and pack day,month,year in AX +; DATE bits 0..4 : day of month 0..31 +; 5..8 : month of year 1..12 +; 9..15 : count of years from 1980 +;----------------------------------------------------- + mov al, 0x7 ;day + out 0x70, al + in al, 0x71 + call bcd2bin + ror eax, 5 + + mov al, 0x8 ;month + out 0x70, al + in al, 0x71 + call bcd2bin + ror eax, 4 + + mov al, 0x9 ;year + out 0x70, al + in al, 0x71 + call bcd2bin + add ax, 20 ;because CMOS return only the two last + ;digit (eg. 2000 -> 00 , 2001 -> 01) and we + rol eax, 9 ;need the difference with 1980 (eg. 2001-1980) + ret + + +get_time_for_file: +;----------------------------------------------------- +; Get time from CMOS and pack hour,minute,second in AX +; TIME bits 0..4 : second (the low bit is lost) +; 5..10 : minute 0..59 +; 11..15 : hour 0..23 +;----------------------------------------------------- + mov al, 0x0 ;second + out 0x70, al + in al, 0x71 + call bcd2bin + ror eax, 6 + + mov al, 0x2 ;minute + out 0x70, al + in al, 0x71 + call bcd2bin + ror eax, 6 + + mov al, 0x4 ;hour + out 0x70, al + in al, 0x71 + call bcd2bin + rol eax, 11 + ret + + +set_current_time_for_entry: +;----------------------------------------------------- +; Set current time/date for file entry +; input : ebx = file entry pointer +;----------------------------------------------------- + push eax + call get_time_for_file; update files date/time + mov [ebx+22], ax + call get_date_for_file + mov [ebx+24], ax + pop eax + ret + + + +add_disk_free_space: +;----------------------------------------------------- +; input : ecx = cluster count +; Note : negative = remove clusters from free space +; positive = add clusters to free space +;----------------------------------------------------- + test ecx, ecx ; no change + je add_dfs_no + cmp [ebp+FAT.fs_type], 32 ; free disk space only used by fat32 + jne add_dfs_no + + push eax ebx + mov eax, [ebp+FAT.ADR_FSINFO] + lea ebx, [ebp+FAT.fsinfo_buffer] + call fs_read32_sys + test eax, eax + jnz add_not_fs + + cmp dword [ebx+0x1fc], 0xaa550000; check sector id + jne add_not_fs + + add [ebx+0x1e8], ecx + push [ebp+FAT.fatStartScan] + pop dword [ebx+0x1ec] + mov eax, [ebp+FAT.ADR_FSINFO] + call fs_write32_sys +; jc add_not_fs + + add_not_fs: + pop ebx eax + + add_dfs_no: + ret + + + +clear_cluster_chain: +;----------------------------------------------------- +; input : eax = first cluster +;----------------------------------------------------- + push eax ecx edx + xor ecx, ecx ; cluster count + + clean_new_chain: + cmp eax, [ebp+FAT.LAST_CLUSTER]; end of file + ja delete_OK + cmp eax, 2 ; unfinished fat chain or zero length file + jb delete_OK + cmp eax, [ebp+FAT.ROOT_CLUSTER]; don't remove root cluster + jz delete_OK + + xor edx, edx + call set_FAT ; clear fat entry + jc access_denied_01 + + inc ecx ; update cluster count + mov eax, edx ; old cluster + jmp clean_new_chain + + delete_OK: + call add_disk_free_space; add clusters to free disk space + clc + access_denied_01: + pop edx ecx eax + ret + + +if 0 +get_hd_info: +;----------------------------------------------------------- +; output : eax = 0 - ok +; 3 - unknown FS +; 10 - access denied +; edx = cluster size in bytes +; ebx = total clusters on disk +; ecx = free clusters on disk +;----------------------------------------------------------- + cmp [ebp+FAT.fs_type], 16 + jz info_fat_ok + cmp [ebp+FAT.fs_type], 32 + jz info_fat_ok + xor edx, edx + xor ebx, ebx + xor ecx, ecx + mov eax, ERROR_UNKNOWN_FS + ret + + info_fat_ok: +; call reserve_hd1 + + xor ecx, ecx ; count of free clusters + mov eax, 2 + mov ebx, [ebp+FAT.LAST_CLUSTER] + + info_cluster: + push eax + call get_FAT ; get cluster info + jc info_access_denied + + test eax, eax ; is it free? + jnz info_used ; no + inc ecx + + info_used: + pop eax + inc eax + cmp eax, ebx ; is above last cluster? + jbe info_cluster ; no. test next cluster + + dec ebx ; cluster count + imul edx, [ebp+FAT.SECTORS_PER_CLUSTER], 512; cluster size in bytes + xor eax, eax + ret + + info_access_denied: + add esp, 4 + xor edx, edx + xor ebx, ebx + xor ecx, ecx + mov eax, ERROR_ACCESS_DENIED + ret +end if + +update_disk: + cmp [ebp+FAT.fat_change], 0 ; is fat changed? + je upd_no_change + cmp [ebp+FAT.fs_type], 12 + jz .fat12 +;----------------------------------------------------------- +; write changed fat and cache to disk +;----------------------------------------------------------- + + call write_fat_sector + jc update_disk_acces_denied + jmp upd_no_change +.fat12: + call restorefatchain + mov [ebp+FAT.fat_change], 0 + + upd_no_change: + + push esi + mov esi, [ebp+PARTITION.Disk] + call disk_sync + pop esi + update_disk_acces_denied: + ret + +fat_lock: + lea ecx, [ebp+FAT.Lock] + jmp mutex_lock +fat_unlock: + lea ecx, [ebp+FAT.Lock] + jmp mutex_unlock + +; \begin{diamond} +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 +.unk: + mov al, '_' + jmp .doit +.yo1: + mov al, 0xF0 ; 'Ё' + jmp .doit +.yo2: + mov al, 0xF1 ; 'ё' + jmp .doit +.rus1: +; 0x410-0x43F -> 0x80-0xAF + add al, 0x70 + jmp .doit +.rus2: +; 0x440-0x44F -> 0xE0-0xEF + add al, 0xA0 +.ascii: +.doit: + stosb + jmp uni2ansi_str +.done: + mov byte [edi], 0 + ret + +ansi2uni_char: +; convert ANSI character in al to UNICODE character in ax, using cp866 encoding + mov ah, 0 +; 0x00-0x7F - trivial map + cmp al, 0x80 + jb .ret +; 0x80-0xAF -> 0x410-0x43F + cmp al, 0xB0 + jae @f + add ax, 0x410-0x80 +.ret: + ret +@@: +; 0xE0-0xEF -> 0x440-0x44F + cmp al, 0xE0 + jb .unk + cmp al, 0xF0 + jae @f + add ax, 0x440-0xE0 + ret +; 0xF0 -> 0x401 +; 0xF1 -> 0x451 +@@: + cmp al, 0xF0 ; 'Ё' + jz .yo1 + cmp al, 0xF1 ; 'ё' + jz .yo2 +.unk: + mov al, '_' ; ah=0 + ret +.yo1: + mov ax, 0x401 + ret +.yo2: + 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, 0xF1 ; 'ё' + jz .yo1 + cmp al, 0xA0 ; 'а' + jb .ret + cmp al, 0xE0 ; 'р' + jb .rus1 + cmp al, 0xEF ; 'я' + ja .ret +; 0xE0-0xEF -> 0x90-0x9F + sub al, 0xE0-0x90 +.ret: + ret +.rus1: +; 0xA0-0xAF -> 0x80-0x8F +.az: + and al, not 0x20 + ret +.yo1: +; 0xF1 -> 0xF0 + dec ax + ret + +fat_get_name: +; in: edi->FAT entry +; out: CF=1 - no valid entry +; else CF=0 and ebp->ASCIIZ-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 +.no: + 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 + + mov eax, [edi] + mov ecx, [edi+4] + mov [ebp], eax + mov [ebp+4], ecx + + mov ecx, 8 +@@: + cmp byte [ebp+ecx-1], ' ' + loope @b + + mov eax, [edi+8] + cmp al, ' ' + je .done + shl eax, 8 + mov al, '.' + + lea ebp, [ebp+ecx+1] + mov [ebp], eax + mov ecx, 3 +@@: + 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 +.unicode_short: + mov ecx, 8 + push 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 +@@: + 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 +@@: + 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 +.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 +@@: + 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 +; ... done + pop eax + sub ebp, eax + test eax, eax + jz @f +; if this is not first entry, more processing required + stc + ret +@@: +; if this is first entry: + 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 +.ret: + clc + ret + +fat_compare_name: +; compares ASCIIZ-names, case-insensitive (cp866 encoding) +; in: esi->name, ebp->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 +.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 +.done: + cmp al, '/' + jnz @f + cmp byte [esp], 0 + jnz @f + mov [esp+4], esi +@@: + pop eax + pop esi ebp + ret + +fat_find_lfn: +; in: esi->name +; [esp+4] = next +; [esp+8] = first +; [esp+C]... - possibly parameters for first and next +; out: CF=1 - file not found, eax=error code +; else CF=0, esi->next name component, edi->direntry + pusha + lea eax, [esp+0Ch+20h] + call dword [eax-4] + jc .reterr + sub esp, 262*2 ; reserve place for LFN + push 0 ; for fat_get_name: read ASCII name +.l1: + lea ebp, [esp+4] + call fat_get_name + jc .l2 + call fat_compare_name + jz .found +.l2: + mov ebp, [esp+8+262*2+4] + lea eax, [esp+0Ch+20h+262*2+4] + call dword [eax-8] + jnc .l1 + add esp, 262*2+4 +.reterr: + mov [esp+28], eax + stc + popa + ret +.found: + add esp, 262*2+4 + mov ebp, [esp+8] +; if this is LFN entry, advance to true entry + cmp byte [edi+11], 0xF + jnz @f + lea eax, [esp+0Ch+20h] + call dword [eax-8] + jc .reterr +@@: + add esp, 8 ; CF=0 + push esi + push edi + popa + 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 + +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 + +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 + +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 + +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 +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 +@@: + mov esi, edi + pop edi ecx +.ret: + ret +.ansi: + 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 +@@: + 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 + +hd_find_lfn: +; in: ebp -> FAT structure +; in: esi+[esp+4] -> name +; out: CF=1 - file not found, eax=error code +; else CF=0 and edi->direntry, eax=sector +; destroys eax + push esi edi + push 0 + push 0 + push fat1x_root_first + push fat1x_root_next + mov eax, [ebp+FAT.ROOT_CLUSTER] + cmp [ebp+FAT.fs_type], 32 + jz .fat32 +.loop: + and [ebp+FAT.longname_sec1], 0 + and [ebp+FAT.longname_sec2], 0 + call fat_find_lfn + jc .notfound + cmp byte [esi], 0 + jz .found +.continue: + test byte [edi+11], 10h + jz .notfound + and dword [esp+12], 0 + mov eax, [edi+20-2] + mov ax, [edi+26] ; cluster +.fat32: + mov [esp+8], eax + mov dword [esp+4], fat_notroot_first + mov dword [esp], fat_notroot_next + jmp .loop +.notfound: + add esp, 16 + pop edi esi + stc + ret 4 +.found: + lea eax, [esp+4+24] + cmp dword [eax], 0 + jz @f + mov esi, [eax] + and dword [eax], 0 + jmp .continue +@@: + lea eax, [esp+8] + cmp dword [eax], 0 + jz .root + call fat_get_sector + jmp .cmn +.root: + mov eax, [eax+4] + add eax, [ebp+FAT.ROOT_START] +.cmn: + add esp, 20 ; CF=0 + pop esi + ret 4 + +;---------------------------------------------------------------- +; fat_Read - FAT implementation of reading a file +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_Read: + call fat_lock + push edi + cmp byte [esi], 0 + jnz @f +.noaccess: + pop edi +.noaccess_2: + call fat_unlock + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret + +@@: + stdcall hd_find_lfn, [esp+4+4] + jnc .found + pop edi + push eax + call fat_unlock + pop eax + or ebx, -1 + ret + +.found: + test byte [edi+11], 0x10; do not allow read directories + jnz .noaccess + cmp dword [ebx+8], 0 + jz @f + xor ebx, ebx +.reteof: + call fat_unlock + mov eax, ERROR_END_OF_FILE + pop edi + ret +@@: + mov ecx, [ebx+12] ; size + mov edx, [ebx+16] ; pointer + mov ebx, [ebx+4] ; file offset + push edx + push 0 + mov eax, [edi+28] + sub eax, ebx + jb .eof + cmp eax, ecx + jae @f + mov ecx, eax + mov byte [esp], 6 +@@: + mov eax, [edi+20-2] + mov ax, [edi+26] +; now eax=cluster, ebx=position, ecx=count, edx=buffer for data +.new_cluster: + jecxz .new_sector + cmp eax, 2 + jb .eof + cmp eax, [ebp+FAT.fatRESERVED] + jae .eof + mov [ebp+FAT.cluster_tmp], eax + dec eax + dec eax + mov edi, [ebp+FAT.SECTORS_PER_CLUSTER] + imul eax, edi + add eax, [ebp+FAT.DATA_START] +.new_sector: + test ecx, ecx + jz .done + sub ebx, 512 + jae .skip + add ebx, 512 + jnz .force_buf + cmp ecx, 512 + jb .force_buf +; we may read directly to given buffer + push eax ebx + mov ebx, edx + call fs_read32_app + test eax, eax + pop ebx eax + jne .noaccess_1 + add edx, 512 + sub ecx, 512 + jmp .skip +.force_buf: +; we must read sector to temporary buffer and then copy it to destination + push eax ebx + lea ebx, [ebp+FAT.buffer] + call fs_read32_app + test eax, eax + mov eax, ebx + pop ebx + jne .noaccess_3 + add eax, ebx + push ecx + add ecx, ebx + cmp ecx, 512 + jbe @f + mov ecx, 512 +@@: + sub ecx, ebx + mov ebx, edx + call memmove + add edx, ecx + sub [esp], ecx + pop ecx + pop eax + xor ebx, ebx +.skip: + inc eax + dec edi + jnz .new_sector + mov eax, [ebp+FAT.cluster_tmp] + call get_FAT + jc .noaccess_1 + + jmp .new_cluster +.noaccess_3: + pop eax +.noaccess_1: + pop eax + push ERROR_DEVICE +.done: + mov ebx, edx + call fat_unlock + pop eax edx edi + sub ebx, edx + ret +.eof: + mov ebx, edx + pop eax edx + sub ebx, edx + jmp .reteof + +;---------------------------------------------------------------- +; fat_ReadFolder - FAT implementation of reading a folder +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_ReadFolder: + call fat_lock + mov eax, [ebp+FAT.ROOT_CLUSTER] + push edi + cmp byte [esi], 0 + jz .doit + stdcall hd_find_lfn, [esp+4+4] + jnc .found + pop edi + push eax + call fat_unlock + pop eax + or ebx, -1 + ret +.found: + test byte [edi+11], 0x10 ; do not allow read files + jnz .found_dir + pop edi + call fat_unlock + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret +.found_dir: + mov eax, [edi+20-2] + mov ax, [edi+26] ; eax=cluster +.doit: + push esi + sub esp, 262*2 ; reserve space for LFN + push dword [ebx+8] ; for fat_get_name: read ANSI/UNICODE name + mov edx, [ebx+16] ; pointer to buffer +; init header + push eax + mov edi, edx + mov ecx, 32/4 + xor eax, eax + rep stosd + pop eax + mov byte [edx], 1 ; version + mov esi, edi ; esi points to BDFE + mov ecx, [ebx+12] ; number of blocks to read + mov ebx, [ebx+4] ; index of the first block +.new_cluster: + mov [ebp+FAT.cluster_tmp], eax + test eax, eax + jnz @f + cmp [ebp+FAT.fs_type], 32 + jz .notfound + mov eax, [ebp+FAT.ROOT_START] + push [ebp+FAT.ROOT_SECTORS] + push ebx + jmp .new_sector +@@: + dec eax + dec eax + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + push [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + push ebx +.new_sector: + lea ebx, [ebp+FAT.buffer] + mov edi, ebx + push eax + call fs_read32_sys + test eax, eax + pop eax + jnz .notfound2 + add ebx, 512 + push eax +.l1: + push ebp + lea ebp, [esp+20] + call fat_get_name + pop ebp + jc .l2 + cmp byte [edi+11], 0xF + jnz .do_bdfe + add edi, 0x20 + cmp edi, ebx + jb .do_bdfe + pop eax + inc eax + dec dword [esp+4] + jnz @f + mov eax, [ebp+FAT.cluster_tmp] + test eax, eax + jz .done + call get_FAT + jc .notfound2 + cmp eax, 2 + jb .done + cmp eax, [ebp+FAT.fatRESERVED] + jae .done + push eax + mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] + mov [esp+8], eax + pop eax + mov [ebp+FAT.cluster_tmp], eax + dec eax + dec eax + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] +@@: + lea ebx, [ebp+FAT.buffer] + mov edi, ebx + push eax + call fs_read32_sys + test eax, eax + pop eax + jnz .notfound2 + add ebx, 512 + push eax +.do_bdfe: + inc dword [edx+8] ; new file found + dec dword [esp+4] + jns .l2 + dec ecx + js .l2 + inc dword [edx+4] ; new file block copied + push ebp + lea ebp, [esp+20] + call fat_entry_to_bdfe + pop ebp +.l2: + add edi, 0x20 + cmp edi, ebx + jb .l1 + pop eax + inc eax + dec dword [esp+4] + jnz .new_sector + mov eax, [ebp+FAT.cluster_tmp] + test eax, eax + jz .done + call get_FAT + jc .notfound2 + cmp eax, 2 + jb .done + cmp eax, [ebp+FAT.fatRESERVED] + jae .done + push eax + mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] + mov [esp+8], eax + pop eax + pop ebx + add esp, 4 + jmp .new_cluster +.notfound2: + add esp, 8 +.notfound: + add esp, 262*2+4 + pop esi edi + mov ebx, [edx+4] + call fat_unlock + mov eax, ERROR_DEVICE + ret +.done: + add esp, 262*2+4+8 + mov ebx, [edx+4] + xor eax, eax + dec ecx + js @f + mov al, ERROR_END_OF_FILE +@@: + push eax + call fat_unlock + pop eax + pop esi edi + ret + +fat1x_root_next: + push ecx + lea ecx, [ebp+FAT.buffer+0x200-0x20] + cmp edi, ecx + jae fat1x_root_next_sector + pop ecx + add edi, 0x20 + ret ; CF=0 +fat1x_root_next_sector: +; read next sector + push [ebp+FAT.longname_sec2] + pop [ebp+FAT.longname_sec1] + mov ecx, [eax+4] + push ecx + add ecx, [ebp+FAT.ROOT_START] + mov [ebp+FAT.longname_sec2], ecx + pop ecx + inc ecx + mov [eax+4], ecx + cmp ecx, [ebp+FAT.ROOT_SECTORS] + pop ecx + jb fat1x_root_first + mov eax, ERROR_FILE_NOT_FOUND + stc + ret +fat1x_root_first: + mov eax, [eax+4] + add eax, [ebp+FAT.ROOT_START] + push ebx + lea edi, [ebp+FAT.buffer] + mov ebx, edi + call fs_read32_sys + pop ebx + test eax, eax + jnz .readerr + ret ; CF=0 +.readerr: + mov eax, ERROR_DEVICE + stc + ret +.notfound: + mov eax, ERROR_FILE_NOT_FOUND + stc + ret +fat1x_root_begin_write: + push edi eax + call fat1x_root_first + pop eax edi + ret +fat1x_root_end_write: + pusha + mov eax, [eax+4] + add eax, [ebp+FAT.ROOT_START] + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + popa + ret +fat1x_root_next_write: + push ecx + lea ecx, [ebp+FAT.buffer+0x200] + cmp edi, ecx + jae @f + pop ecx + ret +@@: + call fat1x_root_end_write + jmp fat1x_root_next_sector +fat1x_root_extend_dir: + stc + ret + +fat_notroot_next: + push ecx + lea ecx, [ebp+FAT.buffer+0x200-0x20] + cmp edi, ecx + jae fat_notroot_next_sector + pop ecx + add edi, 0x20 + ret ; CF=0 +fat_notroot_next_sector: + push [ebp+FAT.longname_sec2] + pop [ebp+FAT.longname_sec1] + push eax + call fat_get_sector + mov [ebp+FAT.longname_sec2], eax + pop eax + mov ecx, [eax+4] + inc ecx + cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + jae fat_notroot_next_cluster + mov [eax+4], ecx + jmp @f +fat_notroot_next_cluster: + push eax + mov eax, [eax] + call get_FAT + mov ecx, eax + pop eax + jc fat_notroot_first.deverr + cmp ecx, 2 + jb fat_notroot_next_err + cmp ecx, [ebp+FAT.fatRESERVED] + jae fat_notroot_next_err + mov [eax], ecx + and dword [eax+4], 0 +@@: + pop ecx +fat_notroot_first: + call fat_get_sector + push ebx + lea edi, [ebp+FAT.buffer] + mov ebx, edi + call fs_read32_sys + pop ebx + test eax, eax + jz .ret ; CF=0 + push ecx +.deverr: + pop ecx + mov eax, ERROR_DEVICE + stc +.ret: + ret +fat_notroot_next_err: + pop ecx + mov eax, ERROR_FILE_NOT_FOUND + stc + ret +fat_notroot_begin_write: + push eax edi + call fat_notroot_first + pop edi eax + ret +fat_notroot_end_write: + call fat_get_sector + push ebx + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + pop ebx + ret +fat_notroot_next_write: + push ecx + lea ecx, [ebp+FAT.buffer+0x200] + cmp edi, ecx + jae @f + pop ecx + ret +@@: + push eax + call fat_notroot_end_write + pop eax + jmp fat_notroot_next_sector +fat_notroot_extend_dir: + push eax + call get_free_FAT + jnc .found + pop eax + ret ; CF=1 +.found: + push edx + mov edx, [ebp+FAT.fatEND] + call set_FAT + jc .writeerr + mov edx, eax + mov eax, [esp+4] + mov eax, [eax] + push edx + call set_FAT + pop edx + jnc @f +.writeerr: + pop edx + pop eax + stc + ret +@@: + push ecx + or ecx, -1 + call add_disk_free_space +; zero new cluster + mov ecx, 512/4 + lea edi, [ebp+FAT.buffer] + push edi + xor eax, eax + rep stosd + pop edi + pop ecx + mov eax, [esp+4] + mov [eax], edx + and dword [eax+4], 0 + pop edx + mov eax, [eax] + dec eax + dec eax + push ebx ecx + mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + imul eax, ecx + add eax, [ebp+FAT.DATA_START] + mov ebx, edi +@@: + push eax + call fs_write32_sys + pop eax + inc eax + loop @b + pop ecx ebx eax + clc + ret + +fat_get_sector: + push ecx + mov ecx, [eax] + dec ecx + dec ecx + imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + add ecx, [ebp+FAT.DATA_START] + add ecx, [eax+4] + mov eax, ecx + pop ecx + ret + +fshrad: + call fat_unlock + mov eax, ERROR_ACCESS_DENIED + xor ebx, ebx + ret + +;---------------------------------------------------------------- +; fat_CreateFolder - FAT implementation of creating a folder +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_CreateFolder: + push 1 + jmp fat_Rewrite.common + +;---------------------------------------------------------------- +; fat_Rewrite - FAT implementation of creating a new file +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_Rewrite: + push 0 +.common: + call fat_lock + pop eax + cmp byte [esi], 0 + jz fshrad + mov ecx, [ebx+12] + mov edx, [ebx+16] + pushad + xor edi, edi + mov edx, [esp+4+20h] + push esi + test edx, edx + jz @f + mov esi, edx +@@: + lodsb + test al, al + jz @f + cmp al, '/' + jnz @b + lea edi, [esi-1] + jmp @b +@@: + pop esi + test edi, edi + jnz .noroot + test edx, edx + jnz .hasebp + mov edx, [ebp+FAT.ROOT_CLUSTER] + cmp [ebp+FAT.fs_type], 32 + jz .pushnotroot + xor edx, edx + push edx + push fat1x_root_extend_dir + push fat1x_root_end_write + push fat1x_root_next_write + push fat1x_root_begin_write + push edx + push edx + push fat1x_root_first + push fat1x_root_next + jmp .common1 +.hasebp: + mov eax, ERROR_ACCESS_DENIED + cmp byte [edx], 0 + jz .ret1 + stdcall hd_find_lfn, 0 + mov esi, [esp+4+20h] + jc .ret1 + jmp .common0 +.noroot: + mov eax, ERROR_ACCESS_DENIED + cmp byte [edi+1], 0 + jz .ret1 +; check existence + mov byte [edi], 0 + push edi + stdcall hd_find_lfn, [esp+4+24h] + pop esi + mov byte [esi], '/' + jnc @f +.notfound0: + mov eax, ERROR_FILE_NOT_FOUND +.ret1: + mov [esp+28], eax + call fat_unlock + popad + xor ebx, ebx + ret +@@: + inc esi +.common0: + test byte [edi+11], 0x10 ; must be directory + mov eax, ERROR_ACCESS_DENIED + jz .ret1 + mov edx, [edi+20-2] + mov dx, [edi+26] ; ebp=cluster + mov eax, ERROR_FAT_TABLE + cmp edx, 2 + jb .ret1 +.pushnotroot: + push edx + push fat_notroot_extend_dir + push fat_notroot_end_write + push fat_notroot_next_write + push fat_notroot_begin_write + push 0 + push edx + push fat_notroot_first + push fat_notroot_next +.common1: + call fat_find_lfn + jc .notfound +; found + 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, 36 + call fat_unlock + popad + test al, al + mov eax, ERROR_ACCESS_DENIED + jz @f + mov al, 0 +@@: + 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+36+28], 0 + jz @f + add esp, 36 + call fat_unlock + 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 + xor ecx, ecx + mov eax, [edi+20-2] + mov ax, [edi+26] + mov word [edi+20], cx + mov word [edi+26], cx + test eax, eax + jz .done1 +@@: + cmp eax, [ebp+FAT.fatRESERVED] + jae .done1 + push edx + xor edx, edx + call set_FAT + mov eax, edx + pop edx + jc .done1 + inc ecx + 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 +.notfound: +; file is not found; generate short name + call fat_name_is_legal + jc @f + add esp, 36 + call fat_unlock + popad + mov eax, ERROR_FILE_NOT_FOUND + xor ebx, ebx + ret +@@: + 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 edx, [eax+24] + mov [eax], edx + and dword [eax+4], 0 + 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 +.test_short_name_cont: + 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 +.disk_full: + add esp, 12+36 + call fat_unlock + popa + mov eax, ERROR_DISK_FULL + xor ebx, ebx + ret +.found: + 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 + movi eax, 1 ; 1 entry + jnz .notilde +; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total + xor eax, eax +@@: + 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 +.notilde: + push -1 + push -1 + push -1 +; find successive entries in directory + xor ecx, ecx + push eax + lea eax, [esp+16+8+12+8] + mov edx, [eax+24] + mov [eax], edx + and dword [eax+4], 0 + call dword [eax-4] + pop eax + jnc .scan_dir +.fsfrfe3: + add esp, 12+8+12+36 + call fat_unlock + popad + mov eax, ERROR_DEVICE + xor ebx, ebx + ret +.scan_dir: + cmp byte [edi], 0 + jz .free + cmp byte [edi], 0xE5 + jz .free + xor ecx, ecx +.scan_cont: + push eax + lea eax, [esp+16+8+12+8] + call dword [eax-8] + mov edx, eax + pop eax + jnc .scan_dir + cmp edx, ERROR_DEVICE + jz .fsfrfe3 + push eax + lea eax, [esp+16+8+12+8] + call dword [eax+20] ; extend directory + pop eax + jnc .scan_dir + add esp, 12+8+12+36 + call fat_unlock + popad + mov eax, ERROR_DISK_FULL + xor ebx, ebx + ret +.free: + test ecx, ecx + jnz @f + mov [esp], edi + mov ecx, [esp+12+8+12+8] + mov [esp+4], ecx + mov ecx, [esp+12+8+12+12] + mov [esp+8], ecx + xor ecx, ecx +@@: + inc ecx + cmp ecx, eax + jb .scan_cont +; found! + push esi ecx +; 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 fat_Rewrite and 1 in fat_CreateFolder. + cmp byte [esp+8+12+8+12+36+28], 0 + jz .no.preallocate.folder.data + call get_free_FAT + jnc @f + add esp, 8+12+8 + jmp .disk_full +@@: + mov [esp+8+12+8+12+36+20], eax ; store the cluster somewhere +.no.preallocate.folder.data: +; calculate name checksum + mov esi, [esp+8+12] + 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+12] + pop dword [esp+8+12+12] +; edi points to first entry in free chunk + dec ecx + jz .nolfn + push esi + push eax + lea eax, [esp+8+8+12+8] + call dword [eax+8] ; begin write + 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 fat_read_symbols + mov ax, 0xF + stosw + mov al, [esp+4] + stosb + mov cl, 6 + call fat_read_symbols + xor eax, eax + stosw + mov cl, 2 + call fat_read_symbols + pop ecx + lea eax, [esp+8+8+12+8] + call dword [eax+12] ; next write + xor eax, eax + loop .writelfn + pop eax + pop esi +; lea eax, [esp+8+12+8] +; call dword [eax+16] ; end write +.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 + xor ecx, ecx + mov word [edi+20], cx ; high word of cluster + mov word [edi+26], cx ; low word of cluster - to be filled + mov dword [edi+28], ecx ; file size - to be filled + cmp byte [esp+36+28], cl + jz .doit +; create directory + mov byte [edi+11], 10h ; attributes: folder + mov esi, edi + lea eax, [esp+8] + call dword [eax+16] ; flush directory + mov eax, [esp+36+20] ; extract saved cluster + mov [esp+36+20], edi ; this is needed for calculating arg of add_disk_free_space! + push ecx + mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + shl ecx, 9 + push ecx + push edi + jmp .doit2 +.doit: + mov esi, [esp+36+20] + lea eax, [esp+8] + call dword [eax+16] ; flush directory + push ecx + mov ecx, [esp+4+36+24] + push ecx + push edi + test ecx, ecx + jz .done + call get_free_FAT + jc .diskfull +.doit2: + push eax + mov [edi+26], ax + shr eax, 16 + mov [edi+20], ax + lea eax, [esp+16+8] + call dword [eax+16] ; flush directory + pop eax + push edx + mov edx, [ebp+FAT.fatEND] + call set_FAT + pop edx +.write_cluster: + push eax + dec eax + dec eax + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + push [ebp+FAT.SECTORS_PER_CLUSTER] +; write data +.write_sector: + cmp byte [esp+20+36+28], 0 + jnz .writedir + mov ecx, 512 + cmp dword [esp+12], ecx + jb .writeshort +; we can write directly from given buffer + mov ebx, esi + add esi, ecx + jmp .writecommon +.writeshort: + mov ecx, [esp+12] + push ecx + lea edi, [ebp+FAT.buffer] + mov ebx, edi + rep movsb +.writedircont: + lea ecx, [ebp+FAT.buffer+0x200] + sub ecx, edi + push eax + xor eax, eax + rep stosb + pop eax + pop ecx +.writecommon: + push eax + call fs_write32_app + test eax, eax + pop eax + jnz .writeerr + inc eax + sub dword [esp+12], ecx + jz .writedone + dec dword [esp] + jnz .write_sector + pop eax +; allocate new cluster + pop eax + mov ecx, eax + call get_free_FAT + jc .diskfull + push edx + mov edx, [ebp+FAT.fatEND] + call set_FAT + xchg eax, ecx + mov edx, ecx + call set_FAT + pop edx + xchg eax, ecx + jmp .write_cluster +.diskfull: + mov eax, ERROR_DISK_FULL + jmp .ret +.writeerr: + pop eax eax + sub esi, ecx + mov eax, ERROR_DEVICE + jmp .ret +.writedone: + pop eax eax +.done: + xor eax, eax +.ret: + pop edi ecx + sub esi, [esp+4+36+20] + mov [esp+4+36+28], eax + mov [esp+4+36+16], esi + lea eax, [esp+12] + call dword [eax+8] + mov [edi+28], esi + call dword [eax+16] + mov [esp+36+16], ebx + lea eax, [esi+511] + shr eax, 9 + mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + lea eax, [eax+ecx-1] + xor edx, edx + div ecx + pop ecx + sub ecx, eax + call add_disk_free_space + add esp, 36 + call update_disk + call fat_unlock + popad + ret +.writedir: + push 512 + lea edi, [ebp+FAT.buffer] + mov ebx, edi + mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + shl ecx, 9 + cmp ecx, [esp+16] + jnz .writedircont + dec dword [esp+20] + push esi + mov ecx, 32/4 + rep movsd + pop esi + mov dword [edi-32], '. ' + mov dword [edi-32+4], ' ' + mov dword [edi-32+8], ' ' + mov byte [edi-32+11], 10h + push esi + mov ecx, 32/4 + rep movsd + pop esi + mov dword [edi-32], '.. ' + mov dword [edi-32+4], ' ' + mov dword [edi-32+8], ' ' + mov byte [edi-32+11], 10h + mov ecx, [esp+20+36] + cmp ecx, [ebp+FAT.ROOT_CLUSTER] + jnz @f + xor ecx, ecx +@@: + mov word [edi-32+26], cx + shr ecx, 16 + mov [edi-32+20], cx + jmp .writedircont + +fat_read_symbol: + or ax, -1 + test esi, esi + jz .retFFFF + lodsb + test al, al + jnz ansi2uni_char + xor eax, eax + xor esi, esi +.retFFFF: + ret + +fat_read_symbols: + call fat_read_symbol + stosw + loop fat_read_symbols + ret + + +fat_Write.access_denied: + push ERROR_ACCESS_DENIED +fat_Write.ret0: + pop eax + xor ebx, ebx + ret + +fat_Write.ret11: + push ERROR_DEVICE + jmp fat_Write.ret0 + +;---------------------------------------------------------------- +; fat_Write - FAT implementation of writing to file +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_Write: + cmp byte [esi], 0 + jz .access_denied + call fat_lock + push edi + stdcall hd_find_lfn, [esp+4+4] + jnc .found + pop edi + push eax + call fat_unlock + jmp .ret0 +.found: +; FAT does not support files larger than 4GB + cmp dword [ebx+8], 0 + jz @f +.eof: + pop edi + push ERROR_END_OF_FILE + call fat_unlock + jmp .ret0 +@@: + mov ecx, [ebx+12] + mov edx, [ebx+16] + mov ebx, [ebx+4] +; now edi points to direntry, ebx=start byte to write, +; ecx=number of bytes to write, edx=data pointer + +; extend file if needed + add ecx, ebx + jc .eof ; FAT does not support files larger than 4GB + push edx + push eax ; save directory sector + push 0 ; return value=0 + + 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 + + push dword [edi+28] ; save current file size + cmp ecx, [edi+28] + jbe .length_ok + cmp ecx, ebx + jz .length_ok + call hd_extend_file + jnc .length_ok + mov [esp+4], eax +; hd_extend_file can return three error codes: FAT table error, device error or disk full. +; First two cases are fatal errors, in third case we may write some data + cmp al, ERROR_DISK_FULL + jz .disk_full + call fat_unlock + pop eax + pop eax + pop ecx + pop edx + pop edi + xor ebx, ebx + ret +.disk_full: +; correct number of bytes to write + mov ecx, [edi+28] + cmp ecx, ebx + ja .length_ok + push 0 +.ret: + pop eax + sub edx, [esp+12] + mov ebx, edx ; ebx=number of written bytes + call update_disk + test eax, eax + jz @f + mov byte [esp+4], ERROR_DEVICE +@@: + call fat_unlock + pop eax + pop eax + pop ecx + pop edx + pop edi + ret +.length_ok: + mov esi, [edi+28] + mov eax, [edi+20-2] + mov ax, [edi+26] + mov edi, eax ; edi=current cluster + push 0 ; current sector in cluster +; save directory + mov eax, [esp+12] + push ebx + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + pop ebx + test eax, eax + jz @f +.device_err: + mov byte [esp+8], ERROR_DEVICE + jmp .ret +.fat_err: + mov byte [esp+8], ERROR_FAT_TABLE + jmp .ret +@@: + +; now ebx=start pos, ecx=end pos, both lie inside file + sub ecx, ebx + jz .ret +.write_loop: +; skip unmodified sectors + cmp dword [esp+4], 0x200 + jb .modify + sub ebx, 0x200 + jae .skip + add ebx, 0x200 +.modify: +; get length of data in current sector + push ecx + sub ebx, 0x200 + jb .hasdata + neg ebx + xor ecx, ecx + jmp @f +.hasdata: + neg ebx + cmp ecx, ebx + jbe @f + mov ecx, ebx +@@: +; get current sector number + mov eax, edi + dec eax + dec eax + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + add eax, [esp+4] +; load sector if needed + cmp dword [esp+8], 0 ; we don't need to read uninitialized data + jz .noread + cmp ecx, 0x200 ; we don't need to read sector if it is fully rewritten + jz .noread + cmp ecx, esi ; (same for the last sector) + jz .noread + push eax ebx + lea ebx, [ebp+FAT.buffer] + call fs_read32_app + test eax, eax + pop ebx eax + jz @f +.device_err2: + pop ecx + jmp .device_err +@@: +.noread: +; zero uninitialized data if file was extended (because hd_extend_file does not this) + push eax ecx edi + xor eax, eax + mov ecx, 0x200 + sub ecx, [esp+8+12] + jbe @f + lea edi, [ebp+FAT.buffer] + add edi, [esp+8+12] + rep stosb +@@: +; zero uninitialized data in the last sector + mov ecx, 0x200 + sub ecx, esi + jbe @f + lea edi, [ebp+FAT.buffer+esi] + rep stosb +@@: + pop edi ecx +; copy new data + mov eax, edx + neg ebx + jecxz @f + lea ebx, [ebp+FAT.buffer+0x200+ebx] + call memmove + xor ebx, ebx +@@: + pop eax +; save sector + push ebx + lea ebx, [ebp+FAT.buffer] + call fs_write32_app + pop ebx + test eax, eax + jnz .device_err2 + add edx, ecx + sub [esp], ecx + pop ecx + jz .ret +.skip: +; next sector + pop eax + inc eax + push eax + cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] + jb @f + and dword [esp], 0 + mov eax, edi + call get_FAT + mov edi, eax + jc .device_err + cmp edi, 2 + jb .fat_err + cmp edi, [ebp+FAT.fatRESERVED] + jae .fat_err +@@: + sub esi, 0x200 + jae @f + xor esi, esi +@@: + sub dword [esp+4], 0x200 + jae @f + and dword [esp+4], 0 +@@: + jmp .write_loop + +hd_extend_file.zero_size: + xor eax, eax + jmp hd_extend_file.start_extend + +; extends file on hd to given size (new data area is undefined) +; in: edi->direntry, ecx=new size +; out: CF=0 => OK, eax=0 +; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL or ERROR_DEVICE) +hd_extend_file: + push esi + mov esi, [ebp+FAT.SECTORS_PER_CLUSTER] + imul esi, [ebp+FAT.BYTES_PER_SECTOR] + push ecx +; find the last cluster of file + mov eax, [edi+20-2] + mov ax, [edi+26] + mov ecx, [edi+28] + jecxz .zero_size +.last_loop: + sub ecx, esi + jbe .last_found + call get_FAT + jnc @f +.device_err: + pop ecx +.device_err2: + pop esi + push ERROR_DEVICE +.ret_err: + pop eax + stc + ret +@@: + cmp eax, 2 + jb .fat_err + cmp eax, [ebp+FAT.fatRESERVED] + jb .last_loop +.fat_err: + pop ecx esi + push ERROR_FAT_TABLE + jmp .ret_err +.last_found: + push eax + call get_FAT + jnc @f + pop eax + jmp .device_err +@@: + cmp eax, [ebp+FAT.fatRESERVED] + pop eax + jb .fat_err +; set length to full number of clusters + sub [edi+28], ecx +.start_extend: + pop ecx +; now do extend + push edx + mov edx, 2 ; start scan from cluster 2 +.extend_loop: + cmp [edi+28], ecx + jae .extend_done +; add new cluster + push eax + call get_free_FAT + jc .disk_full + mov edx, [ebp+FAT.fatEND] + call set_FAT + mov edx, eax + pop eax + test eax, eax + jz .first_cluster + push edx + call set_FAT + pop edx + jmp @f +.first_cluster: + ror edx, 16 + mov [edi+20], dx + ror edx, 16 + mov [edi+26], dx +@@: + push ecx + mov ecx, -1 + call add_disk_free_space + pop ecx + mov eax, edx + add [edi+28], esi + jmp .extend_loop +.extend_done: + mov [edi+28], ecx + pop edx esi + xor eax, eax ; CF=0 + ret +.device_err3: + pop edx + jmp .device_err2 +.disk_full: + pop eax edx esi + movi eax, ERROR_DISK_FULL + stc + 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 + + +;---------------------------------------------------------------- +; fat_SetFileEnd - FAT implementation of setting end-of-file +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_SetFileEnd: + call fat_lock + push edi + cmp byte [esi], 0 + jnz @f +.access_denied: + push ERROR_ACCESS_DENIED +.ret: + call fat_unlock + pop eax + pop edi + ret +@@: + stdcall hd_find_lfn, [esp+4+4] + jnc @f +.reteax: + push eax + jmp .ret +@@: +; must not be directory + test byte [edi+11], 10h + jnz .access_denied +; file size must not exceed 4 Gb + cmp dword [ebx+8], 0 + jz @f + push ERROR_END_OF_FILE + jmp .ret +@@: + push eax ; save directory sector +; set file modification date/time to current + call fat_update_datetime + mov eax, [ebx+4] + cmp eax, [edi+28] + jb .truncate + ja .expand + pop eax + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + test eax, eax + jz @f + push ERROR_DEVICE + jmp .ret +@@: + push 0 + jmp .ret +.expand: + push ebx ebp ecx + push dword [edi+28] ; save old size + mov ecx, eax + call hd_extend_file + push eax ; return code + jnc .expand_ok + cmp al, ERROR_DISK_FULL + jz .disk_full +.pop_ret: + call update_disk + pop eax ecx ecx ebp ebx ecx + jmp .reteax +.expand_ok: +.disk_full: +; save directory + mov eax, [edi+28] + xchg eax, [esp+20] + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + test eax, eax + mov eax, [edi+20-2] + mov ax, [edi+26] + mov edi, eax + jz @f +.pop_ret11: + mov byte [esp], ERROR_DEVICE + jmp .pop_ret +@@: + test edi, edi + jz .pop_ret +; now zero new data + push 0 +; edi=current cluster, [esp]=sector in cluster +; [esp+24]=new size, [esp+8]=old size, [esp+4]=return code +.zero_loop: + cmp edi, 2 + jb .error_fat + cmp edi, [ebp+FAT.fatRESERVED] + jae .error_fat + sub dword [esp+8], 0x200 + jae .next_cluster + lea eax, [edi-2] + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + add eax, [esp] + cmp dword [esp+8], -0x200 + jz .noread + push eax + lea ebx, [ebp+FAT.buffer] + call fs_read32_app + test eax, eax + pop eax + jnz .err_next +.noread: + mov ecx, [esp+8] + neg ecx + push edi + lea edi, [ebp+FAT.buffer+0x200] + add edi, [esp+12] + push eax + xor eax, eax + mov [esp+16], eax + rep stosb + pop eax + pop edi + call fs_write32_app + test eax, eax + jz .next_cluster +.err_next: + mov byte [esp+4], ERROR_DEVICE +.next_cluster: + pop eax + sub dword [esp+20], 0x200 + jbe .pop_ret + inc eax + push eax + cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] + jb .zero_loop + and dword [esp], 0 + mov eax, edi + call get_FAT + mov edi, eax + jnc .zero_loop + pop eax + jmp .pop_ret11 +.truncate: + mov [edi+28], eax + push ecx + mov ecx, [edi+20-2] + mov cx, [edi+26] + push eax + test eax, eax + jz .zero_size +; find new last cluster +@@: + cmp ecx, 2 + jb .error_fat2 + cmp ecx, [ebp+FAT.fatRESERVED] + jae .error_fat2 + mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] + shl eax, 9 + sub [esp], eax + jbe @f + mov eax, ecx + call get_FAT + mov ecx, eax + jnc @b +.device_err3: + pop eax ecx eax edi + call update_disk + call fat_unlock + movi eax, ERROR_DEVICE + ret +@@: +; we will zero data at the end of last sector - remember it + push ecx +; terminate FAT chain + push edx + mov eax, ecx + mov edx, [ebp+FAT.fatEND] + call set_FAT + mov eax, edx + pop edx + jnc @f +.device_err4: + pop ecx + jmp .device_err3 +.zero_size: + and word [edi+20], 0 + and word [edi+26], 0 + push 0 + mov eax, ecx +@@: +; delete FAT chain + call clear_cluster_chain + jc .device_err4 +; save directory + mov eax, [esp+12] + push ebx + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + pop ebx + test eax, eax + jnz .device_err4 +; zero last sector, ignore errors + pop ecx + pop eax + dec ecx + imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + add ecx, [ebp+FAT.DATA_START] + push eax + sar eax, 9 + add ecx, eax + pop eax + and eax, 0x1FF + jz .truncate_done + push ebx eax + mov eax, ecx + lea ebx, [ebp+FAT.buffer] + call fs_read32_app + pop eax + lea edi, [ebp+FAT.buffer+eax] + push ecx + mov ecx, 0x200 + sub ecx, eax + xor eax, eax + rep stosb + pop eax + call fs_write32_app + pop ebx +.truncate_done: + pop ecx eax edi + call update_disk + call fat_unlock + xor eax, eax + ret +.error_fat: + pop eax + mov byte [esp], ERROR_FAT_TABLE + jmp .pop_ret +.error_fat2: + pop eax ecx eax edi + call update_disk + call fat_unlock + movi eax, ERROR_FAT_TABLE + ret + +;---------------------------------------------------------------- +; fat_GetFileInfo - FAT implementation of getting file info +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_GetFileInfo: + cmp byte [esi], 0 + jnz @f + mov eax, 2 + ret +@@: + push edi + call fat_lock + stdcall hd_find_lfn, [esp+4+4] + jc .error + push ebp + xor ebp, ebp + mov esi, [ebx+16] + mov dword [esi+4], ebp + call fat_entry_to_bdfe2 + pop ebp + call fat_unlock + xor eax, eax + pop edi + ret +.error: + push eax + call fat_unlock + pop eax + pop edi + ret + +;---------------------------------------------------------------- +; fat_SetFileInfo - FAT implementation of setting file info +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_SetFileInfo: + cmp byte [esi], 0 + jnz @f + mov eax, 2 + ret +@@: + push edi + call fat_lock + stdcall hd_find_lfn, [esp+4+4] + jc .error + push eax + mov edx, [ebx+16] + call bdfe_to_fat_entry + pop eax + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + call update_disk + call fat_unlock + pop edi + xor eax, eax + ret +.error: + push eax + call fat_unlock + pop eax + pop edi + ret + +;---------------------------------------------------------------- +; fat_Delete - FAT implementation of deleting a file/folder +; in: ebp = pointer to FAT structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +fat_Delete: + call fat_lock + cmp byte [esi], 0 + jnz @f +; cannot delete root! +.access_denied: + push ERROR_ACCESS_DENIED +.pop_ret: + call fat_unlock + pop eax + xor ebx, ebx + ret +@@: + and [ebp+FAT.longname_sec1], 0 + and [ebp+FAT.longname_sec2], 0 + push edi + stdcall hd_find_lfn, [esp+4+4] + 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 +; we can delete only empty folders! + pushad + mov esi, [edi+20-2] + mov si, [edi+26] + xor ecx, ecx + lea eax, [esi-2] + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + lea ebx, [ebp+FAT.buffer] + call fs_read32_sys + test eax, eax + jnz .err1 + lea eax, [ebx+0x200] + add ebx, 2*0x20 +.checkempty: + cmp byte [ebx], 0 + jz .empty + cmp byte [ebx], 0xE5 + jnz .notempty + add ebx, 0x20 + cmp ebx, eax + jb .checkempty + inc ecx + cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] + jb @f + mov eax, esi + call get_FAT + jc .err1 + cmp eax, 2 + jb .error_fat + cmp eax, [ebp+FAT.fatRESERVED] + jae .empty + mov esi, eax + xor ecx, ecx +@@: + lea eax, [esi-2] + imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] + add eax, [ebp+FAT.DATA_START] + add eax, ecx + lea ebx, [ebp+FAT.buffer] + call fs_read32_sys + test eax, eax + lea eax, [ebx+0x200] + jz .checkempty +.err1: + popad +.err2: + pop edi + call fat_unlock + movi eax, ERROR_DEVICE + ret +.error_fat: + popad + pop edi + call fat_unlock + movi eax, ERROR_FAT_TABLE + ret +.notempty: + popad +.access_denied2: + pop edi + call fat_unlock + movi eax, ERROR_ACCESS_DENIED + ret +.empty: + popad + push eax ebx + lea ebx, [ebp+FAT.buffer] + call fs_read32_sys + test eax, eax + pop ebx eax + jnz .err2 +.dodel: + push eax + mov eax, [edi+20-2] + mov ax, [edi+26] + xchg eax, [esp] +; delete folder entry + mov byte [edi], 0xE5 +; delete LFN (if present) +.lfndel: + lea edx, [ebp+FAT.buffer] + cmp edi, edx + ja @f + cmp [ebp+FAT.longname_sec2], 0 + jz .lfndone + push [ebp+FAT.longname_sec2] + push [ebp+FAT.longname_sec1] + pop [ebp+FAT.longname_sec2] + and [ebp+FAT.longname_sec1], 0 + push ebx + mov ebx, edx + call fs_write32_sys + mov eax, [esp+4] + call fs_read32_sys + pop ebx + pop eax + lea edi, [ebp+FAT.buffer+0x200] +@@: + sub edi, 0x20 + cmp byte [edi], 0xE5 + jz .lfndone + cmp byte [edi+11], 0xF + jnz .lfndone + mov byte [edi], 0xE5 + jmp .lfndel +.lfndone: + push ebx + lea ebx, [ebp+FAT.buffer] + call fs_write32_sys + pop ebx +; delete FAT chain + pop eax + call clear_cluster_chain + call update_disk + call fat_unlock + pop edi + xor eax, eax + ret + +; \end{diamond} diff --git a/kernel/branches/kolibri-process/fs/fs_lfn.inc b/kernel/branches/kolibri-process/fs/fs_lfn.inc new file mode 100644 index 000000000..a4edc3c85 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/fs_lfn.inc @@ -0,0 +1,797 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4277 $ + +ERROR_SUCCESS = 0 +ERROR_DISK_BASE = 1 +ERROR_UNSUPPORTED_FS = 2 +ERROR_UNKNOWN_FS = 3 +ERROR_PARTITION = 4 +ERROR_FILE_NOT_FOUND = 5 +ERROR_END_OF_FILE = 6 +ERROR_MEMORY_POINTER = 7 +ERROR_DISK_FULL = 8 +ERROR_FAT_TABLE = 9 ;deprecated +ERROR_FS_FAIL = 9 +ERROR_ACCESS_DENIED = 10 +ERROR_DEVICE = 11 + +image_of_eax EQU esp+32 +image_of_ebx EQU esp+20 + +; System function 70 - files with long names (LFN) +; diamond, 2006 + +iglobal +; in this table names must be in lowercase +rootdirs: +;********************************************** + db 3,'cd0' + dd fs_OnCd0 + dd fs_NextCd + db 3,'cd1' + dd fs_OnCd1 + dd fs_NextCd + db 3,'cd2' + dd fs_OnCd2 + dd fs_NextCd + db 3,'cd3' + dd fs_OnCd3 + dd fs_NextCd +;*********************************************** + db 0 + + +virtual_root_query: +;********************************************** + dd fs_HasCd0 + db 'cd0',0 + dd fs_HasCd1 + db 'cd1',0 + dd fs_HasCd2 + db 'cd2',0 + dd fs_HasCd3 + db 'cd3',0 +;********************************************** + dd 0 +endg + +file_system_lfn_protected: + pushad + call protect_from_terminate + call file_system_lfn + call unprotect_from_terminate + popad + mov [image_of_eax], eax + mov [image_of_ebx], ebx + ret + +file_system_lfn: +; in: ebx->fileinfo block +; operation codes: +; 0 : read file +; 1 : read folder +; 2 : create/rewrite file +; 3 : write/append to file +; 4 : set end of file +; 5 : get file/directory attributes structure +; 6 : set file/directory attributes structure +; 7 : start application +; 8 : delete file +; 9 : create directory + +; parse file name + lea esi, [ebx+20] + lodsb + test al, al + jnz @f + mov esi, [esi] + lodsb +@@: + cmp al, '/' + jz .notcurdir + dec esi + mov ebp, esi + test al, al + jnz @f + xor ebp, ebp +@@: + mov esi, [current_slot] + mov esi, [esi+APPDATA.cur_dir] + jmp .parse_normal +.notcurdir: + cmp byte [esi], 0 + jz .rootdir + call process_replace_file_name +.parse_normal: + cmp dword [ebx], 7 + jne @F + mov edx, [ebx+4] + mov ebx, [ebx+8] + call fs_execute; esi+ebp, ebx, edx + mov [image_of_eax], eax + ret +@@: + mov edi, rootdirs-8 + xor ecx, ecx + push esi +.scan1: + pop esi + add edi, ecx + scasd + scasd + mov cl, byte [edi] + test cl, cl + jz .notfound_try + inc edi + push esi +@@: + lodsb + or al, 20h + scasb + loopz @b + jnz .scan1 + lodsb + cmp al, '/' + jz .found1 + test al, al + jnz .scan1 + pop eax +; directory /xxx +.maindir: + mov esi, [edi+4] +.maindir_noesi: + cmp dword [ebx], 1 + jnz .access_denied + xor eax, eax + mov ebp, [ebx+12] ;количество блоков для считывания + mov edx, [ebx+16] ;куда записывать рузельтат + ; add edx, std_application_base_address + push dword [ebx+4] ; first block + mov ebx, [ebx+8] ; flags +; ebx=flags, [esp]=first block, ebp=number of blocks, edx=return area, esi='Next' handler + mov edi, edx + push ecx + mov ecx, 32/4 + rep stosd + pop ecx + mov byte [edx], 1 ; version +.maindir_loop: + call esi + jc .maindir_done + inc dword [edx+8] + dec dword [esp] + jns .maindir_loop + dec ebp + js .maindir_loop + inc dword [edx+4] + mov dword [edi], 0x10 ; attributes: folder + mov dword [edi+4], 1 ; name type: UNICODE + push eax + xor eax, eax + add edi, 8 + push ecx + mov ecx, 40/4-2 + rep stosd + pop ecx + pop eax + push eax edx +; convert number in eax to decimal UNICODE string + push edi + push ecx + push -'0' + mov ecx, 10 +@@: + xor edx, edx + div ecx + push edx + test eax, eax + jnz @b +@@: + pop eax + add al, '0' + stosb + test bl, 1 ; UNICODE name? + jz .ansi2 + mov byte [edi], 0 + inc edi +.ansi2: + test al, al + jnz @b + mov byte [edi-1], 0 + pop ecx + pop edi +; UNICODE name length is 520 bytes, ANSI - 264 + add edi, 520 + test bl, 1 + jnz @f + sub edi, 520-264 +@@: + pop edx eax + jmp .maindir_loop +.maindir_done: + pop eax + mov ebx, [edx+4] + xor eax, eax + dec ebp + js @f + mov al, ERROR_END_OF_FILE +@@: + mov [image_of_eax], eax + mov [image_of_ebx], ebx + ret +; directory / +.rootdir: + cmp dword [ebx], 1 ; read folder? + jz .readroot +.access_denied: + mov dword [image_of_eax], 10 ; access denied + ret + +.readroot: +; virtual root folder - special handler + mov ebp, [ebx+12] + mov edx, [ebx+16] + ; add edx, std_application_base_address + push dword [ebx+4] ; first block + mov ebx, [ebx+8] ; flags + xor eax, eax +; eax=0, [esp]=first block, ebx=flags, ebp=number of blocks, edx=return area + mov edi, edx + mov ecx, 32/4 + rep stosd + mov byte [edx], 1 ; version + sub esp, 16 +.readroot_ah_loop2: + push edi + lea edi, [esp+4] + call dyndisk_enum_root + pop edi + test eax, eax + jz .readroot_done_dynamic + inc dword [edx+8] + dec dword [esp+16] + jns .readroot_ah_loop2 + dec ebp + js .readroot_ah_loop2 + push eax + xor eax, eax + inc dword [edx+4] + mov dword [edi], 0x10 ; attributes: folder + mov dword [edi+4], ebx + add edi, 8 + mov ecx, 40/4-2 + rep stosd + push esi edi + lea esi, [esp+12] +@@: + lodsb + stosb + test bl, 1 + jz .ansi3 + mov byte [edi], 0 + inc edi +.ansi3: + test al, al + jnz @b + pop edi esi eax + add edi, 520 + test bl, 1 + jnz .readroot_ah_loop2 + sub edi, 520-264 + jmp .readroot_ah_loop2 +.readroot_done_dynamic: + add esp, 16 + mov esi, virtual_root_query +.readroot_loop: + cmp dword [esi], eax + jz .readroot_done + call dword [esi] + add esi, 4 + test eax, eax + jnz @f +.readroot_next: + or ecx, -1 + xchg esi, edi + repnz scasb + xchg esi, edi + jmp .readroot_loop +@@: + xor eax, eax + inc dword [edx+8] + dec dword [esp] + jns .readroot_next + dec ebp + js .readroot_next + inc dword [edx+4] + mov dword [edi], 0x10 ; attributes: folder + mov dword [edi+4], ebx ; name type: UNICODE + add edi, 8 + mov ecx, 40/4-2 + rep stosd + push edi +@@: + lodsb + stosb + test bl, 1 + jz .ansi + mov byte [edi], 0 + inc edi +.ansi: + test eax, eax + jnz @b + pop edi + add edi, 520 + test bl, 1 + jnz .readroot_loop + sub edi, 520-264 + jmp .readroot_loop +.readroot_done: + pop eax + mov ebx, [edx+4] + xor eax, eax + dec ebp + js @f + mov al, ERROR_END_OF_FILE +@@: + mov [image_of_eax], eax + mov [image_of_ebx], ebx + ret +.notfound_try: + call dyndisk_handler +.notfound: + mov dword [image_of_eax], ERROR_FILE_NOT_FOUND + and dword [image_of_ebx], 0 + ret + +.notfounda: + cmp edi, esp + jnz .notfound + call dword [edi+4] + add esp, 16 + jmp .notfound + +.found1: + pop eax + cmp byte [esi], 0 + jz .maindir +.found2: +; read partition number + xor ecx, ecx + xor eax, eax +@@: + lodsb + cmp al, '/' + jz .done1 + test al, al + jz .done1 + sub al, '0' + cmp al, 9 + ja .notfounda + lea ecx, [ecx*5] + lea ecx, [ecx*2+eax] + jmp @b +.done1: + jecxz .notfounda + test al, al + jnz @f + dec esi +@@: + cmp byte [esi], 0 + jnz @f + test ebp, ebp + jz @f + mov esi, ebp + xor ebp, ebp +@@: +; now [edi] contains handler address, ecx - partition number, +; esi points to ASCIIZ string - rest of name + jmp dword [edi] + +; handlers for devices +; in: ecx = 0 => query virtual directory /xxx +; in: ecx = partition number +; esi -> relative (for device) name +; ebx -> fileinfo +; ebp = 0 or pointer to rest of name from folder addressed by esi +; out: [image_of_eax]=image of eax, [image_of_ebx]=image of ebx + +fs_NotImplemented: + mov eax, 2 + ret + +;******************************************************* +fs_OnCd0: + call reserve_cd + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + push 6 + push 1 + jmp fs_OnCd +fs_OnCd1: + call reserve_cd + mov [ChannelNumber], 1 + mov [DiskNumber], 1 + push 4 + push 2 + jmp fs_OnCd +fs_OnCd2: + call reserve_cd + mov [ChannelNumber], 2 + mov [DiskNumber], 0 + push 2 + push 3 + jmp fs_OnCd +fs_OnCd3: + call reserve_cd + mov [ChannelNumber], 2 + mov [DiskNumber], 1 + push 0 + push 4 +fs_OnCd: + call reserve_cd_channel + pop eax + mov [cdpos], eax + pop eax + cmp ecx, 0x100 + jae .nf + push ecx ebx + mov cl, al + mov bl, [DRIVE_DATA+1] + shr bl, cl + test bl, 2 + pop ebx ecx + + jnz @f +.nf: + call free_cd_channel + and [cd_status], 0 + mov dword [image_of_eax], 5 ; not found + ret +@@: + mov ecx, [ebx+12] + mov edx, [ebx+16] + ; add edx, std_application_base_address + mov eax, [ebx] + cmp eax, fs_NumCdServices + jae .not_impl + add ebx, 4 + call dword [fs_CdServices + eax*4] + call free_cd_channel + and [cd_status], 0 + mov [image_of_eax], eax + mov [image_of_ebx], ebx + ret +.not_impl: + call free_cd_channel + and [cd_status], 0 + mov dword [image_of_eax], 2 ; not implemented + ret + +fs_CdServices: + dd fs_CdRead + dd fs_CdReadFolder + dd fs_NotImplemented + dd fs_NotImplemented + dd fs_NotImplemented + dd fs_CdGetFileInfo + dd fs_NotImplemented + dd 0 + dd fs_NotImplemented + dd fs_NotImplemented +fs_NumCdServices = ($ - fs_CdServices)/4 + +;******************************************************* +fs_HasCd0: + test byte [DRIVE_DATA+1], 10000000b + setnz al + ret +fs_HasCd1: + test byte [DRIVE_DATA+1], 00100000b + setnz al + ret +fs_HasCd2: + test byte [DRIVE_DATA+1], 00001000b + setnz al + ret +fs_HasCd3: + test byte [DRIVE_DATA+1], 00000010b + setnz al + ret +;******************************************************* + +; fs_NextXXX functions: +; in: eax = partition number, from which start to scan +; out: CF=1 => no more partitions +; CF=0 => eax=next partition number + +;******************************************************* +fs_NextCd: +; we always have /cdX/1 + test eax, eax + stc + jnz @f + mov al, 1 + clc +@@: + ret +;******************************************************* + +;----------------------------------------------------------------------------- +process_replace_file_name: +; in +; esi - path with filename(f.70) +; +; out +; ebp - full filename + pushfd + cli + mov ebp, [full_file_name_table] + xor edi, edi +.loop: + cmp edi, [full_file_name_table.size] + jae .notfound + push esi edi + shl edi, 7 ; edi*128 + add edi, ebp +@@: + cmp byte [edi], 0 ; end of dir_name + jz .dest_done + lodsb + test al, al + jz .cont + or al, 20h ; 32 - space char + scasb + jz @b + jmp .cont +.dest_done: + cmp byte [esi], 0 + jz .found + cmp byte [esi], '/' + jnz .cont + inc esi + jmp .found +.cont: + pop edi esi + inc edi + jmp .loop +.found: + pop edi eax + shl edi, 7 ; edi*128 + add edi, ebp + mov ebp, esi + cmp byte [esi], 0 + lea esi, [edi+64] + jnz .ret +.notfound: + xor ebp, ebp +.ret: + popfd + ret +;----------------------------------------------------------------------------- +uglobal +lock_flag_for_f30_3 rb 1 +endg + +sys_current_directory: +; mov esi, [current_slot] +; mov esi, [esi+APPDATA.cur_dir] +; mov edx, esi + +;get length string of appdata.cur_dir + mov eax, [current_slot] + mov edi, [eax+APPDATA.cur_dir] + + dec ebx + jz .set + dec ebx + jz .get + dec ebx + jz .mount_additional_directory + ret + +.mount_additional_directory: +; sysfunction 30.2: [for app] eax=30,ebx=3,ecx->dir name+dir path (128) +; for our code: nothing + +; check lock of the function + cmp [lock_flag_for_f30_3], 1 + je @f + + mov esi, ecx + mov edi, sysdir_name1 +; copying fake directory name + mov ecx, 63 + pushfd + cli + cld + rep movsb +; terminator of name, in case if we get the inlet trash + inc esi + xor eax, eax + stosb +; copying real directory path for mounting + mov ecx, 63 + rep movsb +; terminator of name, in case if we get the inlet trash + xor eax, eax + stosb +; increase the pointer of inputs for procedure "process_replace_file_name" + mov [full_file_name_table.size], 2 +; block the ability to call f.30.3 because for one session is necessary +; for us only once + mov [lock_flag_for_f30_3], 1 + popfd +@@: + ret + +.get: +; sysfunction 30.2: [for app] eax=30,ebx=2,ecx->buffer,edx=len +; for our code: ebx->buffer,ecx=len +max_cur_dir equ 0x1000 + + mov ebx, edi + + push ecx + push edi + + xor eax, eax + mov ecx, max_cur_dir + + repne scasb ;find zerro at and string + jnz .error ; no zero in cur_dir: internal error, should not happen + + sub edi, ebx ;lenght for copy + inc edi + mov [esp+32+8], edi ;return in eax + + cmp edx, edi + jbe @f + mov edx, edi +@@: +;source string + pop esi +;destination string + pop edi + cmp edx, 1 + jbe .ret + + mov al, '/' ;start string with '/' + stosb + mov ecx, edx + rep movsb ;copy string +.ret: + ret + +.error: + add esp, 8 + or dword [esp+32], -1 ;error not found zerro at string ->[eax+APPDATA.cur_dir] + ret +.set: +; sysfunction 30.1: [for app] eax=30,ebx=1,ecx->string +; for our code: ebx->string to set +; use generic resolver with APPDATA.cur_dir as destination + push max_cur_dir ;0x1000 + push edi ;destination + mov ebx, ecx + call get_full_file_name + ret + +; in: ebx = file name, [esp+4] = destination, [esp+8] = sizeof destination +; destroys all registers except ebp,esp +get_full_file_name: + push ebp + mov esi, [current_slot] + mov esi, [esi+APPDATA.cur_dir] + mov edx, esi +@@: + inc esi + cmp byte [esi-1], 0 + jnz @b + dec esi + cmp byte [ebx], '/' + jz .set_absolute +; string gives relative path + mov edi, [esp+8] ; destination +.relative: + cmp byte [ebx], 0 + jz .set_ok + cmp word [ebx], '.' + jz .set_ok + cmp word [ebx], './' + jnz @f + add ebx, 2 + jmp .relative +@@: + cmp word [ebx], '..' + jnz .doset_relative + cmp byte [ebx+2], 0 + jz @f + cmp byte [ebx+2], '/' + jnz .doset_relative +@@: + dec esi + cmp byte [esi], '/' + jnz @b + add ebx, 3 + jmp .relative +.set_ok: + cmp edx, edi ; is destination equal to APPDATA.cur_dir? + jz .set_ok.cur_dir + sub esi, edx + cmp esi, [esp+12] + jb .set_ok.copy +.fail: + mov byte [edi], 0 + xor eax, eax ; fail + pop ebp + ret 8 +.set_ok.copy: + mov ecx, esi + mov esi, edx + rep movsb + mov byte [edi], 0 +.ret.ok: + mov al, 1 ; ok + pop ebp + ret 8 +.set_ok.cur_dir: + mov byte [esi], 0 + jmp .ret.ok +.doset_relative: + cmp edx, edi + jz .doset_relative.cur_dir + sub esi, edx + cmp esi, [esp+12] + jae .fail + mov ecx, esi + mov esi, edx + mov edx, edi + rep movsb + jmp .doset_relative.copy +.doset_relative.cur_dir: + mov edi, esi +.doset_relative.copy: + add edx, [esp+12] + mov byte [edi], '/' + inc edi + cmp edi, edx + jae .overflow +@@: + mov al, [ebx] + inc ebx + stosb + test al, al + jz .ret.ok + cmp edi, edx + jb @b +.overflow: + dec edi + jmp .fail +.set_absolute: + lea esi, [ebx+1] + call process_replace_file_name + mov edi, [esp+8] + mov edx, [esp+12] + add edx, edi +.set_copy: + lodsb + stosb + test al, al + jz .set_part2 +.set_copy_cont: + cmp edi, edx + jb .set_copy + jmp .overflow +.set_part2: + mov esi, ebp + xor ebp, ebp + test esi, esi + jz .ret.ok + mov byte [edi-1], '/' + jmp .set_copy_cont diff --git a/kernel/branches/kolibri-process/fs/iso9660.inc b/kernel/branches/kolibri-process/fs/iso9660.inc new file mode 100644 index 000000000..71bfd6dc6 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/iso9660.inc @@ -0,0 +1,759 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3742 $ + + +uglobal +cd_current_pointer_of_input dd 0 +cd_current_pointer_of_input_2 dd 0 +cd_mem_location dd 0 +cd_counter_block dd 0 +IDE_Channel_1 db 0 +IDE_Channel_2 db 0 +endg + +reserve_cd: + + cli + cmp [cd_status], 0 + je reserve_ok2 + + sti + call change_task + jmp reserve_cd + + reserve_ok2: + + push eax + mov eax, [CURRENT_TASK] + shl eax, 5 + mov eax, [eax+CURRENT_TASK+TASKDATA.pid] + mov [cd_status], eax + pop eax + sti + ret + +reserve_cd_channel: + cmp [ChannelNumber], 1 + jne .IDE_Channel_2 +.IDE_Channel_1: + pushad + mov ecx, ide_channel1_mutex + call mutex_lock + mov [IDE_Channel_1], 1 + popad + ret +.IDE_Channel_2: + pushad + mov ecx, ide_channel2_mutex + call mutex_lock + mov [IDE_Channel_2], 1 + popad + ret + +free_cd_channel: + cmp [ChannelNumber], 1 + jne .IDE_Channel_2 +.IDE_Channel_1: + mov [IDE_Channel_1], 0 + pushad + mov ecx, ide_channel1_mutex + call mutex_unlock + popad + ret +.IDE_Channel_2: + mov [IDE_Channel_2], 0 + pushad + mov ecx, ide_channel2_mutex + call mutex_unlock + popad + ret + +uglobal +cd_status dd 0 +endg + +;---------------------------------------------------------------- +; +; fs_CdRead - LFN variant for reading CD disk +; +; esi points to filename /dir1/dir2/.../dirn/file,0 +; ebx pointer to 64-bit number = first wanted byte, 0+ +; may be ebx=0 - start from first byte +; ecx number of bytes to read, 0+ +; edx mem location to return data +; +; ret ebx = bytes read or 0xffffffff file not found +; eax = 0 ok read or other = errormsg +; +;-------------------------------------------------------------- +fs_CdRead: + push edi + cmp byte [esi], 0 + jnz @f +.noaccess: + pop edi +.noaccess_2: + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret + +.noaccess_3: + pop eax edx ecx edi + jmp .noaccess_2 + +@@: + call cd_find_lfn + jnc .found + pop edi + cmp [DevErrorCode], 0 + jne .noaccess_2 + or ebx, -1 + mov eax, ERROR_FILE_NOT_FOUND + ret + +.found: + mov edi, [cd_current_pointer_of_input] + test byte [edi+25], 10b; do not allow read directories + jnz .noaccess + test ebx, ebx + jz .l1 + cmp dword [ebx+4], 0 + jz @f + xor ebx, ebx +.reteof: + mov eax, 6; end of file + pop edi + ret +@@: + mov ebx, [ebx] +.l1: + push ecx edx + push 0 + mov eax, [edi+10] ; реальный размер файловой секции + sub eax, ebx + jb .eof + cmp eax, ecx + jae @f + mov ecx, eax + mov byte [esp], 6 +@@: + mov eax, [edi+2] + mov [CDSectorAddress], eax +; now eax=cluster, ebx=position, ecx=count, edx=buffer for data +.new_sector: + test ecx, ecx + jz .done + sub ebx, 2048 + jae .next + add ebx, 2048 + jnz .incomplete_sector + cmp ecx, 2048 + jb .incomplete_sector +; we may read and memmove complete sector + mov [CDDataBuf_pointer], edx + call ReadCDWRetr; читаем сектор файла + cmp [DevErrorCode], 0 + jne .noaccess_3 + add edx, 2048 + sub ecx, 2048 +.next: + inc dword [CDSectorAddress] + jmp .new_sector +.incomplete_sector: +; we must read and memmove incomplete sector + mov [CDDataBuf_pointer], CDDataBuf + call ReadCDWRetr; читаем сектор файла + cmp [DevErrorCode], 0 + jne .noaccess_3 + push ecx + add ecx, ebx + cmp ecx, 2048 + jbe @f + mov ecx, 2048 +@@: + sub ecx, ebx + push edi esi ecx + mov edi, edx + lea esi, [CDDataBuf + ebx] + cld + rep movsb + pop ecx esi edi + add edx, ecx + sub [esp], ecx + pop ecx + xor ebx, ebx + jmp .next + +.done: + mov ebx, edx + pop eax edx ecx edi + sub ebx, edx + ret +.eof: + mov ebx, edx + pop eax edx ecx + sub ebx, edx + jmp .reteof + +;---------------------------------------------------------------- +; +; fs_CdReadFolder - LFN variant for reading CD disk folder +; +; esi points to filename /dir1/dir2/.../dirn/file,0 +; ebx pointer to structure 32-bit number = first wanted block, 0+ +; & flags (bitfields) +; flags: bit 0: 0=ANSI names, 1=UNICODE names +; ecx number of blocks to read, 0+ +; edx mem location to return data +; +; ret ebx = blocks read or 0xffffffff folder not found +; eax = 0 ok read or other = errormsg +; +;-------------------------------------------------------------- +fs_CdReadFolder: + push edi + call cd_find_lfn + jnc .found + pop edi + cmp [DevErrorCode], 0 + jne .noaccess_1 + or ebx, -1 + mov eax, ERROR_FILE_NOT_FOUND + ret +.found: + mov edi, [cd_current_pointer_of_input] + test byte [edi+25], 10b ; do not allow read directories + jnz .found_dir + pop edi +.noaccess_1: + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret +.found_dir: + mov eax, [edi+2] ; eax=cluster + mov [CDSectorAddress], eax + mov eax, [edi+10] ; размер директрории +.doit: +; init header + push eax ecx + mov edi, edx + mov ecx, 32/4 + xor eax, eax + rep stosd + pop ecx eax + 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] + push ecx +.read_to_buffer: + inc dword [CDSectorAddress] + mov [CDDataBuf_pointer], CDDataBuf + 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 + mov edi, [ebx] + sub [edx+4], edi + xor eax, eax + dec ecx + js @f + mov al, ERROR_END_OF_FILE +@@: + pop ecx edi + mov ebx, [edx+4] + ret + +.get_names_from_buffer: + mov [cd_current_pointer_of_input_2], CDDataBuf + push eax esi edi edx +.get_names_from_buffer_1: + call cd_get_name + jc .end_buffer + inc dword [cd_counter_block] + mov eax, [cd_counter_block] + cmp [ebx], eax + jae .get_names_from_buffer_1 + test ecx, ecx + jz .get_names_from_buffer_1 + mov edi, [cd_counter_block] + mov [edx+4], edi + dec ecx + mov esi, ebp + mov edi, [cd_mem_location] + add edi, 40 + test dword [ebx+4], 1; 0=ANSI, 1=UNICODE + jnz .unicode +; jmp .unicode +.ansi: + cmp [cd_counter_block], 2 + jbe .ansi_parent_directory + cld + lodsw + xchg ah, al + call uni2ansi_char + cld + stosb +; проверка конца файла + mov ax, [esi] + 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 + jb .ansi +.cd_get_parameters_of_file_1: + mov [edi], byte 0 + call cd_get_parameters_of_file + add [cd_mem_location], 304 + jmp .get_names_from_buffer_1 + +.ansi_parent_directory: + cmp [cd_counter_block], 2 + je @f + mov [edi], byte '.' + inc edi + jmp .cd_get_parameters_of_file_1 +@@: + mov [edi], word '..' + add edi, 2 + jmp .cd_get_parameters_of_file_1 + +.unicode: + cmp [cd_counter_block], 2 + jbe .unicode_parent_directory + cld + movsw +; проверка конца файла + mov ax, [esi] + 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 + jb .unicode +.cd_get_parameters_of_file_2: + mov [edi], word 0 + call cd_get_parameters_of_file + add [cd_mem_location], 560 + jmp .get_names_from_buffer_1 + +.unicode_parent_directory: + cmp [cd_counter_block], 2 + je @f + mov [edi], word 2E00h; '.' + add edi, 2 + jmp .cd_get_parameters_of_file_2 +@@: + mov [edi], dword 2E002E00h; '..' + add edi, 4 + jmp .cd_get_parameters_of_file_2 + +.end_buffer: + pop edx edi esi eax + ret + +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, в этом виде отсутсвует +; файл не является системным + shl eax, 3 +; файл является скрытым? (атрибут существование) + test [ebp-8], byte 1 + jz .hidden + inc eax +.hidden: + shl eax, 1 +; файл всегда только для чтения, так как это 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 + mov [edi+4], eax + jmp @f +.unicode_1: + inc eax + mov [edi+4], eax +@@: +; получаем размер файла в байтах + xor eax, eax + mov [edi+32+4], eax + mov eax, [ebp-23] + mov [edi+32], eax + ret + +;---------------------------------------------------------------- +; +; fs_CdGetFileInfo - LFN variant for CD +; get file/directory attributes structure +; +;---------------------------------------------------------------- +fs_CdGetFileInfo: + cmp byte [esi], 0 + jnz @f + mov eax, 2 + ret +@@: + push edi + call cd_find_lfn + pushfd + cmp [DevErrorCode], 0 + jz @f + popfd + pop edi + mov eax, 11 + ret +@@: + popfd + jnc @f + pop edi + mov eax, ERROR_FILE_NOT_FOUND + ret +@@: + + mov edi, edx + push ebp + mov ebp, [cd_current_pointer_of_input] + add ebp, 33 + call cd_get_parameters_of_file_1 + pop ebp + and dword [edi+4], 0 + pop edi + xor eax, eax + ret + +;---------------------------------------------------------------- +cd_find_lfn: + mov [cd_appl_data], 0 +; in: esi+ebp -> name +; out: CF=1 - file not found +; else CF=0 and [cd_current_pointer_of_input] direntry + push eax esi +; 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 + call Read_TOC + mov ah, [CDDataBuf+4+4] + mov al, [CDDataBuf+4+5] + shl eax, 16 + mov ah, [CDDataBuf+4+6] + mov al, [CDDataBuf+4+7] + add eax, 15 + mov [CDSectorAddress], eax +; mov [CDSectorAddress],dword 15 + mov [CDDataBuf_pointer], CDDataBuf + +.start: + inc dword [CDSectorAddress] + call ReadCDWRetr;_1 + cmp [DevErrorCode], 0 + 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 директрории + mov [CDSectorAddress], eax + 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 ; читаем сектор директории + cmp [DevErrorCode], 0 + jne .access_denied + push ebp + call cd_find_name_in_buffer + 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]; размер директории + jmp .mainloop +; указатель файла найден + .done: + test ebp, ebp + jz @f + mov esi, ebp + xor ebp, ebp + jmp .nested +@@: + pop esi eax + mov [cd_appl_data], 1 + clc + ret + +cd_find_name_in_buffer: + mov [cd_current_pointer_of_input_2], CDDataBuf +.start: + call cd_get_name + jc .not_found + call cd_compare_name + jc .start +.found: + clc + ret +.not_found: + stc + ret + +cd_get_name: + push eax + mov ebp, [cd_current_pointer_of_input_2] + mov [cd_current_pointer_of_input], ebp + mov eax, [ebp] + test eax, eax ; входы закончились? + jz .next_sector + cmp ebp, CDDataBuf+2048 ; буфер закончился? + jae .next_sector + movzx eax, byte [ebp] + add [cd_current_pointer_of_input_2], eax; следующий вход каталога + add ebp, 33; указатель установлен на начало имени + pop eax + clc + ret +.next_sector: + pop eax + stc + ret + +cd_compare_name: +; compares ASCIIZ-names, case-insensitive (cp866 encoding) +; in: esi->name, ebp->name +; out: if names match: ZF=1 and esi->next component of name +; else: ZF=0, esi is not changed +; destroys eax + push esi eax edi + mov edi, ebp +.loop: + cld + lodsb + push eax + call char_todown + call ansi2uni_char + xchg ah, al + scasw + pop eax + je .coincides + call char_toupper + call ansi2uni_char + xchg ah, al + sub edi, 2 + scasw + jne .name_not_coincide +.coincides: + cmp [esi], byte '/'; разделитель пути, конец имени текущего элемента + je .done + cmp [esi], byte 0; разделитель пути, конец имени текущего элемента + je .done + jmp .loop +.name_not_coincide: + pop edi eax esi + stc + ret +.done: +; проверка конца файла + 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 + jne .name_not_coincide +.done_1: + pop edi eax + add esp, 4 + inc esi + clc + ret + +char_todown: +; 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, 0x80 ; 'А' + jb .ret + cmp al, 0x90 ; 'Р' + jb .rus1 + cmp al, 0x9F ; 'Я' + ja .ret +; 0x90-0x9F -> 0xE0-0xEF + add al, 0xE0-0x90 +.ret: + ret +.rus1: +; 0x80-0x8F -> 0xA0-0xAF +.az: + add al, 0x20 + ret + +uni2ansi_char: +; convert UNICODE character in al to ANSI character in ax, using cp866 encoding +; in: ax=UNICODE character +; out: al=converted ANSI character + 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 +.yo1: + mov al, 0xF0 ; 'Ё' in cp866 + jmp .doit +.yo2: + mov al, 0xF1 ; 'ё' in cp866 + jmp .doit +.rus1: +; 0x410-0x43F -> 0x80-0xAF + add al, 0x70 + jmp .doit +.rus2: +; 0x440-0x44F -> 0xE0-0xEF + add al, 0xA0 +.ascii: +.doit: + ret diff --git a/kernel/branches/kolibri-process/fs/ntfs.inc b/kernel/branches/kolibri-process/fs/ntfs.inc new file mode 100644 index 000000000..04ffc0bbf --- /dev/null +++ b/kernel/branches/kolibri-process/fs/ntfs.inc @@ -0,0 +1,1926 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3742 $ + +struct NTFS PARTITION +Lock MUTEX ? ; currently operations with one partition + ; can not be executed in parallel since the + ; legacy code is not ready; this mutex guards + ; all operations +sectors_per_cluster dd ? +mft_cluster dd ? +mftmirr_cluster dd ? +frs_size dd ? ; FRS size in bytes +iab_size dd ? ; IndexAllocationBuffer size in bytes +frs_buffer dd ? +iab_buffer dd ? +mft_retrieval dd ? +mft_retrieval_size dd ? +mft_retrieval_alloc dd ? +mft_retrieval_end dd ? +cur_index_size dd ? +cur_index_buf dd ? + +ntfs_cur_attr dd ? +ntfs_cur_iRecord dd ? +ntfs_cur_offs dd ? ; in sectors +ntfs_cur_size dd ? ; in sectors +ntfs_cur_buf dd ? +ntfs_cur_read dd ? ; [output] +ntfs_bCanContinue db ? + rb 3 + +cur_subnode_size dd ? +ntfs_attr_iRecord dd ? +ntfs_attr_iBaseRecord dd ? +ntfs_attr_offs dd ? +ntfs_attr_list dd ? +ntfs_attr_size dq ? +ntfs_cur_tail dd ? + +ntfs_attrlist_buf rb 0x400 +ntfs_attrlist_mft_buf rb 0x400 +ntfs_bitmap_buf rb 0x400 +ends + +iglobal +align 4 +ntfs_user_functions: + dd ntfs_free + dd (ntfs_user_functions_end - ntfs_user_functions - 4) / 4 + dd ntfs_Read + dd ntfs_ReadFolder + dd ntfs_Rewrite + dd ntfs_Write + dd ntfs_SetFileEnd + dd ntfs_GetFileInfo + dd ntfs_SetFileInfo + dd 0 + dd ntfs_Delete + dd ntfs_CreateFolder +ntfs_user_functions_end: +endg + +ntfs_test_bootsec: +; in: ebx->buffer, edx=size of partition +; out: CF set <=> invalid +; 1. Name=='NTFS ' + cmp dword [ebx+3], 'NTFS' + jnz .no + cmp dword [ebx+7], ' ' + jnz .no +; 2. Number of bytes per sector is the same as for physical device +; (that is, 0x200 for hard disk) + cmp word [ebx+11], 0x200 + jnz .no +; 3. Number of sectors per cluster must be power of 2 + movzx eax, byte [ebx+13] + dec eax + js .no + test al, [ebx+13] + jnz .no +; 4. FAT parameters must be zero + cmp word [ebx+14], 0 + jnz .no + cmp dword [ebx+16], 0 + jnz .no + cmp byte [ebx+20], 0 + jnz .no + cmp word [ebx+22], 0 + jnz .no + cmp dword [ebx+32], 0 + jnz .no +; 5. Number of sectors <= partition size + cmp dword [ebx+0x2C], 0 + ja .no + cmp [ebx+0x28], edx + ja .no +; 6. $MFT and $MFTMirr clusters must be within partition + cmp dword [ebx+0x34], 0 + ja .no + push edx + movzx eax, byte [ebx+13] + mul dword [ebx+0x30] + test edx, edx + pop edx + jnz .no + cmp eax, edx + ja .no + cmp dword [ebx+0x3C], 0 + ja .no + push edx + movzx eax, byte [ebx+13] + mul dword [ebx+0x38] + test edx, edx + pop edx + jnz .no + cmp eax, edx + ja .no +; 7. Clusters per FRS must be either negative and in [-31,-9] or positive and power of 2 + movsx eax, byte [ebx+0x40] + cmp al, -31 + jl .no + cmp al, -9 + jle @f + dec eax + js .no + test [ebx+0x40], al + jnz .no +@@: +; 8. Same for clusters per IndexAllocationBuffer + movsx eax, byte [ebx+0x44] + cmp al, -31 + jl .no + cmp al, -9 + jle @f + dec eax + js .no + test [ebx+0x44], al + jnz .no +@@: +; OK, this is correct NTFS bootsector + clc + ret +.no: +; No, this bootsector isn't NTFS + stc + ret + +proc ntfs_create_partition + mov edx, dword [ebp+PARTITION.Length] + cmp dword [esp+4], 0 + jz .boot_read_ok + add ebx, 512 + lea eax, [edx-1] + call fs_read32_sys + test eax, eax + jnz @f + call ntfs_test_bootsec + jnc .ntfs_setup +@@: + mov eax, edx + shr eax, 1 + call fs_read32_sys + test eax, eax + jnz .nope ; no chance... +.boot_read_ok: + call ntfs_test_bootsec + jnc .ntfs_setup +.nope: + xor eax, eax + jmp .exit + +.ntfs_setup: +; By given bootsector, initialize some NTFS variables + movi eax, sizeof.NTFS + call malloc + test eax, eax + jz .exit + mov ecx, dword [ebp+PARTITION.FirstSector] + mov dword [eax+NTFS.FirstSector], ecx + mov ecx, dword [ebp+PARTITION.FirstSector+4] + mov dword [eax+NTFS.FirstSector+4], ecx + mov ecx, dword [ebp+PARTITION.Length] + mov dword [eax+NTFS.Length], ecx + mov ecx, dword [ebp+PARTITION.Length+4] + mov dword [eax+NTFS.Length+4], ecx + mov ecx, [ebp+PARTITION.Disk] + mov [eax+NTFS.Disk], ecx + mov [eax+NTFS.FSUserFunctions], ntfs_user_functions + push ebx ebp esi + mov ebp, eax + + lea ecx, [ebp+NTFS.Lock] + call mutex_init + + movzx eax, byte [ebx+13] + mov [ebp+NTFS.sectors_per_cluster], eax + mov eax, [ebx+0x28] + mov dword [ebp+NTFS.Length], eax + and dword [ebp+NTFS.Length+4], 0 + mov eax, [ebx+0x30] + mov [ebp+NTFS.mft_cluster], eax + mov eax, [ebx+0x38] + mov [ebp+NTFS.mftmirr_cluster], eax + movsx eax, byte [ebx+0x40] + test eax, eax + js .1 + mul [ebp+NTFS.sectors_per_cluster] + shl eax, 9 + jmp .2 +.1: + neg eax + mov ecx, eax + mov eax, 1 + shl eax, cl +.2: + mov [ebp+NTFS.frs_size], eax + movsx eax, byte [ebx+0x44] + test eax, eax + js .3 + mul [ebp+NTFS.sectors_per_cluster] + shl eax, 9 + jmp .4 +.3: + neg eax + mov ecx, eax + mov eax, 1 + shl eax, cl +.4: + mov [ebp+NTFS.iab_size], eax +; allocate space for buffers + add eax, [ebp+NTFS.frs_size] + push eax + call kernel_alloc + test eax, eax + jz .fail_free + mov [ebp+NTFS.frs_buffer], eax + add eax, [ebp+NTFS.frs_size] + mov [ebp+NTFS.iab_buffer], eax +; read $MFT disposition + mov eax, [ebp+NTFS.mft_cluster] + mul [ebp+NTFS.sectors_per_cluster] + call ntfs_read_frs_sector + test eax, eax + jnz .usemirr + cmp dword [ebx], 'FILE' + jnz .usemirr + call ntfs_restore_usa_frs + jnc .mftok +.usemirr: + mov eax, [ebp+NTFS.mftmirr_cluster] + mul [ebp+NTFS.sectors_per_cluster] + call ntfs_read_frs_sector + test eax, eax + jnz @f + cmp dword [ebx], 'FILE' + jnz @f + call ntfs_restore_usa_frs + jnc .mftok +@@: +; $MFT and $MFTMirr invalid! +.fail_free_frs: + push [ebp+NTFS.frs_buffer] + call kernel_free +.fail_free: + mov eax, ebp + call free + xor eax, eax +.pop_exit: + pop esi ebp ebx +.exit: + cmp dword [esp+4], 0 + jz @f + sub ebx, 512 +@@: + ret +.fail_free_mft: + push [ebp+NTFS.mft_retrieval] + call kernel_free + jmp .fail_free_frs +.mftok: +; read $MFT table retrieval information +; start with one page, increase if not enough (when MFT too fragmented) + push ebx + push 0x1000 + call kernel_alloc + pop ebx + test eax, eax + jz .fail_free_frs + mov [ebp+NTFS.mft_retrieval], eax + and [ebp+NTFS.mft_retrieval_size], 0 + mov [ebp+NTFS.mft_retrieval_alloc], 0x1000/8 +; $MFT base record must contain unnamed non-resident $DATA attribute + movzx eax, word [ebx+14h] + add eax, ebx +.scandata: + cmp dword [eax], -1 + jz .fail_free_mft + cmp dword [eax], 0x80 + jnz @f + cmp byte [eax+9], 0 + jz .founddata +@@: + add eax, [eax+4] + jmp .scandata +.founddata: + cmp byte [eax+8], 0 + jz .fail_free_mft +; load first portion of $DATA attribute retrieval information + mov edx, [eax+0x18] + mov [ebp+NTFS.mft_retrieval_end], edx + mov esi, eax + movzx eax, word [eax+0x20] + add esi, eax + sub esp, 10h +.scanmcb: + call ntfs_decode_mcb_entry + jnc .scanmcbend + call .get_mft_retrieval_ptr + mov edx, [esp] ; block length + mov [eax], edx + mov edx, [esp+8] ; block addr (relative) + mov [eax+4], edx + inc [ebp+NTFS.mft_retrieval_size] + jmp .scanmcb +.scanmcbend: + add esp, 10h +; there may be other portions of $DATA attribute in auxiliary records; +; if they will be needed, they will be loaded later + + mov [ebp+NTFS.cur_index_size], 0x1000/0x200 + push 0x1000 + call kernel_alloc + test eax, eax + jz .fail_free_mft + mov [ebp+NTFS.cur_index_buf], eax + + mov eax, ebp + jmp .pop_exit +endp + +.get_mft_retrieval_ptr: + pushad + mov eax, [ebp+NTFS.mft_retrieval_size] + cmp eax, [ebp+NTFS.mft_retrieval_alloc] + jnz .ok + add eax, 0x1000/8 + mov [ebp+NTFS.mft_retrieval_alloc], eax + shl eax, 3 + push eax + call kernel_alloc + test eax, eax + jnz @f + popad + add esp, 14h + jmp .fail_free_mft +@@: + mov esi, [ebp+NTFS.mft_retrieval] + mov edi, eax + mov ecx, [ebp+NTFS.mft_retrieval_size] + add ecx, ecx + rep movsd + push [ebp+NTFS.mft_retrieval] + mov [ebp+NTFS.mft_retrieval], eax + call kernel_free + mov eax, [ebp+NTFS.mft_retrieval_size] +.ok: + shl eax, 3 + add eax, [ebp+NTFS.mft_retrieval] + mov [esp+28], eax + popad + ret + +proc ntfs_free + push ebx + xchg ebx, eax + stdcall kernel_free, [ebx+NTFS.frs_buffer] + stdcall kernel_free, [ebx+NTFS.mft_retrieval] + stdcall kernel_free, [ebx+NTFS.cur_index_buf] + xchg ebx, eax + call free + pop ebx + ret +endp + +proc ntfs_lock + lea ecx, [ebp+NTFS.Lock] + jmp mutex_lock +endp + +proc ntfs_unlock + lea ecx, [ebp+NTFS.Lock] + jmp mutex_unlock +endp + +ntfs_read_frs_sector: + push ecx + mov ebx, [ebp+NTFS.frs_buffer] + push ebx + mov ecx, [ebp+NTFS.frs_size] + shr ecx, 9 + push ecx + mov ecx, eax +@@: + mov eax, ecx + call fs_read32_sys + test eax, eax + jnz .fail + add ebx, 0x200 + inc ecx + dec dword [esp] + jnz @b + pop eax +.fail: + pop ebx + pop ecx + ret + +ntfs_read_attr: +; in: variables in ebp+NTFS.* +; out: [ebp+NTFS.ntfs_cur_read] +; out: CF=1 => notfound, in this case eax=0 => disk ok, otherwise eax=disk error code + xor eax, eax + pushad + and [ebp+NTFS.ntfs_cur_read], 0 + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jnz .nomft + cmp [ebp+NTFS.ntfs_cur_attr], 0x80 + jnz .nomft + mov eax, [ebp+NTFS.mft_retrieval_end] + inc eax + mul [ebp+NTFS.sectors_per_cluster] + cmp eax, [ebp+NTFS.ntfs_cur_offs] + jbe .nomft +; precalculated part of $Mft $DATA + mov esi, [ebp+NTFS.mft_retrieval] + mov eax, [ebp+NTFS.ntfs_cur_offs] + xor edx, edx + div [ebp+NTFS.sectors_per_cluster] +; eax = VCN, edx = offset in sectors from beginning of cluster + xor ecx, ecx ; ecx will contain LCN +.mftscan: + add ecx, [esi+4] + sub eax, [esi] + jb @f + add esi, 8 + push eax + mov eax, [ebp+NTFS.mft_retrieval_end] + shl eax, 3 + add eax, [ebp+NTFS.mft_retrieval] + cmp eax, esi + pop eax + jnz .mftscan + jmp .nomft +@@: + push ecx + add ecx, eax + add ecx, [esi] + push eax + push edx + mov eax, [ebp+NTFS.sectors_per_cluster] + mul ecx +; eax = sector on partition + pop edx + add eax, edx + mov ebx, [ebp+NTFS.ntfs_cur_buf] + pop ecx + neg ecx + imul ecx, [ebp+NTFS.sectors_per_cluster] + sub ecx, edx + cmp ecx, [ebp+NTFS.ntfs_cur_size] + jb @f + mov ecx, [ebp+NTFS.ntfs_cur_size] +@@: +; ecx = number of sequential sectors to read + push eax + call fs_read32_sys + pop edx + test eax, eax + jnz .errread + add [ebp+NTFS.ntfs_cur_read], 0x200 + dec [ebp+NTFS.ntfs_cur_size] + inc [ebp+NTFS.ntfs_cur_offs] + add ebx, 0x200 + mov [ebp+NTFS.ntfs_cur_buf], ebx + lea eax, [edx+1] + loop @b + pop ecx + xor eax, eax + xor edx, edx + cmp [ebp+NTFS.ntfs_cur_size], eax + jz @f + add esi, 8 + push eax + mov eax, [ebp+NTFS.mft_retrieval_end] + shl eax, 3 + add eax, [ebp+NTFS.mft_retrieval] + cmp eax, esi + pop eax + jz .nomft + jmp .mftscan +@@: + popad + ret +.errread: + pop ecx +.errret: + mov [esp+28], eax + stc + popad + ret +.nomft: +; 1. Read file record. +; N.B. This will do recursive call of read_attr for $MFT::$Data. + mov eax, [ebp+NTFS.ntfs_cur_iRecord] + mov [ebp+NTFS.ntfs_attr_iRecord], eax + and [ebp+NTFS.ntfs_attr_list], 0 + or dword [ebp+NTFS.ntfs_attr_size], -1 + or dword [ebp+NTFS.ntfs_attr_size+4], -1 + or [ebp+NTFS.ntfs_attr_iBaseRecord], -1 + call ntfs_read_file_record + jc .errret +; 2. Find required attribute. + mov eax, [ebp+NTFS.frs_buffer] +; a) For auxiliary records, read base record +; N.B. If base record is present, +; base iRecord may be 0 (for $Mft), but SequenceNumber is nonzero + cmp dword [eax+24h], 0 + jz @f + mov eax, [eax+20h] +; test eax, eax +; jz @f +.beginfindattr: + mov [ebp+NTFS.ntfs_attr_iRecord], eax + call ntfs_read_file_record + jc .errret +@@: +; b) Scan for required attribute and for $ATTR_LIST + mov eax, [ebp+NTFS.frs_buffer] + movzx ecx, word [eax+14h] + add eax, ecx + mov ecx, [ebp+NTFS.ntfs_cur_attr] + and [ebp+NTFS.ntfs_attr_offs], 0 +.scanattr: + cmp dword [eax], -1 + jz .scandone + cmp dword [eax], ecx + jz .okattr + cmp [ebp+NTFS.ntfs_attr_iBaseRecord], -1 + jnz .scancont + cmp dword [eax], 0x20 ; $ATTR_LIST + jnz .scancont + mov [ebp+NTFS.ntfs_attr_list], eax + jmp .scancont +.okattr: +; ignore named $DATA attributes (aka NTFS streams) + cmp ecx, 0x80 + jnz @f + cmp byte [eax+9], 0 + jnz .scancont +@@: + mov [ebp+NTFS.ntfs_attr_offs], eax +.scancont: + add eax, [eax+4] + jmp .scanattr +.continue: + pushad + and [ebp+NTFS.ntfs_cur_read], 0 +.scandone: +; c) Check for required offset and length + mov ecx, [ebp+NTFS.ntfs_attr_offs] + jecxz .noattr + push [ebp+NTFS.ntfs_cur_size] + push [ebp+NTFS.ntfs_cur_read] + call .doreadattr + pop edx + pop ecx + jc @f + cmp [ebp+NTFS.ntfs_bCanContinue], 0 + jz @f + sub edx, [ebp+NTFS.ntfs_cur_read] + neg edx + shr edx, 9 + sub ecx, edx + mov [ebp+NTFS.ntfs_cur_size], ecx + jnz .not_in_cur +@@: + popad + ret +.noattr: +.not_in_cur: + cmp [ebp+NTFS.ntfs_cur_attr], 0x20 + jz @f + mov ecx, [ebp+NTFS.ntfs_attr_list] + test ecx, ecx + jnz .lookattr +.ret_is_attr: + and dword [esp+28], 0 + cmp [ebp+NTFS.ntfs_attr_offs], 1 ; CF set <=> ntfs_attr_offs == 0 + popad + ret +.lookattr: +; required attribute or required offset was not found in base record; +; it may be present in auxiliary records; +; scan $ATTR_LIST + mov eax, [ebp+NTFS.ntfs_attr_iBaseRecord] + cmp eax, -1 + jz @f + call ntfs_read_file_record + jc .errret + or [ebp+NTFS.ntfs_attr_iBaseRecord], -1 +@@: + push [ebp+NTFS.ntfs_cur_offs] + push [ebp+NTFS.ntfs_cur_size] + push [ebp+NTFS.ntfs_cur_read] + push [ebp+NTFS.ntfs_cur_buf] + push dword [ebp+NTFS.ntfs_attr_size] + push dword [ebp+NTFS.ntfs_attr_size+4] + or dword [ebp+NTFS.ntfs_attr_size], -1 + or dword [ebp+NTFS.ntfs_attr_size+4], -1 + and [ebp+NTFS.ntfs_cur_offs], 0 + mov [ebp+NTFS.ntfs_cur_size], 2 + and [ebp+NTFS.ntfs_cur_read], 0 + lea eax, [ebp+NTFS.ntfs_attrlist_buf] + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jnz @f + lea eax, [ebp+NTFS.ntfs_attrlist_mft_buf] +@@: + mov [ebp+NTFS.ntfs_cur_buf], eax + push eax + call .doreadattr + pop esi + mov edx, 1 + pop dword [ebp+NTFS.ntfs_attr_size+4] + pop dword [ebp+NTFS.ntfs_attr_size] + mov ecx, [ebp+NTFS.ntfs_cur_read] + pop [ebp+NTFS.ntfs_cur_buf] + pop [ebp+NTFS.ntfs_cur_read] + pop [ebp+NTFS.ntfs_cur_size] + pop [ebp+NTFS.ntfs_cur_offs] + jc .errret + or edi, -1 + lea ecx, [ecx+esi-1Ah] +.scanliststart: + push ecx + mov eax, [ebp+NTFS.ntfs_cur_attr] +.scanlist: + cmp esi, [esp] + jae .scanlistdone + cmp eax, [esi] + jz @f +.scanlistcont: + movzx ecx, word [esi+4] + add esi, ecx + jmp .scanlist +@@: +; ignore named $DATA attributes (aka NTFS streams) + cmp eax, 0x80 + jnz @f + cmp byte [esi+6], 0 + jnz .scanlistcont +@@: + push eax + mov eax, [esi+8] + test eax, eax + jnz .testf + mov eax, dword [ebp+NTFS.ntfs_attr_size] + and eax, dword [ebp+NTFS.ntfs_attr_size+4] + cmp eax, -1 + jnz .testfz +; if attribute is in auxiliary records, its size is defined only in first + mov eax, [esi+10h] + call ntfs_read_file_record + jnc @f +.errret_pop: + pop ecx ecx + jmp .errret +.errret2_pop: + xor eax, eax + jmp .errret_pop +@@: + mov eax, [ebp+NTFS.frs_buffer] + movzx ecx, word [eax+14h] + add eax, ecx + mov ecx, [ebp+NTFS.ntfs_cur_attr] +@@: + cmp dword [eax], -1 + jz .errret2_pop + cmp dword [eax], ecx + jz @f +.l1: + add eax, [eax+4] + jmp @b +@@: + cmp eax, 0x80 + jnz @f + cmp byte [eax+9], 0 + jnz .l1 +@@: + cmp byte [eax+8], 0 + jnz .sdnores + mov eax, [eax+10h] + mov dword [ebp+NTFS.ntfs_attr_size], eax + and dword [ebp+NTFS.ntfs_attr_size+4], 0 + jmp .testfz +.sdnores: + mov ecx, [eax+30h] + mov dword [ebp+NTFS.ntfs_attr_size], ecx + mov ecx, [eax+34h] + mov dword [ebp+NTFS.ntfs_attr_size+4], ecx +.testfz: + xor eax, eax +.testf: + imul eax, [ebp+NTFS.sectors_per_cluster] + cmp eax, [ebp+NTFS.ntfs_cur_offs] + pop eax + ja @f + mov edi, [esi+10h] ; keep previous iRecord + jmp .scanlistcont +@@: + pop ecx +.scanlistfound: + cmp edi, -1 + jnz @f + popad + ret +@@: + mov eax, [ebp+NTFS.ntfs_cur_iRecord] + mov [ebp+NTFS.ntfs_attr_iBaseRecord], eax + mov eax, edi + jmp .beginfindattr +.scanlistdone: + pop ecx + sub ecx, ebp + sub ecx, NTFS.ntfs_attrlist_buf-1Ah + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jnz @f + sub ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf +@@: + cmp ecx, 0x400 + jnz .scanlistfound + inc edx + push esi edi + lea esi, [ebp+NTFS.ntfs_attrlist_buf+0x200] + lea edi, [ebp+NTFS.ntfs_attrlist_buf] + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jnz @f + lea esi, [ebp+NTFS.ntfs_attrlist_mft_buf+0x200] + lea edi, [ebp+NTFS.ntfs_attrlist_mft_buf] +@@: + mov ecx, 0x200/4 + rep movsd + mov eax, edi + pop edi esi + sub esi, 0x200 + push [ebp+NTFS.ntfs_cur_offs] + push [ebp+NTFS.ntfs_cur_size] + push [ebp+NTFS.ntfs_cur_read] + push [ebp+NTFS.ntfs_cur_buf] + push dword [ebp+NTFS.ntfs_attr_size] + push dword [ebp+NTFS.ntfs_attr_size+4] + or dword [ebp+NTFS.ntfs_attr_size], -1 + or dword [ebp+NTFS.ntfs_attr_size+4], -1 + mov [ebp+NTFS.ntfs_cur_offs], edx + mov [ebp+NTFS.ntfs_cur_size], 1 + and [ebp+NTFS.ntfs_cur_read], 0 + mov [ebp+NTFS.ntfs_cur_buf], eax + mov ecx, [ebp+NTFS.ntfs_attr_list] + push esi edx edi + call .doreadattr + pop edi edx esi + mov ecx, [ebp+NTFS.ntfs_cur_read] + pop dword [ebp+NTFS.ntfs_attr_size+4] + pop dword [ebp+NTFS.ntfs_attr_size] + pop [ebp+NTFS.ntfs_cur_buf] + pop [ebp+NTFS.ntfs_cur_read] + pop [ebp+NTFS.ntfs_cur_size] + pop [ebp+NTFS.ntfs_cur_offs] + jc .errret + lea ecx, [ecx+ebp+NTFS.ntfs_attrlist_buf+0x200-0x1A] + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jnz .scanliststart + add ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf + jmp .scanliststart + +.doreadattr: + mov [ebp+NTFS.ntfs_bCanContinue], 0 + cmp byte [ecx+8], 0 + jnz .nonresident + mov eax, [ecx+10h] ; length + mov esi, eax + mov edx, [ebp+NTFS.ntfs_cur_offs] + shr eax, 9 + cmp eax, edx + jb .okret + shl edx, 9 + sub esi, edx + movzx eax, word [ecx+14h] + add edx, eax + add edx, ecx ; edx -> data + mov eax, [ebp+NTFS.ntfs_cur_size] + cmp eax, (0xFFFFFFFF shr 9)+1 + jbe @f + mov eax, (0xFFFFFFFF shr 9)+1 +@@: + shl eax, 9 + cmp eax, esi + jbe @f + mov eax, esi +@@: +; eax = length, edx -> data + mov [ebp+NTFS.ntfs_cur_read], eax + mov ecx, eax + mov eax, edx + mov ebx, [ebp+NTFS.ntfs_cur_buf] + call memmove + and [ebp+NTFS.ntfs_cur_size], 0 ; CF=0 + ret +.nonresident: +; Not all auxiliary records contain correct FileSize info + mov eax, dword [ebp+NTFS.ntfs_attr_size] + mov edx, dword [ebp+NTFS.ntfs_attr_size+4] + push eax + and eax, edx + cmp eax, -1 + pop eax + jnz @f + mov eax, [ecx+30h] ; FileSize + mov edx, [ecx+34h] + mov dword [ebp+NTFS.ntfs_attr_size], eax + mov dword [ebp+NTFS.ntfs_attr_size+4], edx +@@: + add eax, 0x1FF + adc edx, 0 + shrd eax, edx, 9 + sub eax, [ebp+NTFS.ntfs_cur_offs] + ja @f +; return with nothing read + and [ebp+NTFS.ntfs_cur_size], 0 +.okret: + clc + ret +@@: +; reduce read length + and [ebp+NTFS.ntfs_cur_tail], 0 + cmp [ebp+NTFS.ntfs_cur_size], eax + jb @f + mov [ebp+NTFS.ntfs_cur_size], eax + mov eax, dword [ebp+NTFS.ntfs_attr_size] + and eax, 0x1FF + mov [ebp+NTFS.ntfs_cur_tail], eax +@@: + cmp [ebp+NTFS.ntfs_cur_size], 0 + jz .okret + mov eax, [ebp+NTFS.ntfs_cur_offs] + xor edx, edx + div [ebp+NTFS.sectors_per_cluster] + sub eax, [ecx+10h] ; first_vbo + jb .okret +; eax = cluster, edx = starting sector + sub esp, 10h + movzx esi, word [ecx+20h] ; mcb_info_ofs + add esi, ecx + xor edi, edi +.readloop: + call ntfs_decode_mcb_entry + jnc .break + add edi, [esp+8] + sub eax, [esp] + jae .readloop + push ecx + push eax + add eax, [esp+8] + add eax, edi + imul eax, [ebp+NTFS.sectors_per_cluster] + add eax, edx + pop ecx + neg ecx + imul ecx, [ebp+NTFS.sectors_per_cluster] + sub ecx, edx + cmp ecx, [ebp+NTFS.ntfs_cur_size] + jb @f + mov ecx, [ebp+NTFS.ntfs_cur_size] +@@: + mov ebx, [ebp+NTFS.ntfs_cur_buf] +@@: + push eax + cmp [ebp+NTFS.ntfs_cur_attr], 0x80 + jnz .sys + cmp [ebp+NTFS.ntfs_cur_iRecord], 0 + jz .sys + call fs_read32_app + jmp .appsys +.sys: + call fs_read32_sys +.appsys: + pop edx + test eax, eax + jnz .errread2 + add ebx, 0x200 + mov [ebp+NTFS.ntfs_cur_buf], ebx + lea eax, [edx+1] + add [ebp+NTFS.ntfs_cur_read], 0x200 + dec [ebp+NTFS.ntfs_cur_size] + inc [ebp+NTFS.ntfs_cur_offs] + loop @b + pop ecx + xor eax, eax + xor edx, edx + cmp [ebp+NTFS.ntfs_cur_size], 0 + jnz .readloop + add esp, 10h + mov eax, [ebp+NTFS.ntfs_cur_tail] + test eax, eax + jz @f + sub eax, 0x200 + add [ebp+NTFS.ntfs_cur_read], eax +@@: + clc + ret +.errread2: + pop ecx + add esp, 10h + stc + ret +.break: + add esp, 10h ; CF=0 + mov [ebp+NTFS.ntfs_bCanContinue], 1 + ret + +ntfs_read_file_record: +; in: eax=iRecord +; out: [ebp+NTFS.frs_buffer] contains information +; CF=1 - failed, in this case eax=0 => something with FS, eax nonzero => disk error +; Read attr $DATA of $Mft, starting from eax*[ebp+NTFS.frs_size] + push ecx edx + mov ecx, [ebp+NTFS.frs_size] + mul ecx + shrd eax, edx, 9 + shr edx, 9 + jnz .errret + push [ebp+NTFS.ntfs_attr_iRecord] + push [ebp+NTFS.ntfs_attr_iBaseRecord] + push [ebp+NTFS.ntfs_attr_offs] + push [ebp+NTFS.ntfs_attr_list] + push dword [ebp+NTFS.ntfs_attr_size+4] + push dword [ebp+NTFS.ntfs_attr_size] + push [ebp+NTFS.ntfs_cur_iRecord] + push [ebp+NTFS.ntfs_cur_attr] + push [ebp+NTFS.ntfs_cur_offs] + push [ebp+NTFS.ntfs_cur_size] + push [ebp+NTFS.ntfs_cur_buf] + push [ebp+NTFS.ntfs_cur_read] + mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA + and [ebp+NTFS.ntfs_cur_iRecord], 0 ; $Mft + mov [ebp+NTFS.ntfs_cur_offs], eax + shr ecx, 9 + mov [ebp+NTFS.ntfs_cur_size], ecx + mov eax, [ebp+NTFS.frs_buffer] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr + mov edx, [ebp+NTFS.ntfs_cur_read] + pop [ebp+NTFS.ntfs_cur_read] + pop [ebp+NTFS.ntfs_cur_buf] + pop [ebp+NTFS.ntfs_cur_size] + pop [ebp+NTFS.ntfs_cur_offs] + pop [ebp+NTFS.ntfs_cur_attr] + pop [ebp+NTFS.ntfs_cur_iRecord] + pop dword [ebp+NTFS.ntfs_attr_size] + pop dword [ebp+NTFS.ntfs_attr_size+4] + pop [ebp+NTFS.ntfs_attr_list] + pop [ebp+NTFS.ntfs_attr_offs] + pop [ebp+NTFS.ntfs_attr_iBaseRecord] + pop [ebp+NTFS.ntfs_attr_iRecord] + jc .ret + cmp edx, [ebp+NTFS.frs_size] + jnz .errret + mov eax, [ebp+NTFS.frs_buffer] + cmp dword [eax], 'FILE' + jnz .errret + push ebx + mov ebx, eax + call ntfs_restore_usa_frs + pop ebx + jc .errret +.ret: + pop edx ecx + ret +.errret: + pop edx ecx + xor eax, eax + stc + ret + +ntfs_restore_usa_frs: + mov eax, [ebp+NTFS.frs_size] +ntfs_restore_usa: + pushad + shr eax, 9 + mov ecx, eax + inc eax + cmp [ebx+6], ax + jnz .err + movzx eax, word [ebx+4] + lea esi, [eax+ebx] + lodsw + mov edx, eax + lea edi, [ebx+0x1FE] +@@: + cmp [edi], dx + jnz .err + lodsw + stosw + add edi, 0x1FE + loop @b + popad + clc + ret +.err: + popad + stc + ret + +ntfs_decode_mcb_entry: + push eax ecx edi + lea edi, [esp+16] + xor eax, eax + lodsb + test al, al + jz .end + mov ecx, eax + and ecx, 0xF + cmp ecx, 8 + ja .end + push ecx + rep movsb + pop ecx + sub ecx, 8 + neg ecx + cmp byte [esi-1], 80h + jae .end + push eax + xor eax, eax + rep stosb + pop ecx + shr ecx, 4 + cmp ecx, 8 + ja .end + push ecx + rep movsb + pop ecx + sub ecx, 8 + neg ecx + cmp byte [esi-1], 80h + cmc + sbb eax, eax + rep stosb + stc +.end: + pop edi ecx eax + ret + +unichar_toupper: + push eax + call uni2ansi_char + cmp al, '_' + jz .unk + add esp, 4 + call char_toupper + jmp ansi2uni_char +.unk: + pop eax + ret + +ntfs_find_lfn: +; in: esi+[esp+4] -> name +; out: CF=1 - file not found +; else CF=0, [ebp+NTFS.ntfs_cur_iRecord] valid, eax->record in parent directory + mov [ebp+NTFS.ntfs_cur_iRecord], 5 ; start parse from root cluster +.doit2: + mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT + and [ebp+NTFS.ntfs_cur_offs], 0 + mov eax, [ebp+NTFS.cur_index_size] + mov [ebp+NTFS.ntfs_cur_size], eax + mov eax, [ebp+NTFS.cur_index_buf] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr + jnc @f +.ret: + ret 4 +@@: + xor eax, eax + cmp [ebp+NTFS.ntfs_cur_read], 0x20 + jc .ret + pushad + mov esi, [ebp+NTFS.cur_index_buf] + mov eax, [esi+14h] + add eax, 10h + cmp [ebp+NTFS.ntfs_cur_read], eax + jae .readok1 + add eax, 1FFh + shr eax, 9 + cmp eax, [ebp+NTFS.cur_index_size] + ja @f +.stc_ret: + popad + stc + ret 4 +@@: +; reallocate + push eax + push [ebp+NTFS.cur_index_buf] + call kernel_free + pop eax + mov [ebp+NTFS.cur_index_size], eax + push eax + call kernel_alloc + test eax, eax + jnz @f + and [ebp+NTFS.cur_index_size], 0 + and [ebp+NTFS.cur_index_buf], 0 + jmp .stc_ret +@@: + mov [ebp+NTFS.cur_index_buf], eax + popad + jmp .doit2 +.readok1: + mov edx, [esi+8] ; subnode_size + shr edx, 9 + cmp edx, [ebp+NTFS.cur_index_size] + jbe .ok2 + push esi edx + push edx + call kernel_alloc + pop edx esi + test eax, eax + jz .stc_ret + mov edi, eax + mov ecx, [ebp+NTFS.cur_index_size] + shl ecx, 9-2 + rep movsd + mov esi, eax + mov [ebp+NTFS.cur_index_size], edx + push esi edx + push [ebp+NTFS.cur_index_buf] + call kernel_free + pop edx esi + mov [ebp+NTFS.cur_index_buf], esi +.ok2: + add esi, 10h + mov edi, [esp+4] +; edi -> name, esi -> current index data, edx = subnode size +.scanloop: + add esi, [esi] +.scanloopint: + test byte [esi+0Ch], 2 + jnz .subnode + push esi + add esi, 0x52 + movzx ecx, byte [esi-2] + push edi +@@: + lodsw + call unichar_toupper + push eax + mov al, [edi] + inc edi + cmp al, '/' + jz .slash + call char_toupper + call ansi2uni_char + cmp ax, [esp] + pop eax + loopz @b + jz .found + pop edi + pop esi + jb .subnode +.scanloopcont: + movzx eax, word [esi+8] + add esi, eax + jmp .scanloopint +.slash: + pop eax + pop edi + pop esi +.subnode: + test byte [esi+0Ch], 1 + jz .notfound + movzx eax, word [esi+8] + mov eax, [esi+eax-8] + imul eax, [ebp+NTFS.sectors_per_cluster] + mov [ebp+NTFS.ntfs_cur_offs], eax + mov [ebp+NTFS.ntfs_cur_attr], 0xA0 ; $INDEX_ALLOCATION + mov [ebp+NTFS.ntfs_cur_size], edx + mov eax, [ebp+NTFS.cur_index_buf] + mov esi, eax + mov [ebp+NTFS.ntfs_cur_buf], eax + push edx + call ntfs_read_attr + pop edx + mov eax, edx + shl eax, 9 + cmp [ebp+NTFS.ntfs_cur_read], eax + jnz .notfound + cmp dword [esi], 'INDX' + jnz .notfound + mov ebx, esi + call ntfs_restore_usa + jc .notfound + add esi, 0x18 + jmp .scanloop +.notfound: + popad + stc + ret 4 +.found: + cmp byte [edi], 0 + jz .done + cmp byte [edi], '/' + jz .next + pop edi + pop esi + jmp .scanloopcont +.done: +.next: + pop esi + pop esi + mov eax, [esi] + mov [ebp+NTFS.ntfs_cur_iRecord], eax + mov [esp+1Ch], esi + mov [esp+4], edi + popad + inc esi + cmp byte [esi-1], 0 + jnz .doit2 + cmp dword [esp+4], 0 + jz @f + mov esi, [esp+4] + mov dword [esp+4], 0 + jmp .doit2 +@@: + ret 4 + +;---------------------------------------------------------------- +; ntfs_Read - NTFS implementation of reading a file +; in: ebp = pointer to NTFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +ntfs_Read: + cmp byte [esi], 0 + jnz @f + or ebx, -1 + movi eax, ERROR_ACCESS_DENIED + ret +@@: + call ntfs_lock + stdcall ntfs_find_lfn, [esp+4] + jnc .found + call ntfs_unlock + or ebx, -1 + movi eax, ERROR_FILE_NOT_FOUND + ret +.found: + mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA + and [ebp+NTFS.ntfs_cur_offs], 0 + and [ebp+NTFS.ntfs_cur_size], 0 + call ntfs_read_attr + jnc @f + call ntfs_unlock + or ebx, -1 + movi eax, ERROR_ACCESS_DENIED + ret +@@: + pushad + and dword [esp+10h], 0 + xor eax, eax + cmp dword [ebx+8], 0x200 + jb @f +.eof0: + popad + xor ebx, ebx +.eof: + movi eax, ERROR_END_OF_FILE + push eax + call ntfs_unlock + pop eax + ret +@@: + mov ecx, [ebx+12] + mov edx, [ebx+16] + mov eax, [ebx+4] + test eax, 0x1FF + jz .alignedstart + push edx + mov edx, [ebx+8] + shrd eax, edx, 9 + pop edx + mov [ebp+NTFS.ntfs_cur_offs], eax + mov [ebp+NTFS.ntfs_cur_size], 1 + lea eax, [ebp+NTFS.ntfs_bitmap_buf] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr.continue + mov eax, [ebx+4] + and eax, 0x1FF + lea esi, [ebp+NTFS.ntfs_bitmap_buf+eax] + sub eax, [ebp+NTFS.ntfs_cur_read] + jae .eof0 + neg eax + push ecx + cmp ecx, eax + jb @f + mov ecx, eax +@@: + mov [esp+10h+4], ecx + mov edi, edx + rep movsb + mov edx, edi + pop ecx + sub ecx, [esp+10h] + jnz @f +.retok: + popad + call ntfs_unlock + xor eax, eax + ret +@@: + cmp [ebp+NTFS.ntfs_cur_read], 0x200 + jz .alignedstart +.eof_ebx: + popad + jmp .eof +.alignedstart: + mov eax, [ebx+4] + push edx + mov edx, [ebx+8] + add eax, 511 + adc edx, 0 + shrd eax, edx, 9 + pop edx +.zero1: + mov [ebp+NTFS.ntfs_cur_offs], eax + mov [ebp+NTFS.ntfs_cur_buf], edx + mov eax, ecx + shr eax, 9 + mov [ebp+NTFS.ntfs_cur_size], eax + add eax, [ebp+NTFS.ntfs_cur_offs] + push eax + call ntfs_read_attr.continue + pop [ebp+NTFS.ntfs_cur_offs] + mov eax, [ebp+NTFS.ntfs_cur_read] + add [esp+10h], eax + mov eax, ecx + and eax, not 0x1FF + cmp [ebp+NTFS.ntfs_cur_read], eax + jnz .eof_ebx + and ecx, 0x1FF + jz .retok + add edx, [ebp+NTFS.ntfs_cur_read] + mov [ebp+NTFS.ntfs_cur_size], 1 + lea eax, [ebp+NTFS.ntfs_bitmap_buf] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr.continue + cmp [ebp+NTFS.ntfs_cur_read], ecx + jb @f + mov [ebp+NTFS.ntfs_cur_read], ecx +@@: + xchg ecx, [ebp+NTFS.ntfs_cur_read] + push ecx + mov edi, edx + lea esi, [ebp+NTFS.ntfs_bitmap_buf] + add [esp+10h+4], ecx + rep movsb + pop ecx + xor eax, eax + cmp ecx, [ebp+NTFS.ntfs_cur_read] + jz @f + mov al, ERROR_END_OF_FILE +@@: + mov [esp+1Ch], eax + call ntfs_unlock + popad + ret + +;---------------------------------------------------------------- +; ntfs_ReadFolder - NTFS implementation of reading a folder +; in: ebp = pointer to NTFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +ntfs_ReadFolder: + call ntfs_lock + mov eax, 5 ; root cluster + cmp byte [esi], 0 + jz .doit + stdcall ntfs_find_lfn, [esp+4] + jnc .doit2 +.notfound: + or ebx, -1 + push ERROR_FILE_NOT_FOUND +.pop_ret: + call ntfs_unlock + pop eax + ret +.doit: + mov [ebp+NTFS.ntfs_cur_iRecord], eax +.doit2: + mov [ebp+NTFS.ntfs_cur_attr], 0x10 ; $STANDARD_INFORMATION + and [ebp+NTFS.ntfs_cur_offs], 0 + mov [ebp+NTFS.ntfs_cur_size], 1 + lea eax, [ebp+NTFS.ntfs_bitmap_buf] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr + jc .notfound + mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT + and [ebp+NTFS.ntfs_cur_offs], 0 + mov eax, [ebp+NTFS.cur_index_size] + mov [ebp+NTFS.ntfs_cur_size], eax + mov eax, [ebp+NTFS.cur_index_buf] + mov [ebp+NTFS.ntfs_cur_buf], eax + call ntfs_read_attr + jnc .ok + test eax, eax + jz .notfound + or ebx, -1 + push 11 + jmp .pop_ret +.ok: + cmp [ebp+NTFS.ntfs_cur_read], 0x20 + jae @f + or ebx, -1 +.fserr: + push ERROR_FAT_TABLE + jmp .pop_ret +@@: + pushad + mov esi, [ebp+NTFS.cur_index_buf] + mov eax, [esi+14h] + add eax, 10h + cmp [ebp+NTFS.ntfs_cur_read], eax + jae .readok1 + add eax, 1FFh + shr eax, 9 + cmp eax, [ebp+NTFS.cur_index_size] + ja @f + popad + jmp .fserr +@@: +; reallocate + push eax + push [ebp+NTFS.cur_index_buf] + call kernel_free + pop eax + mov [ebp+NTFS.cur_index_size], eax + push eax + call kernel_alloc + test eax, eax + jnz @f + and [ebp+NTFS.cur_index_size], 0 + and [ebp+NTFS.cur_index_buf], 0 +.nomem: + call ntfs_unlock + popad + or ebx, -1 + movi eax, 12 + ret +@@: + mov [ebp+NTFS.cur_index_buf], eax + popad + jmp .doit2 +.readok1: + mov edx, [esi+8] ; subnode_size + shr edx, 9 + mov [ebp+NTFS.cur_subnode_size], edx + cmp edx, [ebp+NTFS.cur_index_size] + jbe .ok2 + push esi edx + push edx + call kernel_alloc + pop edx esi + test eax, eax + jz .nomem + mov edi, eax + mov ecx, [ebp+NTFS.cur_index_size] + shl ecx, 9-2 + rep movsd + mov esi, eax + mov [ebp+NTFS.cur_index_size], edx + push [ebp+NTFS.cur_index_buf] + call kernel_free + mov [ebp+NTFS.cur_index_buf], esi +.ok2: + add esi, 10h + mov edx, [ebx+16] + push dword [ebx+8] ; read ANSI/UNICODE name +; init header + mov edi, edx + mov ecx, 32/4 + xor eax, eax + rep stosd + mov byte [edx], 1 ; version + mov ecx, [ebx+12] + mov ebx, [ebx+4] + push edx + mov edx, esp +; edi -> BDFE, esi -> current index data, ebx = first wanted block, +; ecx = number of blocks to read +; edx -> parameters block: dd , dd + cmp [ebp+NTFS.ntfs_cur_iRecord], 5 + jz .skip_specials +; dot and dotdot entries + push esi + xor esi, esi + call .add_special_entry + inc esi + call .add_special_entry + pop esi +.skip_specials: +; at first, dump index root + add esi, [esi] +.dump_root: + test byte [esi+0Ch], 2 + jnz .dump_root_done + call .add_entry + movzx eax, word [esi+8] + add esi, eax + jmp .dump_root +.dump_root_done: +; now dump all subnodes + push ecx edi + lea edi, [ebp+NTFS.ntfs_bitmap_buf] + mov [ebp+NTFS.ntfs_cur_buf], edi + mov ecx, 0x400/4 + xor eax, eax + rep stosd + mov [ebp+NTFS.ntfs_cur_attr], 0xB0 ; $BITMAP + and [ebp+NTFS.ntfs_cur_offs], 0 + mov [ebp+NTFS.ntfs_cur_size], 2 + call ntfs_read_attr + pop edi ecx + push 0 ; save offset in $BITMAP attribute + and [ebp+NTFS.ntfs_cur_offs], 0 +.dumploop: + mov [ebp+NTFS.ntfs_cur_attr], 0xA0 + mov eax, [ebp+NTFS.cur_subnode_size] + mov [ebp+NTFS.ntfs_cur_size], eax + mov eax, [ebp+NTFS.cur_index_buf] + mov esi, eax + mov [ebp+NTFS.ntfs_cur_buf], eax + push [ebp+NTFS.ntfs_cur_offs] + mov eax, [ebp+NTFS.ntfs_cur_offs] + imul eax, [ebp+NTFS.cur_subnode_size] + mov [ebp+NTFS.ntfs_cur_offs], eax + call ntfs_read_attr + pop [ebp+NTFS.ntfs_cur_offs] + mov eax, [ebp+NTFS.cur_subnode_size] + shl eax, 9 + cmp [ebp+NTFS.ntfs_cur_read], eax + jnz .done + push eax + mov eax, [ebp+NTFS.ntfs_cur_offs] + and eax, 0x400*8-1 + bt dword [ebp+NTFS.ntfs_bitmap_buf], eax + pop eax + jnc .dump_subnode_done + cmp dword [esi], 'INDX' + jnz .dump_subnode_done + push ebx + mov ebx, esi + call ntfs_restore_usa + pop ebx + jc .dump_subnode_done + add esi, 0x18 + add esi, [esi] +.dump_subnode: + test byte [esi+0Ch], 2 + jnz .dump_subnode_done + call .add_entry + movzx eax, word [esi+8] + add esi, eax + jmp .dump_subnode +.dump_subnode_done: + inc [ebp+NTFS.ntfs_cur_offs] + test [ebp+NTFS.ntfs_cur_offs], 0x400*8-1 + jnz .dumploop + mov [ebp+NTFS.ntfs_cur_attr], 0xB0 + push ecx edi + lea edi, [ebp+NTFS.ntfs_bitmap_buf] + mov [ebp+NTFS.ntfs_cur_buf], edi + mov ecx, 0x400/4 + xor eax, eax + rep stosd + pop edi ecx + pop eax + push [ebp+NTFS.ntfs_cur_offs] + inc eax + mov [ebp+NTFS.ntfs_cur_offs], eax + mov [ebp+NTFS.ntfs_cur_size], 2 + push eax + call ntfs_read_attr + pop eax + pop [ebp+NTFS.ntfs_cur_offs] + push eax + jmp .dumploop +.done: + pop eax + pop edx + mov ebx, [edx+4] + pop edx + xor eax, eax + dec ecx + js @f + mov al, ERROR_END_OF_FILE +@@: + mov [esp+1Ch], eax + mov [esp+10h], ebx + call ntfs_unlock + popad + ret + +.add_special_entry: + mov eax, [edx] + inc dword [eax+8] ; new file found + dec ebx + jns .ret + dec ecx + js .ret + inc dword [eax+4] ; new file block copied + mov eax, [edx+4] + mov [edi+4], eax +; mov eax, dword [ntfs_bitmap_buf+0x20] +; or al, 0x10 + mov eax, 0x10 + stosd + scasd + push edx + mov eax, dword [ebp+NTFS.ntfs_bitmap_buf] + mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+4] + call ntfs_datetime_to_bdfe + mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+0x18] + mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0x1C] + call ntfs_datetime_to_bdfe + mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+8] + mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0xC] + call ntfs_datetime_to_bdfe + pop edx + xor eax, eax + stosd + stosd + mov al, '.' + push edi ecx + lea ecx, [esi+1] + test byte [edi-0x24], 1 + jz @f + rep stosw + pop ecx + xor eax, eax + stosw + pop edi + add edi, 520 + ret +@@: + rep stosb + pop ecx + xor eax, eax + stosb + pop edi + add edi, 264 +.ret: + ret + +.add_entry: +; do not return DOS 8.3 names + cmp byte [esi+0x51], 2 + jz .ret +; do not return system files +; ... note that there will be no bad effects if system files also were reported ... + cmp dword [esi], 0x10 + jb .ret + mov eax, [edx] + inc dword [eax+8] ; new file found + dec ebx + jns .ret + dec ecx + js .ret + inc dword [eax+4] ; new file block copied + mov eax, [edx+4] ; flags + call ntfs_direntry_to_bdfe + push ecx esi edi + movzx ecx, byte [esi+0x50] + add esi, 0x52 + test byte [edi-0x24], 1 + jz .ansi + shr ecx, 1 + rep movsd + adc ecx, ecx + rep movsw + and word [edi], 0 + pop edi + add edi, 520 + pop esi ecx + ret +.ansi: + jecxz .skip +@@: + lodsw + call uni2ansi_char + stosb + loop @b +.skip: + xor al, al + stosb + pop edi + add edi, 264 + pop esi ecx + ret + +ntfs_direntry_to_bdfe: + mov [edi+4], eax ; ANSI/UNICODE name + mov eax, [esi+48h] + test eax, 0x10000000 + jz @f + and eax, not 0x10000000 + or al, 0x10 +@@: + stosd + scasd + push edx + mov eax, [esi+0x18] + mov edx, [esi+0x1C] + call ntfs_datetime_to_bdfe + mov eax, [esi+0x30] + mov edx, [esi+0x34] + call ntfs_datetime_to_bdfe + mov eax, [esi+0x20] + mov edx, [esi+0x24] + call ntfs_datetime_to_bdfe + pop edx + mov eax, [esi+0x40] + stosd + mov eax, [esi+0x44] + stosd + ret + +iglobal +_24 dd 24 +_60 dd 60 +_10000000 dd 10000000 +days400year dd 365*400+100-4+1 +days100year dd 365*100+25-1 +days4year dd 365*4+1 +days1year dd 365 +months dd 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +months2 dd 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +_400 dd 400 +_100 dd 100 +endg + +ntfs_datetime_to_bdfe: +; edx:eax = number of 100-nanosecond intervals since January 1, 1601, in UTC + push eax + mov eax, edx + xor edx, edx + div [_10000000] + xchg eax, [esp] + div [_10000000] + pop edx + .sec: +; edx:eax = number of seconds since January 1, 1601 + push eax + mov eax, edx + xor edx, edx + div [_60] + xchg eax, [esp] + div [_60] + mov [edi], dl + pop edx +; edx:eax = number of minutes + div [_60] + mov [edi+1], dl +; eax = number of hours (note that 2^64/(10^7*60*60) < 2^32) + xor edx, edx + div [_24] + mov [edi+2], dl + mov [edi+3], byte 0 +; eax = number of days since January 1, 1601 + xor edx, edx + div [days400year] + imul eax, 400 + add eax, 1601 + mov [edi+6], ax + mov eax, edx + xor edx, edx + div [days100year] + cmp al, 4 + jnz @f + dec eax + add edx, [days100year] +@@: + imul eax, 100 + add [edi+6], ax + mov eax, edx + xor edx, edx + div [days4year] + shl eax, 2 + add [edi+6], ax + mov eax, edx + xor edx, edx + div [days1year] + cmp al, 4 + jnz @f + dec eax + add edx, [days1year] +@@: + add [edi+6], ax + push esi edx + mov esi, months + movzx eax, word [edi+6] + test al, 3 + jnz .noleap + xor edx, edx + push eax + div [_400] + pop eax + test edx, edx + jz .leap + xor edx, edx + div [_100] + test edx, edx + jz .noleap +.leap: + mov esi, months2 +.noleap: + pop edx + xor eax, eax + inc eax +@@: + sub edx, [esi] + jb @f + add esi, 4 + inc eax + jmp @b +@@: + add edx, [esi] + pop esi + inc edx + mov [edi+4], dl + mov [edi+5], al + add edi, 8 + ret + +;---------------------------------------------------------------- +; ntfs_Rewrite - NTFS implementation of creating a new file +; in: ebp = pointer to NTFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +ntfs_Rewrite: +ntfs_CreateFolder: + xor ebx, ebx + mov eax, ERROR_UNSUPPORTED_FS + ret + +;---------------------------------------------------------------- +; ntfs_Write - NTFS implementation of writing to file +; in: ebp = pointer to NTFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +ntfs_Write: + xor ebx, ebx + mov eax, ERROR_UNSUPPORTED_FS + ret + +ntfs_SetFileEnd: +ntfs_SetFileInfo: +ntfs_Delete: + mov eax, ERROR_UNSUPPORTED_FS + ret + +;---------------------------------------------------------------- +; ntfs_GetFileInfo - NTFS implementation of getting file info +; in: ebp = pointer to NTFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +ntfs_GetFileInfo: + cmp byte [esi], 0 + jnz @f + movi eax, 2 + ret +@@: + call ntfs_lock + stdcall ntfs_find_lfn, [esp+4] + jnc .doit + test eax, eax + movi eax, ERROR_FILE_NOT_FOUND + jz @f + mov al, 11 +@@: + push eax + call ntfs_unlock + pop eax + ret +.doit: + push esi edi + mov esi, eax + mov edi, [ebx+16] + xor eax, eax + call ntfs_direntry_to_bdfe + pop edi esi + call ntfs_unlock + xor eax, eax + ret + diff --git a/kernel/branches/kolibri-process/fs/parse_fn.inc b/kernel/branches/kolibri-process/fs/parse_fn.inc new file mode 100644 index 000000000..ffb41ac59 --- /dev/null +++ b/kernel/branches/kolibri-process/fs/parse_fn.inc @@ -0,0 +1,247 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------------------------------------------------------------------------- +; +; File path partial substitution (according to configuration) +; +; +; SPraid +; +;------------------------------------------------------------------------- + +$Revision: 3780 $ + + +iglobal +; pointer to memory for path replace table, +; size of one record is 128 bytes: 64 bytes for search pattern + 64 bytes for replace string + +; start with one entry: sys -> +full_file_name_table dd sysdir_name +.size dd 1 + +tmp_file_name_size dd 1 +endg + +uglobal +; Parser_params will initialize: sysdir_name = "sys", sysdir_path = +sysdir_name rb 64 +sysdir_path rb 64 +sysdir_name1 rb 64 +sysdir_path1 rb 64 + +; for example: +;dir_name1 db 'KolibriOS',0 +; rb 64-8 +;dir_path1 db 'HD0/1',0 +; rb 64-6 +endg + +uglobal +tmp_file_name_table dd ? +endg + +; use bx_from_load and init system directory /sys +proc Parser_params +locals + buff db 4 dup(?) ; for test cd +endl + mov eax, [OS_BASE+0x10000+bx_from_load] + mov ecx, sysdir_path + mov [ecx-64], dword 'sys' + cmp al, 'r'; if ram disk + jnz @f + mov [ecx], dword 'RD/?' + mov [ecx+3], byte ah + mov [ecx+4], byte 0 + ret +@@: + cmp al, 'm'; if ram disk + jnz @f + mov [ecx], dword 'CD?/'; if cd disk {m} + mov [ecx+4], byte '1' + mov [ecx+5], dword '/KOL' + mov [ecx+9], dword 'IBRI' + mov [ecx+13], byte 0 +.next_cd: + mov [ecx+2], byte ah + inc ah + cmp ah, '5' + je .not_found_cd + lea edx, [buff] + pushad + stdcall read_file, read_firstapp, edx, 0, 4 + popad + cmp [edx], dword 'MENU' + jne .next_cd + jmp .ok + +@@: + sub al, 49 + mov [ecx], dword 'HD?/'; if hard disk + mov [ecx+2], byte al + mov [ecx+4], byte ah + mov [ecx+5], dword '/KOL' + mov [ecx+9], dword 'IBRI' + mov [ecx+13], byte 0 +.ok: +.not_found_cd: + ret +endp + +proc load_file_parse_table + stdcall kernel_alloc, 0x1000 + mov [tmp_file_name_table], eax + mov edi, eax + mov esi, sysdir_name + mov ecx, 128/4 + rep movsd + + invoke ini.enum_keys, conf_fname, conf_path_sect, get_every_key + + mov eax, [tmp_file_name_table] + mov [full_file_name_table], eax + mov eax, [tmp_file_name_size] + mov [full_file_name_table.size], eax + ret +endp + +uglobal +def_val_1 db 0 +endg + +proc get_every_key stdcall, f_name, sec_name, key_name + mov esi, [key_name] + mov ecx, esi + cmp byte [esi], '/' + jnz @f + inc esi +@@: + mov edi, [tmp_file_name_size] + shl edi, 7 + cmp edi, 0x1000 + jae .stop_parse + add edi, [tmp_file_name_table] + lea ebx, [edi+64] +@@: + cmp edi, ebx + jae .skip_this_key + lodsb + test al, al + jz @f + or al, 20h + stosb + jmp @b +@@: + stosb + + invoke ini.get_str, [f_name], [sec_name], ecx, ebx, 64, def_val_1 + + cmp byte [ebx], '/' + jnz @f + lea esi, [ebx+1] + mov edi, ebx + mov ecx, 63 + rep movsb +@@: + push ebp + mov ebp, [tmp_file_name_table] + mov ecx, [tmp_file_name_size] + jecxz .noreplace + mov eax, ecx + dec eax + shl eax, 7 + add ebp, eax +.replace_loop: + mov edi, ebx + mov esi, ebp +@@: + lodsb + test al, al + jz .doreplace + mov dl, [edi] + inc edi + test dl, dl + jz .replace_loop_cont + or dl, 20h + cmp al, dl + jz @b + jmp .replace_loop_cont +.doreplace: + cmp byte [edi], 0 + jz @f + cmp byte [edi], '/' + jnz .replace_loop_cont +@@: + lea esi, [ebp+64] + call .replace + jc .skip_this_key2 +.replace_loop_cont: + sub ebp, 128 + loop .replace_loop +.noreplace: + pop ebp + + inc [tmp_file_name_size] +.skip_this_key: + xor eax, eax + inc eax + ret +.skip_this_key2: + pop ebp + jmp .skip_this_key +.stop_parse: + xor eax, eax + ret +endp + +proc get_every_key.replace +; in: ebx->destination, esi->first part of name, edi->second part of name +; maximum length is 64 bytes +; out: CF=1 <=> overflow +; 1) allocate temporary buffer in stack + sub esp, 64 +; 2) save second part of name to temporary buffer + push esi + lea esi, [esp+4] ; esi->tmp buffer + xchg esi, edi ; edi->tmp buffer, esi->source +@@: + lodsb + stosb + test al, al + jnz @b +; 3) copy first part of name to destination + pop esi + mov edi, ebx +@@: + lodsb + test al, al + jz @f + stosb + jmp @b +@@: +; 4) restore second part of name from temporary buffer to destination +; (may cause overflow) + lea edx, [ebx+64] ; limit of destination + mov esi, esp +@@: + cmp edi, edx + jae .overflow + lodsb + stosb + test al, al + jnz @b +; all is OK + add esp, 64 ; CF is cleared + ret +.overflow: +; name is too long + add esp, 64 + stc + ret +endp diff --git a/kernel/branches/kolibri-process/fs/xfs.asm b/kernel/branches/kolibri-process/fs/xfs.asm new file mode 100644 index 000000000..0ab3de2fa --- /dev/null +++ b/kernel/branches/kolibri-process/fs/xfs.asm @@ -0,0 +1,2769 @@ +include 'xfs.inc' + +; +; This file contains XFS related code. +; For more information on XFS check sources below. +; +; 1. XFS Filesystem Structure, 2nd Edition, Revision 1. Silicon Graphics Inc. 2006 +; 2. Linux source http://kernel.org +; + + +; test partition type (valid XFS one?) +; alloc and fill XFS (see xfs.inc) structure +; this function is called for each partition +; returns 0 (not XFS or invalid) / pointer to partition structure +xfs_create_partition: + push ebx ecx edx esi edi + cmp dword[ebx + xfs_sb.sb_magicnum], XFS_SB_MAGIC ; signature + jne .error + + ; TODO: check XFS.versionnum and XFS.features2 + ; print superblock params for debugging (waiting for bug reports) + + movi eax, sizeof.XFS + call malloc + test eax, eax + jz .error + + ; standard partition initialization, common for all file systems + + mov edi, eax + mov eax, dword[ebp + PARTITION.FirstSector] + mov dword[edi + XFS.FirstSector], eax + mov eax, dword[ebp + PARTITION.FirstSector + 4] + mov dword[edi + XFS.FirstSector + 4], eax + mov eax, dword[ebp + PARTITION.Length] + mov dword[edi + XFS.Length], eax + mov eax, dword[ebp + PARTITION.Length + 4] + mov dword[edi + XFS.Length + 4], eax + mov eax, [ebp + PARTITION.Disk] + mov [edi + XFS.Disk], eax + mov [edi + XFS.FSUserFunctions], xfs_user_functions + + ; here we initialize only one mutex so far (for the entire partition) + ; XFS potentially allows parallel r/w access to several AGs, keep it in mind for SMP times + + lea ecx, [edi + XFS.Lock] + call mutex_init + + ; read superblock and fill just allocated XFS partition structure + + mov eax, [ebx + xfs_sb.sb_blocksize] + bswap eax ; XFS is big endian + mov [edi + XFS.blocksize], eax + movzx eax, word[ebx + xfs_sb.sb_sectsize] + xchg al, ah + mov [edi + XFS.sectsize], eax + movzx eax, word[ebx + xfs_sb.sb_versionnum] + xchg al, ah + mov [edi + XFS.versionnum], eax + mov eax, [ebx + xfs_sb.sb_features2] + bswap eax + mov [edi + XFS.features2], eax + movzx eax, word[ebx + xfs_sb.sb_inodesize] + xchg al, ah + mov [edi + XFS.inodesize], eax + movzx eax, word[ebx + xfs_sb.sb_inopblock] ; inodes per block + xchg al, ah + mov [edi + XFS.inopblock], eax + movzx eax, byte[ebx + xfs_sb.sb_blocklog] ; log2 of block size, in bytes + mov [edi + XFS.blocklog], eax + movzx eax, byte[ebx + xfs_sb.sb_sectlog] + mov [edi + XFS.sectlog], eax + movzx eax, byte[ebx + xfs_sb.sb_inodelog] + mov [edi + XFS.inodelog], eax + movzx eax, byte[ebx + xfs_sb.sb_inopblog] + mov [edi + XFS.inopblog], eax + movzx eax, byte[ebx + xfs_sb.sb_dirblklog] + mov [edi + XFS.dirblklog], eax + mov eax, dword[ebx + xfs_sb.sb_rootino + 4] ; + bswap eax ; big + mov dword[edi + XFS.rootino + 0], eax ; endian + mov eax, dword[ebx + xfs_sb.sb_rootino + 0] ; 64bit + bswap eax ; number + mov dword[edi + XFS.rootino + 4], eax ; + + mov eax, [edi + XFS.blocksize] + mov ecx, [edi + XFS.dirblklog] + shl eax, cl + mov [edi + XFS.dirblocksize], eax ; blocks for files, dirblocks for directories + + ; sector is always smaller than block + ; so precalculate shift order to allow faster sector_num->block_num conversion + + mov ecx, [edi + XFS.blocklog] + sub ecx, [edi + XFS.sectlog] + mov [edi + XFS.blockmsectlog], ecx + + mov eax, 1 + shl eax, cl + mov [edi + XFS.sectpblock], eax + + ; shift order for inode_num->block_num conversion + + mov eax, [edi + XFS.blocklog] + sub eax, [edi + XFS.inodelog] + mov [edi + XFS.inodetoblocklog], eax + + mov eax, [ebx + xfs_sb.sb_agblocks] + bswap eax + mov [edi + XFS.agblocks], eax + movzx ecx, byte[ebx + xfs_sb.sb_agblklog] + mov [edi + XFS.agblklog], ecx + + ; get the mask for block numbers + ; block numbers are AG relative! + ; bitfield length may vary between partitions + + mov eax, 1 + shl eax, cl + dec eax + mov dword[edi + XFS.agblockmask + 0], eax + mov eax, 1 + sub ecx, 32 + jc @f + shl eax, cl + @@: + dec eax + mov dword[edi + XFS.agblockmask + 4], eax + + ; calculate magic offsets for directories + + mov ecx, [edi + XFS.blocklog] + mov eax, XFS_DIR2_LEAF_OFFSET AND 0xffffffff ; lo + mov edx, XFS_DIR2_LEAF_OFFSET SHR 32 ; hi + shrd eax, edx, cl + mov [edi + XFS.dir2_leaf_offset_blocks], eax + + mov ecx, [edi + XFS.blocklog] + mov eax, XFS_DIR2_FREE_OFFSET AND 0xffffffff ; lo + mov edx, XFS_DIR2_FREE_OFFSET SHR 32 ; hi + shrd eax, edx, cl + mov [edi + XFS.dir2_free_offset_blocks], eax + +; mov ecx, [edi + XFS.dirblklog] +; mov eax, [edi + XFS.blocksize] +; shl eax, cl +; mov [edi + XFS.dirblocksize], eax + + mov eax, [edi + XFS.blocksize] + call malloc + test eax, eax + jz .error + mov [edi + XFS.cur_block], eax + + ; we do need XFS.blocksize bytes for single inode + ; minimal file system structure is block, inodes are packed in blocks + + mov eax, [edi + XFS.blocksize] + call malloc + test eax, eax + jz .error + mov [edi + XFS.cur_inode], eax + + ; temporary inode + ; used for browsing directories + + mov eax, [edi + XFS.blocksize] + call malloc + test eax, eax + jz .error + mov [edi + XFS.tmp_inode], eax + + ; current sector + ; only for sector size structures like AGI + ; inodes has usually the same size, but never store them here + + mov eax, [edi + XFS.sectsize] + call malloc + test eax, eax + jz .error + mov [edi + XFS.cur_sect], eax + + ; current directory block + + mov eax, [edi + XFS.dirblocksize] + call malloc + test eax, eax + jz .error + mov [edi + XFS.cur_dirblock], eax + + .quit: + mov eax, edi ; return pointer to allocated XFS partition structure + pop edi esi edx ecx ebx + ret + .error: + xor eax, eax + pop edi esi edx ecx ebx + ret + + +iglobal +align 4 +xfs_user_functions: + dd xfs_free + dd (xfs_user_functions_end - xfs_user_functions - 4) / 4 + dd xfs_Read + dd xfs_ReadFolder + dd 0;xfs_Rewrite + dd 0;xfs_Write + dd 0;xfs_SetFileEnd + dd xfs_GetFileInfo + dd 0;xfs_SetFileInfo + dd 0 + dd 0;xfs_Delete + dd 0;xfs_CreateFolder +xfs_user_functions_end: +endg + + +; lock partition access mutex +proc xfs_lock +;DEBUGF 1,"xfs_lock\n" + lea ecx, [ebp + XFS.Lock] + jmp mutex_lock +endp + + +; unlock partition access mutex +proc xfs_unlock +;DEBUGF 1,"xfs_unlock\n" + lea ecx, [ebp + XFS.Lock] + jmp mutex_unlock +endp + + +; free all the allocated memory +; called on partition destroy +proc xfs_free + push ebp + xchg ebp, eax + stdcall kernel_free, [ebp + XFS.cur_block] + stdcall kernel_free, [ebp + XFS.cur_inode] + stdcall kernel_free, [ebp + XFS.cur_sect] + stdcall kernel_free, [ebp + XFS.cur_dirblock] + stdcall kernel_free, [ebp + XFS.tmp_inode] + xchg ebp, eax + call free + pop ebp + ret +endp + + +;--------------------------------------------------------------- +; block number (AG relative) +; eax -- inode_lo +; edx -- inode_hi +; ebx -- buffer +;--------------------------------------------------------------- +xfs_read_block: + push ebx esi + + push edx + push eax + + ; XFS block numbers are AG relative + ; they come in bitfield form of concatenated AG and block numbers + ; to get absolute block number for fs_read32_sys we should + ; 1. extract AG number (using precalculated mask) + ; 2. multiply it by the AG size in blocks + ; 3. add AG relative block number + + ; 1. + mov ecx, [ebp + XFS.agblklog] + shrd eax, edx, cl + shr edx, cl + ; 2. + mul dword[ebp + XFS.agblocks] + pop ecx + pop esi + and ecx, dword[ebp + XFS.agblockmask + 0] + and esi, dword[ebp + XFS.agblockmask + 4] + ; 3. + add eax, ecx + adc edx, esi + +;DEBUGF 1,"read block: 0x%x%x\n",edx,eax + ; there is no way to read file system block at once, therefore we + ; 1. calculate the number of sectors first + ; 2. and then read them in series + + ; 1. + mov ecx, [ebp + XFS.blockmsectlog] + shld edx, eax, cl + shl eax, cl + mov esi, [ebp + XFS.sectpblock] + + ; 2. + .next_sector: + push eax edx + call fs_read32_sys + mov ecx, eax + pop edx eax + test ecx, ecx + jnz .error + add eax, 1 ; be ready to fs_read64_sys + adc edx, 0 + add ebx, [ebp + XFS.sectsize] ; update buffer offset + dec esi + jnz .next_sector + + .quit: + xor eax, eax + pop esi ebx + ret + .error: + mov eax, ecx + pop esi ebx + ret + + +;--------------------------------------------------------------- +; push buffer +; push startblock_hi +; push startblock_lo +; call xfs_read_dirblock +; test eax, eax +;--------------------------------------------------------------- +xfs_read_dirblock: +;mov eax, [esp + 4] +;mov edx, [esp + 8] +;DEBUGF 1,"read dirblock at: %d %d\n",edx,eax +;DEBUGF 1,"dirblklog: %d\n",[ebp + XFS.dirblklog] + push ebx esi + + mov eax, [esp + 12] ; startblock_lo + mov edx, [esp + 16] ; startblock_hi + mov ebx, [esp + 20] ; buffer + + ; dirblock >= block + ; read dirblocks by blocks + + mov ecx, [ebp + XFS.dirblklog] + mov esi, 1 + shl esi, cl + .next_block: + push eax edx + call xfs_read_block + mov ecx, eax + pop edx eax + test ecx, ecx + jnz .error + add eax, 1 ; be ready to fs_read64_sys + adc edx, 0 + add ebx, [ebp + XFS.blocksize] + dec esi + jnz .next_block + + .quit: + xor eax, eax + pop esi ebx + ret 12 + .error: + mov eax, ecx + pop esi ebx + ret 12 + + +;--------------------------------------------------------------- +; push buffer +; push inode_hi +; push inode_lo +; call xfs_read_inode +; test eax, eax +;--------------------------------------------------------------- +xfs_read_inode: +;DEBUGF 1,"reading inode: 0x%x%x\n",[esp+8],[esp+4] + push ebx + mov eax, [esp + 8] ; inode_lo + mov edx, [esp + 12] ; inode_hi + mov ebx, [esp + 16] ; buffer + + ; inodes are packed into blocks + ; 1. calculate block number + ; 2. read the block + ; 3. add inode offset to block base address + + ; 1. + mov ecx, [ebp + XFS.inodetoblocklog] + shrd eax, edx, cl + shr edx, cl + ; 2. + call xfs_read_block + test eax, eax + jnz .error + + ; note that inode numbers should be first extracted from bitfields using mask + + mov eax, [esp + 8] + mov edx, 1 + mov ecx, [ebp + XFS.inopblog] + shl edx, cl + dec edx ; get inode number mask + and eax, edx ; apply mask + mov ecx, [ebp + XFS.inodelog] + shl eax, cl + add ebx, eax + + cmp word[ebx], XFS_DINODE_MAGIC ; test signature + jne .error + .quit: + xor eax, eax + mov edx, ebx + pop ebx + ret 12 + .error: + movi eax, ERROR_FS_FAIL + mov edx, ebx + pop ebx + ret 12 + + +;---------------------------------------------------------------- +; push encoding ; ASCII / UNICODE +; push src ; inode +; push dst ; bdfe +; push entries_to_read +; push start_number ; from 0 +;---------------------------------------------------------------- +xfs_dir_get_bdfes: +DEBUGF 1,"xfs_dir_get_bdfes: %d entries from %d\n",[esp+8],[esp+4] + sub esp, 4 ; local vars + push ecx edx esi edi + + mov ebx, [esp + 36] ; src + mov edx, [esp + 32] ; dst + mov ecx, [esp + 24] ; start_number + + ; define directory ondisk format and jump to corresponding label + + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL + jne .not_shortdir + jmp .shortdir + .not_shortdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_blockdir + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + cmp eax, 1 + jne .not_blockdir + jmp .blockdir + .not_blockdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_leafdir + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + cmp eax, 4 + ja .not_leafdir + jmp .leafdir + .not_leafdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_nodedir + jmp .nodedir + .not_nodedir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE + jne .not_btreedir + jmp .btreedir + .not_btreedir: + movi eax, ERROR_FS_FAIL + jmp .error + + ; short form directory (all the data fits into inode) + .shortdir: +;DEBUGF 1,"shortdir\n", + movzx eax, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count] + test al, al ; is count zero? + jnz @f ; if not, use it (i8count must be zero then) + shr eax, 8 ; use i8count + @@: + add eax, 1 ; '..' and '.' are implicit + mov dword[edx + 0], 1 ; version + mov [edx + 8], eax ; total entries + sub eax, [esp + 24] ; start number + cmp eax, [esp + 28] ; entries to read + jbe @f + mov eax, [esp + 28] + @@: + mov [esp + 28], eax + mov [edx + 4], eax ; number of actually read entries + mov [ebp + XFS.entries_read], eax + + ; inode numbers are often saved as 4 bytes (iff they fit) + ; compute the length of inode numbers + + mov eax, 4 ; 4 by default + cmp byte[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.i8count], 0 + je @f + add eax, eax ; 4+4=8, iff i8count != 0 + @@: + mov dword[edx + 12], 0 ; reserved + mov dword[edx + 16], 0 ; + mov dword[edx + 20], 0 ; + mov dword[edx + 24], 0 ; + mov dword[edx + 28], 0 ; + add edx, 32 + lea esi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax] + dec ecx + js .shortdir.fill + + ; skip some entries if the first entry to read is not 0 + + .shortdir.skip: + test ecx, ecx + jz .shortdir.skipped + movzx edi, byte[esi + xfs_dir2_sf_entry.namelen] + lea esi, [esi + xfs_dir2_sf_entry.name + edi] + add esi, eax + dec ecx + jnz .shortdir.skip + mov ecx, [esp + 28] ; entries to read + jmp .shortdir.skipped + .shortdir.fill: + mov ecx, [esp + 28] ; total number + test ecx, ecx + jz .quit + push ecx +;DEBUGF 1,"ecx: %d\n",ecx + lea edi, [edx + 40] ; get file name offset +;DEBUGF 1,"filename: ..\n" + mov dword[edi], '..' + mov edi, edx + push eax ebx edx esi + stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent] + stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode] +; test eax, eax +; jnz .error + stdcall xfs_get_inode_info, edx, edi + test eax, eax + pop esi edx ebx eax + jnz .error + mov ecx, [esp + 44] ; file name encding + mov [edx + 4], ecx + add edx, 304 ; ASCII only for now + pop ecx + dec ecx + jz .quit + +; push ecx +; lea edi, [edx + 40] +;DEBUGF 1,"filename: .\n" +; mov dword[edi], '.' +; mov edi, edx +; push eax edx +; stdcall xfs_get_inode_info, [ebp + XFS.cur_inode], edi +; test eax, eax +; pop edx eax +; jnz .error +; mov ecx, [esp + 44] +; mov [edx + 4], ecx +; add edx, 304 ; ASCII only for now +; pop ecx +; dec ecx +; jz .quit + + ; we skipped some entries + ; now we fill min(required, present) number of bdfe's + + .shortdir.skipped: +;DEBUGF 1,"ecx: %d\n",ecx + push ecx + movzx ecx, byte[esi + xfs_dir2_sf_entry.namelen] + add esi, xfs_dir2_sf_entry.name + lea edi, [edx + 40] ; bdfe offset of file name +;DEBUGF 1,"filename: |%s|\n",esi + rep movsb + mov word[edi], 0 ; terminator (ASCIIZ) + + push eax ebx ecx edx esi +; push edx ; for xfs_get_inode_info + mov edi, edx + stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [esi + 4], [esi] + stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode] +; test eax, eax +; jnz .error + stdcall xfs_get_inode_info, edx, edi + test eax, eax + pop esi edx ecx ebx eax + jnz .error + mov ecx, [esp + 44] ; file name encoding + mov [edx + 4], ecx + + add edx, 304 ; ASCII only for now + add esi, eax + pop ecx + dec ecx + jnz .shortdir.skipped + jmp .quit + + .blockdir: +;DEBUGF 1,"blockdir\n" + push edx + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + stdcall xfs_extent_unpack, eax +;DEBUGF 1,"extent.br_startoff : 0x%x%x\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0] +;DEBUGF 1,"extent.br_startblock: 0x%x%x\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0] +;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount] +;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state] + stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock] + test eax, eax + pop edx + jnz .error +;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock] + mov ebx, [ebp + XFS.cur_dirblock] + mov dword[edx + 0], 1 ; version + mov eax, [ebp + XFS.dirblocksize] + mov ecx, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.stale] + mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count] + bswap ecx + bswap eax + sub eax, ecx ; actual number of entries = count - stale + mov [edx + 8], eax ; total entries +;DEBUGF 1,"total entries: %d\n",eax + sub eax, [esp + 24] ; start number + cmp eax, [esp + 28] ; entries to read + jbe @f + mov eax, [esp + 28] + @@: + mov [esp + 28], eax + mov [edx + 4], eax ; number of actually read entries + mov [ebp + XFS.entries_read], eax +;DEBUGF 1,"actually read entries: %d\n",eax + mov dword[edx + 12], 0 ; reserved + mov dword[edx + 16], 0 ; + mov dword[edx + 20], 0 ; + mov dword[edx + 24], 0 ; + mov dword[edx + 28], 0 ; + add ebx, xfs_dir2_block.u + + mov ecx, [esp + 24] ; start entry number + ; also means how many to skip + test ecx, ecx + jz .blockdir.skipped + .blockdir.skip: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .blockdir.skip + @@: + movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen] + lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 bytes for 'tag' + add ebx, 7 ; align on 8 bytes + and ebx, not 7 + dec ecx + jnz .blockdir.skip + .blockdir.skipped: + mov ecx, [edx + 4] ; actually read entries + test ecx, ecx + jz .quit + add edx, 32 ; set edx to the first bdfe + .blockdir.next_entry: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_NULL + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .blockdir.next_entry + @@: + push ecx + push eax ebx ecx edx esi + mov edi, edx + mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0] + mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4] + bswap edx + bswap eax + stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode] + stdcall xfs_get_inode_info, edx, edi + test eax, eax + pop esi edx ecx ebx eax + jnz .error + mov ecx, [esp + 44] + mov [edx + 4], ecx + lea edi, [edx + 40] + movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen] + lea esi, [ebx + xfs_dir2_data_union.xentry.name] +;DEBUGF 1,"filename: |%s|\n",esi + rep movsb +; call utf8_to_cp866 + mov word[edi], 0 ; terminator + lea ebx, [esi + 2] ; skip 'tag' + add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes + and ebx, not 7 + add edx, 304 + pop ecx + dec ecx + jnz .blockdir.next_entry + jmp .quit + + .leafdir: +;DEBUGF 1,"readdir: leaf\n" + mov [ebp + XFS.cur_inode_save], ebx + push ebx ecx edx + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, 0xffffffff, 0xffffffff + mov ecx, eax + and ecx, edx + inc ecx + pop edx ecx ebx + jz .error + + mov eax, [ebp + XFS.cur_dirblock] + movzx ecx, word[eax + xfs_dir2_leaf.hdr.stale] + movzx eax, word[eax + xfs_dir2_leaf.hdr.count] + xchg cl, ch + xchg al, ah + sub eax, ecx +;DEBUGF 1,"total count: %d\n",eax + + mov dword[edx + 0], 1 ; version + mov [edx + 8], eax ; total entries + sub eax, [esp + 24] ; start number + cmp eax, [esp + 28] ; entries to read + jbe @f + mov eax, [esp + 28] + @@: + mov [esp + 28], eax + mov [edx + 4], eax ; number of actually read entries + + mov dword[edx + 12], 0 ; reserved + mov dword[edx + 16], 0 ; + mov dword[edx + 20], 0 ; + mov dword[edx + 24], 0 ; + mov dword[edx + 28], 0 ; + + mov eax, [ebp + XFS.cur_dirblock] + add eax, [ebp + XFS.dirblocksize] + mov [ebp + XFS.max_dirblockaddr], eax + mov dword[ebp + XFS.next_block_num + 0], 0 + mov dword[ebp + XFS.next_block_num + 4], 0 + + mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately + mov ecx, [esp + 24] ; start number + test ecx, ecx + jz .leafdir.skipped + .leafdir.skip: + cmp ebx, [ebp + XFS.max_dirblockaddr] + jne @f + push ecx edx + mov ebx, [ebp + XFS.cur_inode_save] + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 + mov ecx, eax + and ecx, edx + inc ecx + jz .error + add eax, 1 + adc edx, 0 + mov dword[ebp + XFS.next_block_num + 0], eax + mov dword[ebp + XFS.next_block_num + 4], edx + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, sizeof.xfs_dir2_data_hdr + pop edx ecx + @@: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .leafdir.skip + @@: + movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen] + lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag' + add ebx, 7 + and ebx, not 7 + dec ecx + jnz .leafdir.skip + .leafdir.skipped: + mov [ebp + XFS.entries_read], 0 + mov ecx, [edx + 4] ; actually read entries + test ecx, ecx + jz .quit + add edx, 32 ; first bdfe entry + .leafdir.next_entry: +;DEBUGF 1,"next_extry\n" + cmp ebx, [ebp + XFS.max_dirblockaddr] + jne .leafdir.process_current_block + push ecx edx + mov ebx, [ebp + XFS.cur_inode_save] + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + mov ecx, eax + and ecx, edx + inc ecx + jnz @f + pop edx ecx + jmp .quit + @@: + add eax, 1 + adc edx, 0 + mov dword[ebp + XFS.next_block_num + 0], eax + mov dword[ebp + XFS.next_block_num + 4], edx + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, sizeof.xfs_dir2_data_hdr + pop edx ecx + .leafdir.process_current_block: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .leafdir.next_entry + @@: + push eax ebx ecx edx esi + mov edi, edx + mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0] + mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4] + bswap edx + bswap eax + stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode] + stdcall xfs_get_inode_info, edx, edi + test eax, eax + pop esi edx ecx ebx eax + jnz .error + push ecx + mov ecx, [esp + 44] + mov [edx + 4], ecx + lea edi, [edx + 40] + movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen] + lea esi, [ebx + xfs_dir2_data_union.xentry.name] +;DEBUGF 1,"filename: |%s|\n",esi + rep movsb + pop ecx + mov word[edi], 0 + lea ebx, [esi + 2] ; skip 'tag' + add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes + and ebx, not 7 + add edx, 304 ; ASCII only for now + inc [ebp + XFS.entries_read] + dec ecx + jnz .leafdir.next_entry + jmp .quit + + .nodedir: +;DEBUGF 1,"readdir: node\n" + push edx + mov [ebp + XFS.cur_inode_save], ebx + mov [ebp + XFS.entries_read], 0 + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks] + pop edx + test eax, eax + jnz .error + mov eax, [ebp + XFS.entries_read] + mov [ebp + XFS.entries_read], 0 +;DEBUGF 1,"numfiles: %d\n",eax + mov dword[edx + 0], 1 ; version + mov [edx + 8], eax ; total entries + sub eax, [esp + 24] ; start number + cmp eax, [esp + 28] ; entries to read + jbe @f + mov eax, [esp + 28] + @@: + mov [esp + 28], eax + mov [edx + 4], eax ; number of actually read entries + + mov dword[edx + 12], 0 ; reserved + mov dword[edx + 16], 0 ; + mov dword[edx + 20], 0 ; + mov dword[edx + 24], 0 ; + mov dword[edx + 28], 0 ; + + mov eax, [ebp + XFS.cur_dirblock] + add eax, [ebp + XFS.dirblocksize] + mov [ebp + XFS.max_dirblockaddr], eax + mov dword[ebp + XFS.next_block_num + 0], 0 + mov dword[ebp + XFS.next_block_num + 4], 0 + + mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately + mov ecx, [esp + 24] ; start number + test ecx, ecx + jz .leafdir.skipped + jmp .leafdir.skip + + .btreedir: +;DEBUGF 1,"readdir: btree\n" + mov [ebp + XFS.cur_inode_save], ebx + push ebx edx + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + mov [ebp + XFS.ro_nextents], eax + mov eax, [ebp + XFS.inodesize] + sub eax, xfs_inode.di_u + sub eax, sizeof.xfs_bmdr_block + shr eax, 4 +;DEBUGF 1,"maxnumresc: %d\n",eax + mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0] + mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4] + bswap eax + bswap edx + mov ebx, [ebp + XFS.cur_block] +;DEBUGF 1,"read_block: %x %x ",edx,eax + stdcall xfs_read_block + pop edx ebx + test eax, eax + jnz .error +;DEBUGF 1,"ok\n" + + mov ebx, [ebp + XFS.cur_block] + push edx + mov [ebp + XFS.entries_read], 0 + lea eax, [ebx + sizeof.xfs_bmbt_block] + mov edx, [ebp + XFS.ro_nextents] + stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks] + pop edx + test eax, eax + jnz .error + mov eax, [ebp + XFS.entries_read] + mov [ebp + XFS.entries_read], 0 +;DEBUGF 1,"numfiles: %d\n",eax + + mov dword[edx + 0], 1 ; version + mov [edx + 8], eax ; total entries + sub eax, [esp + 24] ; start number + cmp eax, [esp + 28] ; entries to read + jbe @f + mov eax, [esp + 28] + @@: + mov [esp + 28], eax + mov [edx + 4], eax ; number of actually read entries + + mov dword[edx + 12], 0 + mov dword[edx + 16], 0 + mov dword[edx + 20], 0 + mov dword[edx + 24], 0 + mov dword[edx + 28], 0 + + mov eax, [ebp + XFS.cur_dirblock] ; fsblock? + add eax, [ebp + XFS.dirblocksize] + mov [ebp + XFS.max_dirblockaddr], eax + mov dword[ebp + XFS.next_block_num + 0], 0 + mov dword[ebp + XFS.next_block_num + 4], 0 + + mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately + mov ecx, [esp + 24] ; start number + test ecx, ecx + jz .btreedir.skipped +; jmp .btreedir.skip + .btreedir.skip: + cmp ebx, [ebp + XFS.max_dirblockaddr] + jne @f + push ecx edx + mov ebx, [ebp + XFS.cur_block] + lea eax, [ebx + sizeof.xfs_bmbt_block] + mov edx, [ebp + XFS.ro_nextents] + stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + mov ecx, eax + and ecx, edx + inc ecx + jz .error + add eax, 1 + adc edx, 0 + mov dword[ebp + XFS.next_block_num + 0], eax + mov dword[ebp + XFS.next_block_num + 4], edx + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, sizeof.xfs_dir2_data_hdr + pop edx ecx + @@: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .btreedir.skip + @@: + movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen] + lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag' + add ebx, 7 + and ebx, not 7 + dec ecx + jnz .btreedir.skip + .btreedir.skipped: + mov [ebp + XFS.entries_read], 0 + mov ecx, [edx + 4] ; actually read entries + test ecx, ecx + jz .quit + add edx, 32 + .btreedir.next_entry: +;mov eax, [ebp + XFS.entries_read] +;DEBUGF 1,"next_extry: %d\n",eax + cmp ebx, [ebp + XFS.max_dirblockaddr] + jne .btreedir.process_current_block + push ecx edx + mov ebx, [ebp + XFS.cur_block] + lea eax, [ebx + sizeof.xfs_bmbt_block] + mov edx, [ebp + XFS.ro_nextents] + stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + mov ecx, eax + and ecx, edx + inc ecx + jnz @f + pop edx ecx + jmp .quit + @@: + add eax, 1 + adc edx, 0 + mov dword[ebp + XFS.next_block_num + 0], eax + mov dword[ebp + XFS.next_block_num + 4], edx + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, sizeof.xfs_dir2_data_hdr + pop edx ecx + .btreedir.process_current_block: + cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG + jne @f + movzx eax, word[ebx + xfs_dir2_data_union.unused.length] + xchg al, ah + add ebx, eax + jmp .btreedir.next_entry + @@: + push eax ebx ecx edx esi + mov edi, edx + mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0] + mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4] + bswap edx + bswap eax + stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode] + stdcall xfs_get_inode_info, edx, edi + test eax, eax + pop esi edx ecx ebx eax + jnz .error + push ecx + mov ecx, [esp + 44] + mov [edx + 4], ecx + lea edi, [edx + 40] + movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen] + lea esi, [ebx + xfs_dir2_data_union.xentry.name] +;DEBUGF 1,"filename: |%s|\n",esi + rep movsb + pop ecx + mov word[edi], 0 + lea ebx, [esi + 2] ; skip 'tag' + add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes + and ebx, not 7 + add edx, 304 + inc [ebp + XFS.entries_read] + dec ecx + jnz .btreedir.next_entry + jmp .quit + + + .quit: + pop edi esi edx ecx + add esp, 4 ; pop vars + xor eax, eax +; mov ebx, [esp + 8] + mov ebx, [ebp + XFS.entries_read] +DEBUGF 1,"xfs_dir_get_bdfes done: %d\n",ebx + ret 20 + .error: + pop edi esi edx ecx + add esp, 4 ; pop vars + mov eax, ERROR_FS_FAIL + movi ebx, -1 + ret 20 + + +;---------------------------------------------------------------- +; push inode_hi +; push inode_lo +; push name +;---------------------------------------------------------------- +xfs_get_inode_short: + ; this function searches for the file in _current_ dir + ; it is called recursively for all the subdirs /path/to/my/file + +;DEBUGF 1,"xfs_get_inode_short: %s\n",[esp+4] + mov esi, [esp + 4] ; name + movzx eax, word[esi] + cmp eax, '.' ; current dir; it is already read, just return + je .quit + cmp eax, './' ; same thing + je .quit + + ; read inode + + mov eax, [esp + 8] ; inode_lo + mov edx, [esp + 12] ; inode_hi + stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode] + test eax, eax + movi eax, ERROR_FS_FAIL + jnz .error + + ; find file name in directory + ; switch directory ondisk format + + mov ebx, edx + mov [ebp + XFS.cur_inode_save], ebx + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL + jne .not_shortdir +;DEBUGF 1,"dir: shortdir\n" + jmp .shortdir + .not_shortdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_blockdir + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + cmp eax, 1 + jne .not_blockdir + jmp .blockdir + .not_blockdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_leafdir + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + cmp eax, 4 + ja .not_leafdir + jmp .leafdir + .not_leafdir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_nodedir + jmp .nodedir + .not_nodedir: + cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE + jne .not_btreedir + jmp .btreedir + .not_btreedir: +DEBUGF 1,"NOT IMPLEMENTED: DIR FORMAT\n" + jmp .error + + .shortdir: + .shortdir.check_parent: + ; parent inode number in shortform directories is always implicit, check this case + mov eax, [esi] + and eax, 0x00ffffff + cmp eax, '..' + je .shortdir.parent2 + cmp eax, '../' + je .shortdir.parent3 + jmp .shortdir.common + .shortdir.parent3: + inc esi + .shortdir.parent2: + add esi, 2 + add ebx, xfs_inode.di_u + stdcall xfs_get_inode_number_sf, dword[ebx + xfs_dir2_sf_hdr.count], dword[ebx + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_dir2_sf_hdr.parent] +;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + ; not a parent inode? + ; search in the list, all the other files are stored uniformly + + .shortdir.common: + mov eax, 4 + movzx edx, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count] ; read count (byte) and i8count (byte) at once + test dl, dl ; is count zero? + jnz @f + shr edx, 8 ; use i8count + add eax, eax ; inode_num size + @@: + lea edi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax] + + .next_name: + movzx ecx, byte[edi + xfs_dir2_sf_entry.namelen] + add edi, xfs_dir2_sf_entry.name + mov esi, [esp + 4] +;DEBUGF 1,"esi: %s\n",esi +;DEBUGF 1,"edi: %s\n",edi + repe cmpsb + jne @f + cmp byte[esi], 0 ; HINT: use adc here? + je .found + cmp byte[esi], '/' + je .found_inc + @@: + add edi, ecx + add edi, eax + dec edx + jnz .next_name + movi eax, ERROR_FILE_NOT_FOUND + jmp .error + .found_inc: ; increment esi to skip '/' symbol + ; this means esi always points to valid file name or zero terminator byte + inc esi + .found: + stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [edi + 4], [edi] +;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + .blockdir: + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + stdcall xfs_extent_unpack, eax + stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock] + test eax, eax + jnz .error +;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock] + mov ebx, [ebp + XFS.cur_dirblock] + mov eax, [ebp + XFS.dirblocksize] + mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count] + ; note that we don't subtract xfs_dir2_block_tail.stale here, + ; since we need the number of leaf entries rather than file number + bswap eax + add ebx, [ebp + XFS.dirblocksize] +; mov ecx, sizeof.xfs_dir2_leaf_entry + imul ecx, eax, sizeof.xfs_dir2_leaf_entry + sub ebx, sizeof.xfs_dir2_block_tail + sub ebx, ecx + shr ecx, 3 + push ecx ; for xfs_get_inode_by_hash + push ebx ; for xfs_get_inode_by_hash + + mov edi, esi + xor eax, eax + mov ecx, 4096 ; MAX_PATH_LEN + repne scasb + movi eax, ERROR_FS_FAIL + jne .error + neg ecx + add ecx, 4096 ; MAX_PATH_LEN + dec ecx + mov edx, ecx +;DEBUGF 1,"strlen total : %d\n",edx + mov edi, esi + mov eax, '/' + mov ecx, edx + repne scasb + jne @f + inc ecx + @@: + neg ecx + add ecx, edx +;DEBUGF 1,"strlen current: %d\n",ecx + stdcall xfs_hashname, esi, ecx + add esi, ecx + cmp byte[esi], '/' + jne @f + inc esi + @@: +;DEBUGF 1,"hashed: 0x%x\n",eax +; bswap eax + stdcall xfs_get_addr_by_hash + bswap eax +;DEBUGF 1,"got address: 0x%x\n",eax + cmp eax, -1 + jne @f + movi eax, ERROR_FILE_NOT_FOUND + mov ebx, -1 + jmp .error + @@: + shl eax, 3 + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, eax + mov edx, [ebx + 0] + mov eax, [ebx + 4] + bswap edx + bswap eax +;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + .leafdir: +;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock] + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, -1, -1 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + mov ecx, eax + and ecx, edx + inc ecx + jz .error + + mov ebx, [ebp + XFS.cur_dirblock] + movzx eax, [ebx + xfs_dir2_leaf.hdr.count] + ; note that we don't subtract xfs_dir2_leaf.hdr.stale here, + ; since we need the number of leaf entries rather than file number + xchg al, ah + add ebx, xfs_dir2_leaf.ents +; imul ecx, eax, sizeof.xfs_dir2_leaf_entry +; shr ecx, 3 + push eax ; for xfs_get_addr_by_hash: len + push ebx ; for xfs_get_addr_by_hash: base + + mov edi, esi + xor eax, eax + mov ecx, 4096 ; MAX_PATH_LEN + repne scasb + movi eax, ERROR_FS_FAIL + jne .error + neg ecx + add ecx, 4096 + dec ecx + mov edx, ecx +;DEBUGF 1,"strlen total : %d\n",edx + mov edi, esi + mov eax, '/' + mov ecx, edx + repne scasb + jne @f + inc ecx + @@: + neg ecx + add ecx, edx +;DEBUGF 1,"strlen current: %d\n",ecx + stdcall xfs_hashname, esi, ecx + add esi, ecx + cmp byte[esi], '/' + jne @f + inc esi + @@: +;DEBUGF 1,"hashed: 0x%x\n",eax + stdcall xfs_get_addr_by_hash + bswap eax +;DEBUGF 1,"got address: 0x%x\n",eax + cmp eax, -1 + jne @f + movi eax, ERROR_FILE_NOT_FOUND + mov ebx, -1 + jmp .error + @@: + + mov ebx, [ebp + XFS.cur_inode_save] + push esi edi + xor edi, edi + mov esi, eax + shld edi, esi, 3 ; get offset + shl esi, 3 ; 2^3 = 8 byte align + mov edx, esi + mov ecx, [ebp + XFS.dirblklog] + add ecx, [ebp + XFS.blocklog] + mov eax, 1 + shl eax, cl + dec eax + and edx, eax + push edx + shrd esi, edi, cl + shr edi, cl + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + pop edx + pop edi esi + mov ecx, eax + and ecx, edx + inc ecx + jz .error + + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, edx + mov edx, [ebx + 0] + mov eax, [ebx + 4] + bswap edx + bswap eax +;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + .nodedir: +;DEBUGF 1,"lookupdir: node\n" + mov [ebp + XFS.cur_inode_save], ebx + + mov edi, esi + xor eax, eax + mov ecx, 4096 ; MAX_PATH_LEN + repne scasb + movi eax, ERROR_FS_FAIL + jne .error + neg ecx + add ecx, 4096 ; MAX_PATH_LEN + dec ecx + mov edx, ecx +;DEBUGF 1,"strlen total : %d\n",edx + mov edi, esi + mov eax, '/' + mov ecx, edx + repne scasb + jne @f + inc ecx + @@: + neg ecx + add ecx, edx +;DEBUGF 1,"strlen current: %d\n",ecx + stdcall xfs_hashname, esi, ecx + add esi, ecx + cmp byte[esi], '/' + jne @f + inc esi + @@: +;DEBUGF 1,"hashed: 0x%x\n",eax + push edi edx + mov edi, eax + mov [ebp + XFS.entries_read], 0 + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi + pop edx edi + test eax, eax + jnz .error + bswap ecx +;DEBUGF 1,"got address: 0x%x\n",ecx + + mov ebx, [ebp + XFS.cur_inode_save] + push esi edi + xor edi, edi + mov esi, ecx + shld edi, esi, 3 ; get offset + shl esi, 3 ; 8 byte align + mov edx, esi + mov ecx, [ebp + XFS.dirblklog] + add ecx, [ebp + XFS.blocklog] + mov eax, 1 + shl eax, cl + dec eax + and edx, eax + push edx + shrd esi, edi, cl + shr edi, cl + lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0] + mov edx, [ebx + xfs_inode.di_core.di_nextents] + bswap edx + stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + pop edx + pop edi esi + mov ecx, eax + and ecx, edx + inc ecx + jz .error + + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, edx + mov edx, [ebx + 0] + mov eax, [ebx + 4] + bswap edx + bswap eax +;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + .btreedir: +DEBUGF 1,"lookupdir: btree\n" + mov [ebp + XFS.cur_inode_save], ebx + + push ebx edx + mov eax, [ebx + xfs_inode.di_core.di_nextents] + bswap eax + mov [ebp + XFS.ro_nextents], eax + mov eax, [ebp + XFS.inodesize] + sub eax, xfs_inode.di_u + sub eax, sizeof.xfs_bmdr_block + shr eax, 4 ; FIXME forkoff +;DEBUGF 1,"maxnumresc: %d\n",eax + mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0] + mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4] + bswap eax + bswap edx + mov ebx, [ebp + XFS.cur_block] +;DEBUGF 1,"read_block: %x %x ",edx,eax + stdcall xfs_read_block + pop edx ebx + test eax, eax + jnz .error +;DEBUGF 1,"ok\n" + mov ebx, [ebp + XFS.cur_block] + + mov edi, esi + xor eax, eax + mov ecx, 4096 ; MAX_PATH_LEN + repne scasb + movi eax, ERROR_FS_FAIL + jne .error + neg ecx + add ecx, 4096 + dec ecx + mov edx, ecx +DEBUGF 1,"strlen total : %d\n",edx + mov edi, esi + mov eax, '/' + mov ecx, edx + repne scasb + jne @f + inc ecx + @@: + neg ecx + add ecx, edx +DEBUGF 1,"strlen current: %d\n",ecx + stdcall xfs_hashname, esi, ecx + add esi, ecx + cmp byte[esi], '/' + jne @f + inc esi + @@: +DEBUGF 1,"hashed: 0x%x\n",eax + push edi edx + mov edi, eax + mov [ebp + XFS.entries_read], 0 + lea eax, [ebx + sizeof.xfs_bmbt_block] + mov edx, [ebp + XFS.ro_nextents] +;push eax +;mov eax, [ebp + XFS.dir2_leaf_offset_blocks] +;DEBUGF 1,": 0x%x %d\n",eax,eax +;pop eax + stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi + pop edx edi + test eax, eax + jnz .error + bswap ecx +DEBUGF 1,"got address: 0x%x\n",ecx + + mov ebx, [ebp + XFS.cur_block] + push esi edi + xor edi, edi + mov esi, ecx + shld edi, esi, 3 ; get offset + shl esi, 3 + mov edx, esi + mov ecx, [ebp + XFS.dirblklog] + add ecx, [ebp + XFS.blocklog] + mov eax, 1 + shl eax, cl + dec eax + and edx, eax + push edx + shrd esi, edi, cl + shr edi, cl + lea eax, [ebx + sizeof.xfs_bmbt_block] + mov edx, [ebp + XFS.ro_nextents] + stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0 +;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax + pop edx + pop edi esi + mov ecx, eax + and ecx, edx + inc ecx + jz .error + + mov ebx, [ebp + XFS.cur_dirblock] + add ebx, edx + mov edx, [ebx + 0] + mov eax, [ebx + 4] + bswap edx + bswap eax +DEBUGF 1,"found inode: 0x%x%x\n",edx,eax + jmp .quit + + .quit: + ret 12 + .error: + xor eax, eax + mov edx, eax + ret 12 + + +;---------------------------------------------------------------- +; push name +; call xfs_get_inode +; test eax, eax +;---------------------------------------------------------------- +xfs_get_inode: + ; call xfs_get_inode_short until file is found / error returned + +;DEBUGF 1,"getting inode of: %s\n",[esp+4] + push ebx esi edi + + ; start from the root inode + + mov edx, dword[ebp + XFS.rootino + 4] ; hi + mov eax, dword[ebp + XFS.rootino + 0] ; lo + mov esi, [esp + 16] ; name + + .next_dir: + cmp byte[esi], 0 + je .found + +;DEBUGF 1,"next_level: |%s|\n",esi + stdcall xfs_get_inode_short, esi, eax, edx + test edx, edx + jnz @f + test eax, eax + jz .error + @@: + jmp .next_dir ; file name found, go to next directory level + + .found: + + .quit: + pop edi esi ebx + ret 4 + .error: + pop edi esi ebx + xor eax, eax + mov edx, eax + ret 4 + + +;---------------------------------------------------------------- +; xfs_ReadFolder - XFS implementation of reading a folder +; in: ebp = pointer to XFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +xfs_ReadFolder: + + ; to read folder + ; 1. lock partition + ; 2. find inode number + ; 3. read this inode + ; 4. get bdfe's + ; 5. unlock partition + + ; 1. + call xfs_lock + push ecx edx esi edi + + ; 2. + push ebx esi edi + add esi, [esp + 32] ; directory name +;DEBUGF 1,"xfs_ReadFolder: |%s|\n",esi + stdcall xfs_get_inode, esi + pop edi esi ebx + mov ecx, edx + or ecx, eax + jnz @f + movi eax, ERROR_FILE_NOT_FOUND + @@: + + ; 3. + stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode] + test eax, eax + movi eax, ERROR_FS_FAIL + jnz .error + + ; 4. + mov eax, [ebx + 8] ; encoding + and eax, 1 + stdcall xfs_dir_get_bdfes, [ebx + 4], [ebx + 12], [ebx + 16], edx, eax + test eax, eax + jnz .error + + .quit: +;DEBUGF 1,"\n\n" + pop edi esi edx ecx + ; 5. + call xfs_unlock + xor eax, eax + ret + .error: +;DEBUGF 1,"\n\n" + pop edi esi edx ecx + push eax + call xfs_unlock + pop eax + ret + + +;---------------------------------------------------------------- +; push inode_num_hi +; push inode_num_lo +; push [count] +; call xfs_get_inode_number_sf +;---------------------------------------------------------------- +xfs_get_inode_number_sf: + + ; inode numbers in short form directories may be 4 or 8 bytes long + ; determine the length in run time and read inode number at given address + + cmp byte[esp + 4 + xfs_dir2_sf_hdr.i8count], 0 ; i8count == 0 means 4 byte per inode number + je .i4bytes + .i8bytes: + mov edx, [esp + 12] ; hi + mov eax, [esp + 8] ; lo + bswap edx ; big endian + bswap eax + ret 12 + .i4bytes: + xor edx, edx ; no hi + mov eax, [esp + 12] ; hi = lo + bswap eax ; big endian + ret 12 + + +;---------------------------------------------------------------- +; push dest +; push src +; call xfs_get_inode_info +;---------------------------------------------------------------- +xfs_get_inode_info: + + ; get access time and other file properties + ; useful for browsing directories + ; called for each dir entry + +;DEBUGF 1,"get_inode_info\n" + xor eax, eax + mov edx, [esp + 4] + movzx ecx, word[edx + xfs_inode.di_core.di_mode] + xchg cl, ch +;DEBUGF 1,"di_mode: %x\n",ecx + test ecx, S_IFDIR ; directory? + jz @f + mov eax, 0x10 ; set directory flag + @@: + + mov edi, [esp + 8] + mov [edi + 0], eax + mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi + bswap eax + mov dword[edi + 36], eax ; file size hi +;DEBUGF 1,"file_size hi: %d\n",eax + mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo + bswap eax + mov dword[edi + 32], eax ; file size lo +;DEBUGF 1,"file_size lo: %d\n",eax + + add edi, 8 + mov eax, [edx + xfs_inode.di_core.di_ctime.t_sec] + bswap eax + push edx + xor edx, edx + add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + pop edx + + mov eax, [edx + xfs_inode.di_core.di_atime.t_sec] + bswap eax + push edx + xor edx, edx + add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + pop edx + + mov eax, [edx + xfs_inode.di_core.di_mtime.t_sec] + bswap eax + push edx + xor edx, edx + add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + pop edx + + .quit: + xor eax, eax + ret 8 + .error: + movi eax, ERROR_FS_FAIL + ret 8 + + +;---------------------------------------------------------------- +; push extent_data +; call xfs_extent_unpack +;---------------------------------------------------------------- +xfs_extent_unpack: + + ; extents come as packet 128bit bitfields + ; lets unpack them to access internal fields + ; write result to the XFS.extent structure + + push eax ebx ecx edx + mov ebx, [esp + 20] + + xor eax, eax + mov edx, [ebx + 0] + bswap edx + test edx, 0x80000000 ; mask, see documentation + setnz al + mov [ebp + XFS.extent.br_state], eax + + and edx, 0x7fffffff ; mask + mov eax, [ebx + 4] + bswap eax + shrd eax, edx, 9 + shr edx, 9 + mov dword[ebp + XFS.extent.br_startoff + 0], eax + mov dword[ebp + XFS.extent.br_startoff + 4], edx + + mov edx, [ebx + 4] + mov eax, [ebx + 8] + mov ecx, [ebx + 12] + bswap edx + bswap eax + bswap ecx + and edx, 0x000001ff ; mask + shrd ecx, eax, 21 + shrd eax, edx, 21 + mov dword[ebp + XFS.extent.br_startblock + 0], ecx + mov dword[ebp + XFS.extent.br_startblock + 4], eax + + mov eax, [ebx + 12] + bswap eax + and eax, 0x001fffff ; mask + mov [ebp + XFS.extent.br_blockcount], eax + + pop edx ecx ebx eax +;DEBUGF 1,"extent.br_startoff : %d %d\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0] +;DEBUGF 1,"extent.br_startblock: %d %d\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0] +;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount] +;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state] + ret 4 + + +;---------------------------------------------------------------- +; push namelen +; push name +; call xfs_hashname +;---------------------------------------------------------------- +xfs_hashname: ; xfs_da_hashname + + ; simple hash function + ; never fails) + + push ecx esi + xor eax, eax + mov esi, [esp + 12] ; name + mov ecx, [esp + 16] ; namelen +;mov esi, '.' +;mov ecx, 1 +;DEBUGF 1,"hashname: %d %s\n",ecx,esi + + @@: + rol eax, 7 + xor al, [esi] + add esi, 1 + loop @b + + pop esi ecx + ret 8 + + +;---------------------------------------------------------------- +; push len +; push base +; eax -- hash value +; call xfs_get_addr_by_hash +;---------------------------------------------------------------- +xfs_get_addr_by_hash: + + ; look for the directory entry offset by its file name hash + ; allows fast file search for block, leaf and node directories + ; binary (ternary) search + +;DEBUGF 1,"get_addr_by_hash\n" + push ebx esi + mov ebx, [esp + 12] ; left + mov edx, [esp + 16] ; len + .next: + mov ecx, edx +; jecxz .error + test ecx, ecx + jz .error + shr ecx, 1 + mov esi, [ebx + ecx*8 + xfs_dir2_leaf_entry.hashval] + bswap esi +;DEBUGF 1,"cmp 0x%x",esi + cmp eax, esi + jb .below + ja .above + mov eax, [ebx + ecx*8 + xfs_dir2_leaf_entry.address] + pop esi ebx + ret 8 + .below: +;DEBUGF 1,"b\n" + mov edx, ecx + jmp .next + .above: +;DEBUGF 1,"a\n" + lea ebx, [ebx + ecx*8 + 8] + sub edx, ecx + dec edx + jmp .next + .error: + mov eax, -1 + pop esi ebx + ret 8 + + +;---------------------------------------------------------------- +; xfs_GetFileInfo - XFS implementation of getting file info +; in: ebp = pointer to XFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +xfs_GetFileInfo: + + ; lock partition + ; get inode number by file name + ; read inode + ; get info + ; unlock partition + + push ecx edx esi edi + call xfs_lock + + add esi, [esp + 20] ; name +;DEBUGF 1,"xfs_GetFileInfo: |%s|\n",esi + stdcall xfs_get_inode, esi + mov ecx, edx + or ecx, eax + jnz @f + movi eax, ERROR_FILE_NOT_FOUND + jmp .error + @@: + stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode] + test eax, eax + movi eax, ERROR_FS_FAIL + jnz .error + + stdcall xfs_get_inode_info, edx, [ebx + 16] + + .quit: + call xfs_unlock + pop edi esi edx ecx + xor eax, eax +;DEBUGF 1,"quit\n\n" + ret + .error: + call xfs_unlock + pop edi esi edx ecx +;DEBUGF 1,"error\n\n" + ret + + +;---------------------------------------------------------------- +; xfs_Read - XFS implementation of reading a file +; in: ebp = pointer to XFS structure +; in: esi+[esp+4] = name +; in: ebx = pointer to parameters from sysfunc 70 +; out: eax, ebx = return values for sysfunc 70 +;---------------------------------------------------------------- +xfs_Read: + push ebx ecx edx esi edi + call xfs_lock + + add esi, [esp + 24] +;DEBUGF 1,"xfs_Read: %d %d |%s|\n",[ebx+4],[ebx+12],esi + stdcall xfs_get_inode, esi + mov ecx, edx + or ecx, eax + jnz @f + movi eax, ERROR_FILE_NOT_FOUND + jmp .error + @@: + stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode] + test eax, eax + movi eax, ERROR_FS_FAIL + jnz .error + mov [ebp + XFS.cur_inode_save], edx + + cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS + jne .not_extent_list + jmp .extent_list + .not_extent_list: + cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE + jne .not_btree + jmp .btree + .not_btree: +DEBUGF 1,"XFS: NOT IMPLEMENTED: FILE FORMAT\n" + movi eax, ERROR_FS_FAIL + jmp .error + .extent_list: + mov ecx, [ebx + 12] ; bytes to read + mov edi, [ebx + 16] ; buffer for data + mov esi, [ebx + 8] ; offset_hi + mov ebx, [ebx + 4] ; offset_lo + + mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo + bswap eax + mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo + mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi + bswap eax + mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi + + mov eax, [edx + xfs_inode.di_core.di_nextents] + bswap eax + mov [ebp + XFS.left_extents], eax + + mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes + + xor eax, eax ; extent offset in list + .extent_list.next_extent: +;DEBUGF 1,"extent_list.next_extent, eax: 0x%x\n",eax +;DEBUGF 1,"bytes_to_read: %d\n",ecx +;DEBUGF 1,"cur file offset: %d %d\n",esi,ebx +;DEBUGF 1,"esp: 0x%x\n",esp + cmp [ebp + XFS.left_extents], 0 + jne @f + test ecx, ecx + jz .quit + movi eax, ERROR_END_OF_FILE + jmp .error + @@: + push eax + lea eax, [edx + xfs_inode.di_u + eax + xfs_bmbt_rec.l0] + stdcall xfs_extent_unpack, eax + pop eax + dec [ebp + XFS.left_extents] + add eax, sizeof.xfs_bmbt_rec + push eax ebx ecx edx esi + mov ecx, [ebp + XFS.blocklog] + shrd ebx, esi, cl + shr esi, cl + cmp esi, dword[ebp + XFS.extent.br_startoff + 4] + jb .extent_list.to_hole ; handle sparse files + ja @f + cmp ebx, dword[ebp + XFS.extent.br_startoff + 0] + jb .extent_list.to_hole ; handle sparse files + je .extent_list.to_extent ; read from the start of current extent + @@: + xor edx, edx + mov eax, [ebp + XFS.extent.br_blockcount] + add eax, dword[ebp + XFS.extent.br_startoff + 0] + adc edx, dword[ebp + XFS.extent.br_startoff + 4] +;DEBUGF 1,"br_startoff: %d %d\n",edx,eax + cmp esi, edx + ja .extent_list.skip_extent + jb .extent_list.to_extent + cmp ebx, eax + jae .extent_list.skip_extent + jmp .extent_list.to_extent + .extent_list.to_hole: +;DEBUGF 1,"extent_list.to_hole\n" + pop esi edx ecx ebx eax + jmp .extent_list.read_hole + .extent_list.to_extent: +;DEBUGF 1,"extent_list.to_extent\n" + pop esi edx ecx ebx eax + jmp .extent_list.read_extent + .extent_list.skip_extent: +;DEBUGF 1,"extent_list.skip_extent\n" + pop esi edx ecx ebx eax + jmp .extent_list.next_extent + + .extent_list.read_hole: +;DEBUGF 1,"hole: offt: 0x%x%x ",esi,ebx + push eax edx + mov eax, dword[ebp + XFS.extent.br_startoff + 0] + mov edx, dword[ebp + XFS.extent.br_startoff + 4] + push esi ebx + mov ebx, ecx + sub eax, ebx ; get hole_size, it is 64 bit + sbb edx, 0 ; now edx:eax contains the size of hole +;DEBUGF 1,"size: 0x%x%x\n",edx,eax + jnz @f ; if hole size >= 2^32, write bytes_to_read zero bytes + cmp eax, ecx ; if hole size >= bytes_to_read, write bytes_to_read zeros + jae @f + mov ecx, eax ; if hole is < than bytes_to_read, write hole size zeros + @@: + sub ebx, ecx ; bytes_to_read - hole_size = left_to_read + add dword[esp + 0], ecx ; update pushed file offset + adc dword[esp + 4], 0 + xor eax, eax ; hole is made of zeros + rep stosb + mov ecx, ebx + pop ebx esi + + test ecx, ecx ; all requested bytes are read? + pop edx eax + jz .quit + jmp .extent_list.read_extent ; continue from the start of unpacked extent + + .extent_list.read_extent: +;DEBUGF 1,"extent_list.read_extent\n" + push eax ebx ecx edx esi + mov eax, ebx + mov edx, esi + mov ecx, [ebp + XFS.blocklog] + shrd eax, edx, cl + shr edx, cl + sub eax, dword[ebp + XFS.extent.br_startoff + 0] ; skip esi:ebx ? + sbb edx, dword[ebp + XFS.extent.br_startoff + 4] + sub [ebp + XFS.extent.br_blockcount], eax + add dword[ebp + XFS.extent.br_startblock + 0], eax + adc dword[ebp + XFS.extent.br_startblock + 4], 0 + .extent_list.read_extent.next_block: +;DEBUGF 1,"extent_list.read_extent.next_block\n" + cmp [ebp + XFS.extent.br_blockcount], 0 ; out of blocks in current extent? + jne @f + pop esi edx ecx ebx eax + jmp .extent_list.next_extent ; go to next extent + @@: + mov eax, dword[ebp + XFS.extent.br_startblock + 0] + mov edx, dword[ebp + XFS.extent.br_startblock + 4] + push ebx + mov ebx, [ebp + XFS.cur_block] +;DEBUGF 1,"read block: 0x%x%x\n",edx,eax + stdcall xfs_read_block + test eax, eax + pop ebx + jz @f + pop esi edx ecx ebx eax + movi eax, ERROR_FS_FAIL + jmp .error + @@: + dec [ebp + XFS.extent.br_blockcount] + add dword[ebp + XFS.extent.br_startblock + 0], 1 + adc dword[ebp + XFS.extent.br_startblock + 4], 0 + mov esi, [ebp + XFS.cur_block] + mov ecx, [ebp + XFS.blocklog] + mov eax, 1 + shl eax, cl + dec eax ; get blocklog mask + and eax, ebx ; offset in current block + add esi, eax + neg eax + add eax, [ebp + XFS.blocksize] + mov ecx, [esp + 8] ; pushed ecx, bytes_to_read + cmp ecx, eax ; is current block enough? + jbe @f ; if so, read bytes_to_read bytes + mov ecx, eax ; otherwise read the block up to the end + @@: + sub [esp + 8], ecx ; left_to_read + add [esp + 12], ecx ; update current file offset, pushed ebx + sub dword[ebp + XFS.bytes_left_in_file + 0], ecx + sbb dword[ebp + XFS.bytes_left_in_file + 4], 0 + jnc @f + add dword[ebp + XFS.bytes_left_in_file + 0], ecx + mov ecx, dword[ebp + XFS.bytes_left_in_file + 0] + mov dword[ebp + XFS.bytes_left_in_file + 0], 0 + mov dword[ebp + XFS.bytes_left_in_file + 4], 0 + @@: + add [ebp + XFS.bytes_read], ecx + adc [esp + 0], dword 0 ; pushed esi +;DEBUGF 1,"read data: %d\n",ecx + rep movsb + mov ecx, [esp + 8] +;DEBUGF 1,"left_to_read: %d\n",ecx + xor ebx, ebx + test ecx, ecx + jz @f + cmp dword[ebp + XFS.bytes_left_in_file + 4], 0 + jne .extent_list.read_extent.next_block + cmp dword[ebp + XFS.bytes_left_in_file + 0], 0 + jne .extent_list.read_extent.next_block + @@: + pop esi edx ecx ebx eax + jmp .quit + + .btree: + mov ecx, [ebx + 12] ; bytes to read + mov [ebp + XFS.bytes_to_read], ecx + mov edi, [ebx + 16] ; buffer for data + mov esi, [ebx + 8] ; offset_hi + mov ebx, [ebx + 4] ; offset_lo + mov dword[ebp + XFS.file_offset + 0], ebx + mov dword[ebp + XFS.file_offset + 4], esi + mov [ebp + XFS.buffer_pos], edi + + mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo + bswap eax + mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo + mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi + bswap eax + mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi + + mov eax, [edx + xfs_inode.di_core.di_nextents] + bswap eax + mov [ebp + XFS.left_extents], eax + + mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes + + push ebx ecx edx esi edi + mov [ebp + XFS.eof], 0 + mov eax, dword[ebp + XFS.file_offset + 0] + mov edx, dword[ebp + XFS.file_offset + 4] + add eax, [ebp + XFS.bytes_to_read] + adc edx, 0 + sub eax, dword[ebp + XFS.bytes_left_in_file + 0] + sbb edx, dword[ebp + XFS.bytes_left_in_file + 4] + jc @f ; file_offset + bytes_to_read < file_size + jz @f ; file_offset + bytes_to_read = file_size + mov [ebp + XFS.eof], 1 + cmp edx, 0 + jne .error.eof + sub dword[ebp + XFS.bytes_to_read], eax + jc .error.eof + jz .error.eof + @@: + stdcall xfs_btree_read, 0, 0, 1 + pop edi esi edx ecx ebx + test eax, eax + jnz .error + cmp [ebp + XFS.eof], 1 + jne .quit + jmp .error.eof + + + .quit: + call xfs_unlock + pop edi esi edx ecx ebx + xor eax, eax + mov ebx, [ebp + XFS.bytes_read] +;DEBUGF 1,"quit: %d\n\n",ebx + ret + .error.eof: + movi eax, ERROR_END_OF_FILE + .error: +;DEBUGF 1,"error\n\n" + call xfs_unlock + pop edi esi edx ecx ebx + mov ebx, [ebp + XFS.bytes_read] + ret + + +;---------------------------------------------------------------- +; push max_offset_hi +; push max_offset_lo +; push nextents +; push block_number_hi +; push block_number_lo +; push extent_list +; -1 / read block number +;---------------------------------------------------------------- +xfs_extent_list_read_dirblock: ; skips holes +;DEBUGF 1,"xfs_extent_list_read_dirblock\n" + push ebx esi edi +;mov eax, [esp+28] +;DEBUGF 1,"nextents: %d\n",eax +;mov eax, [esp+20] +;mov edx, [esp+24] +;DEBUGF 1,"block_number: 0x%x%x\n",edx,eax +;mov eax, [esp+32] +;mov edx, [esp+36] +;DEBUGF 1,"max_addr : 0x%x%x\n",edx,eax + mov ebx, [esp + 16] + mov esi, [esp + 20] + mov edi, [esp + 24] +; mov ecx, [esp + 28] ; nextents + .next_extent: +;DEBUGF 1,"next_extent\n" + dec dword[esp + 28] + js .error + stdcall xfs_extent_unpack, ebx + add ebx, sizeof.xfs_bmbt_rec ; next extent + mov edx, dword[ebp + XFS.extent.br_startoff + 4] + mov eax, dword[ebp + XFS.extent.br_startoff + 0] + cmp edx, [esp + 36] ; max_offset_hi + ja .error + jb @f + cmp eax, [esp + 32] ; max_offset_lo + jae .error + @@: + cmp edi, edx + jb .hole + ja .check_count + cmp esi, eax + jb .hole + ja .check_count + jmp .read_block + .hole: +;DEBUGF 1,"hole\n" + mov esi, eax + mov edi, edx + jmp .read_block + .check_count: +;DEBUGF 1,"check_count\n" + add eax, [ebp + XFS.extent.br_blockcount] + adc edx, 0 + cmp edi, edx + ja .next_extent + jb .read_block + cmp esi, eax + jae .next_extent +; jmp .read_block + .read_block: +;DEBUGF 1,"read_block\n" + push esi edi + sub esi, dword[ebp + XFS.extent.br_startoff + 0] + sbb edi, dword[ebp + XFS.extent.br_startoff + 4] + add esi, dword[ebp + XFS.extent.br_startblock + 0] + adc edi, dword[ebp + XFS.extent.br_startblock + 4] + stdcall xfs_read_dirblock, esi, edi, [ebp + XFS.cur_dirblock] + pop edx eax + .quit: +;DEBUGF 1,"xfs_extent_list_read_dirblock: quit\n" + pop edi esi ebx + ret 24 + .error: +;DEBUGF 1,"xfs_extent_list_read_dirblock: error\n" + xor eax, eax + dec eax + mov edx, eax + pop edi esi ebx + ret 24 + + +;---------------------------------------------------------------- +; push dirblock_num +; push nextents +; push extent_list +;---------------------------------------------------------------- +xfs_dir2_node_get_numfiles: + + ; unfortunately, we need to set 'total entries' field + ; this often requires additional effort, since there is no such a number in most directory ondisk formats + +;DEBUGF 1,"xfs_dir2_node_get_numfiles\n" + push ebx ecx edx esi edi + + mov eax, [esp + 24] + mov edx, [esp + 28] + mov esi, [esp + 32] + stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1 + mov ecx, eax + and ecx, edx + inc ecx + jnz @f + movi eax, ERROR_FS_FAIL + jmp .error + @@: + mov ebx, [ebp + XFS.cur_dirblock] + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC + je .node + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC + je .leaf + mov eax, ERROR_FS_FAIL + jmp .error + + .node: +;DEBUGF 1,".node\n" + mov edi, [ebx + xfs_da_intnode.hdr.info.forw] + bswap edi + mov eax, [esp + 24] + mov edx, [esp + 28] + mov esi, [ebx + xfs_da_intnode.btree.before] + bswap esi + stdcall xfs_dir2_node_get_numfiles, eax, edx, esi + test eax, eax + jnz .error + jmp .common + + .leaf: +;DEBUGF 1,".leaf\n" + movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count] + xchg cl, ch + movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale] + xchg al, ah + sub ecx, eax + add [ebp + XFS.entries_read], ecx + mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw] + bswap edi + jmp .common + + .common: + test edi, edi + jz .quit + mov esi, edi + mov eax, [esp + 24] + mov edx, [esp + 28] + stdcall xfs_dir2_node_get_numfiles, eax, edx, esi + test eax, eax + jnz .error + jmp .quit + + .quit: +;DEBUGF 1,".quit\n" + pop edi esi edx ecx ebx + xor eax, eax + ret 12 + .error: +;DEBUGF 1,".error\n" + pop edi esi edx ecx ebx + movi eax, ERROR_FS_FAIL + ret 12 + + +;---------------------------------------------------------------- +; push hash +; push dirblock_num +; push nextents +; push extent_list +;---------------------------------------------------------------- +xfs_dir2_lookupdir_node: +DEBUGF 1,"xfs_dir2_lookupdir_node\n" + push ebx edx esi edi + + mov eax, [esp + 20] + mov edx, [esp + 24] + mov esi, [esp + 28] +DEBUGF 1,"read dirblock: 0x%x %d\n",esi,esi + stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1 +DEBUGF 1,"dirblock read: 0x%x%x\n",edx,eax + mov ecx, eax + and ecx, edx + inc ecx + jnz @f + movi eax, ERROR_FS_FAIL + jmp .error + @@: +DEBUGF 1,"checkpoint #1\n" + mov ebx, [ebp + XFS.cur_dirblock] + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC + je .node + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC + je .leaf + mov eax, ERROR_FS_FAIL +DEBUGF 1,"checkpoint #2\n" + jmp .error + + .node: +DEBUGF 1,".node\n" + mov edi, [esp + 32] ; hash + movzx ecx, word[ebx + xfs_da_intnode.hdr.count] + xchg cl, ch + mov [ebp + XFS.left_leaves], ecx + xor ecx, ecx + .node.next_leaf: + mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.hashval] + bswap esi + cmp edi, esi + jbe .node.leaf_found + inc ecx + cmp ecx, [ebp + XFS.left_leaves] + jne .node.next_leaf + mov eax, ERROR_FILE_NOT_FOUND + jmp .error + @@: + .node.leaf_found: + mov eax, [esp + 20] + mov edx, [esp + 24] + mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.before] + bswap esi + stdcall xfs_dir2_lookupdir_node, eax, edx, esi, edi + test eax, eax + jz .quit + movi eax, ERROR_FILE_NOT_FOUND + jmp .error + + .leaf: +DEBUGF 1,".leaf\n" + movzx ecx, [ebx + xfs_dir2_leaf.hdr.count] + xchg cl, ch + lea esi, [ebx + xfs_dir2_leaf.ents] + mov eax, [esp + 32] + stdcall xfs_get_addr_by_hash, esi, ecx + cmp eax, -1 + je .error + mov ecx, eax + jmp .quit + + .quit: +DEBUGF 1,".quit\n" + pop edi esi edx ebx + xor eax, eax + ret 16 + .error: +DEBUGF 1,".error\n" + pop edi esi edx ebx + ret 16 + + +;---------------------------------------------------------------- +; push dirblock_num +; push nextents +; push extent_list +;---------------------------------------------------------------- +xfs_dir2_btree_get_numfiles: +;DEBUGF 1,"xfs_dir2_node_get_numfiles\n" + push ebx ecx edx esi edi + + mov eax, [esp + 24] + mov edx, [esp + 28] + mov esi, [esp + 32] + stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1 + mov ecx, eax + and ecx, edx + inc ecx + jnz @f + movi eax, ERROR_FS_FAIL + jmp .error + @@: + mov ebx, [ebp + XFS.cur_dirblock] + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC + je .node + cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC + je .leaf + mov eax, ERROR_FS_FAIL + jmp .error + + .node: +;DEBUGF 1,".node\n" + mov edi, [ebx + xfs_da_intnode.hdr.info.forw] + bswap edi + mov eax, [esp + 24] + mov edx, [esp + 28] + mov esi, [ebx + xfs_da_intnode.btree.before] + bswap esi + stdcall xfs_dir2_node_get_numfiles, eax, edx, esi + test eax, eax + jnz .error + jmp .common + + .leaf: +;DEBUGF 1,".leaf\n" + movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count] + xchg cl, ch + movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale] + xchg al, ah + sub ecx, eax + add [ebp + XFS.entries_read], ecx + mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw] + bswap edi + jmp .common + + .common: + test edi, edi + jz .quit + mov esi, edi + mov eax, [esp + 24] + mov edx, [esp + 28] + stdcall xfs_dir2_node_get_numfiles, eax, edx, esi + test eax, eax + jnz .error + jmp .quit + + .quit: +;DEBUGF 1,".quit\n" + pop edi esi edx ecx ebx + xor eax, eax + ret 12 + .error: +;DEBUGF 1,".error\n" + pop edi esi edx ecx ebx + movi eax, ERROR_FS_FAIL + ret 12 + + +;---------------------------------------------------------------- +; push is_root +; push block_hi +; push block_lo +;---------------------------------------------------------------- +xfs_btree_read: + push ebx ecx edx esi edi + cmp dword[esp + 32], 1 ; is root? + je .root + jmp .not_root + .root: +DEBUGF 1,".root\n" + mov ebx, [ebp + XFS.cur_inode_save] + add ebx, xfs_inode.di_u + movzx edx, [ebx + xfs_bmdr_block.bb_numrecs] + xchg dl, dh + dec edx + add ebx, sizeof.xfs_bmdr_block + xor eax, eax + dec eax + .root.next_key: +DEBUGF 1,".root.next_key\n" + cmp [ebp + XFS.bytes_to_read], 0 + je .quit + inc eax + cmp eax, edx ; out of keys? + ja .root.key_found ; there is no length field, so try the last key + lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0] + lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4] + bswap edi + bswap esi + mov ecx, [ebp + XFS.blocklog] + shld edi, esi, cl + shl esi, cl + cmp edi, dword[ebp + XFS.file_offset + 4] + ja .root.prev_or_hole + jb .root.next_key + cmp esi, dword[ebp + XFS.file_offset + 0] + ja .root.prev_or_hole + jb .root.next_key + jmp .root.key_found + .root.prev_or_hole: +DEBUGF 1,".root.prev_or_hole\n" + test eax, eax + jz .root.hole + dec eax + jmp .root.key_found + .root.hole: +DEBUGF 1,".root.hole\n" + push eax edx esi edi + mov ecx, [ebp + XFS.blocklog] + shld edi, esi, cl + shl esi, cl + sub esi, dword[ebp + XFS.file_offset + 0] + sbb edi, dword[ebp + XFS.file_offset + 4] + mov ecx, [ebp + XFS.bytes_to_read] + cmp edi, 0 ; hole size >= 2^32 + jne @f + cmp ecx, esi + jbe @f + mov ecx, esi + @@: + add dword[ebp + XFS.file_offset + 0], ecx + adc dword[ebp + XFS.file_offset + 4], 0 + sub [ebp + XFS.bytes_to_read], ecx + xor eax, eax + mov edi, [ebp + XFS.buffer_pos] + rep stosb + mov [ebp + XFS.buffer_pos], edi + pop edi esi edx eax + jmp .root.next_key + .root.key_found: +DEBUGF 1,".root.key_found\n" + mov edx, [ebp + XFS.cur_inode_save] + mov eax, [ebp + XFS.inodesize] + sub eax, xfs_inode.di_u + cmp [edx + xfs_inode.di_core.di_forkoff], 0 + je @f + movzx eax, [edx + xfs_inode.di_core.di_forkoff] + shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3 + @@: + sub eax, sizeof.xfs_bmdr_block + shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr) + mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi + mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi + bswap edx + bswap eax + stdcall xfs_btree_read, eax, edx, 0 + test eax, eax + jnz .error + jmp .root.next_key + + .not_root: +DEBUGF 1,".root.not_root\n" + mov eax, [esp + 24] ; block_lo + mov edx, [esp + 28] ; block_hi + mov ebx, [ebp + XFS.cur_block] + stdcall xfs_read_block + test eax, eax + jnz .error + mov ebx, [ebp + XFS.cur_block] + + cmp [ebx + xfs_bmbt_block.bb_magic], XFS_BMAP_MAGIC + jne .error + cmp [ebx + xfs_bmbt_block.bb_level], 0 ; leaf? + je .leaf + jmp .node + + .node: +; mov eax, [ebp + XFS.blocksize] +; sub eax, sizeof.xfs_bmbt_block +; shr eax, 4 ; maxnumrecs + mov eax, dword[ebp + XFS.file_offset + 0] ; lo + mov edx, dword[ebp + XFS.file_offset + 4] ; hi + movzx edx, [ebx + xfs_bmbt_block.bb_numrecs] + xchg dl, dh + dec edx + add ebx, sizeof.xfs_bmbt_block + xor eax, eax + dec eax + .node.next_key: + push eax ecx edx esi edi + mov eax, [esp + 44] ; block_lo + mov edx, [esp + 48] ; block_hi + mov ebx, [ebp + XFS.cur_block] + stdcall xfs_read_block + test eax, eax + jnz .error + mov ebx, [ebp + XFS.cur_block] + add ebx, sizeof.xfs_bmbt_block + pop edi esi edx ecx eax + cmp [ebp + XFS.bytes_to_read], 0 + je .quit + inc eax + cmp eax, edx ; out of keys? + ja .node.key_found ; there is no length field, so try the last key + lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0] + lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4] + bswap edi + bswap esi + mov ecx, [ebp + XFS.blocklog] + shld edi, esi, cl + shl esi, cl + cmp edi, dword[ebp + XFS.file_offset + 4] + ja .node.prev_or_hole + jb .node.next_key + cmp esi, dword[ebp + XFS.file_offset + 0] + ja .node.prev_or_hole + jb .node.next_key + jmp .node.key_found + .node.prev_or_hole: + test eax, eax + jz .node.hole + dec eax + jmp .node.key_found + .node.hole: + push eax edx esi edi + mov ecx, [ebp + XFS.blocklog] + shld edi, esi, cl + shl esi, cl + sub esi, dword[ebp + XFS.file_offset + 0] + sbb edi, dword[ebp + XFS.file_offset + 4] + mov ecx, [ebp + XFS.bytes_to_read] + cmp edi, 0 ; hole size >= 2^32 + jne @f + cmp ecx, esi + jbe @f + mov ecx, esi + @@: + add dword[ebp + XFS.file_offset + 0], ecx + adc dword[ebp + XFS.file_offset + 4], 0 + sub [ebp + XFS.bytes_to_read], ecx + xor eax, eax + mov edi, [ebp + XFS.buffer_pos] + rep stosb + mov [ebp + XFS.buffer_pos], edi + pop edi esi edx eax + jmp .node.next_key + .node.key_found: + mov edx, [ebp + XFS.cur_inode_save] + mov eax, [ebp + XFS.inodesize] + sub eax, xfs_inode.di_u + cmp [edx + xfs_inode.di_core.di_forkoff], 0 + je @f + movzx eax, [edx + xfs_inode.di_core.di_forkoff] + shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3 + @@: + sub eax, sizeof.xfs_bmdr_block + shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr) + mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi + mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi + bswap edx + bswap eax + stdcall xfs_btree_read, eax, edx, 0 + test eax, eax + jnz .error + jmp .node.next_key + jmp .quit + + .leaf: + + jmp .quit + + .error: + pop edi esi edx ecx ebx + movi eax, ERROR_FS_FAIL + ret 4 + .quit: + pop edi esi edx ecx ebx + xor eax, eax + ret 4 + + +;---------------------------------------------------------------- +; push nextents +; push extent_list +; push file_offset_hi +; push file_offset_lo +;---------------------------------------------------------------- +;xfs_extent_list_read: +; push ebx 0 edx esi edi ; zero means actually_read_bytes +; +; .quit: +; pop edi esi edx ecx ebx +; xor eax, eax +; ret 24 +; .error: +; pop edi esi edx ecx ebx +; ret 24 diff --git a/kernel/branches/kolibri-process/fs/xfs.inc b/kernel/branches/kolibri-process/fs/xfs.inc new file mode 100644 index 000000000..7ba2ce68f --- /dev/null +++ b/kernel/branches/kolibri-process/fs/xfs.inc @@ -0,0 +1,518 @@ +; from stat.h +; distinguish file types +S_IFMT = 0170000o ; These bits determine file type. +S_IFDIR = 0040000o ; Directory. +S_IFCHR = 0020000o ; Character device. +S_IFBLK = 0060000o ; Block device. +S_IFREG = 0100000o ; Regular file. +S_IFIFO = 0010000o ; FIFO. +S_IFLNK = 0120000o ; Symbolic link. +S_IFSOCK = 0140000o ; Socket. +; end stat.h + + +; XFS null constant: empty fields must be all ones, not zeros! +XFS_NULL = -1 + + +; static sector numbers +XFS_SECT_SB = 0 +XFS_SECT_AGF = 1 +XFS_SECT_AGI = 2 +XFS_SECT_AGFL = 3 + + +; signatures of file system structures +; 'string' numbers are treated by fasm as big endian +XFS_SB_MAGIC = 'XFSB' +XFS_AGF_MAGIC = 'XAGF' +XFS_AGI_MAGIC = 'XAGI' +XFS_ABTB_MAGIC = 'ABTB' +XFS_ABTC_MAGIC = 'ABTC' +XFS_IBT_MAGIC = 'IABT' +XFS_DINODE_MAGIC = 'IN' +XFS_BMAP_MAGIC = 'BMAP' +XFS_DA_NODE_MAGIC = 0xbefe ; those are little endian here +XFS_ATTR_LEAF_MAGIC = 0xeefb ; but big endian in docs +XFS_DIR2_LEAF1_MAGIC = 0xf1d2 ; pay attention! +XFS_DIR2_LEAFN_MAGIC = 0xffd2 ; +XFS_DIR2_BLOCK_MAGIC = 'XD2B' +XFS_DIR2_DATA_MAGIC = 'XD2D' +XFS_DIR2_FREE_MAGIC = 'XD2F' +XFS_DQUOT_MAGIC = 'DQ' + + +; bitfield lengths for packed extent +; MSB to LSB / left to right +BMBT_EXNTFLAG_BITLEN = 1 +BMBT_STARTOFF_BITLEN = 54 +BMBT_STARTBLOCK_BITLEN = 52 +BMBT_BLOCKCOUNT_BITLEN = 21 + + +; those constants are taken from linux source (xfs_dir2_leaf.h) +; they are magic infile offsets for directories +XFS_DIR2_DATA_ALIGN_LOG = 3 ; i.e., 8 bytes +XFS_DIR2_LEAF_SPACE = 1 +XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG)) +XFS_DIR2_LEAF_OFFSET = (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE) +XFS_DIR2_FREE_SPACE = 2 +XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG)) +XFS_DIR2_FREE_OFFSET = (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE) + + +; data section magic constants for directories (xfs_dir2_data.h) +XFS_DIR2_DATA_FD_COUNT = 3 +XFS_DIR2_DATA_FREE_TAG = 0xffff + + +; valid inode formats +; enum xfs_dinode_fmt (xfs_dinode.h) +XFS_DINODE_FMT_DEV = 0 ; xfs_dev_t +XFS_DINODE_FMT_LOCAL = 1 ; one inode is enough (shortdir) +XFS_DINODE_FMT_EXTENTS = 2 ; one or more extents (leafdir, nodedir, regular files) +XFS_DINODE_FMT_BTREE = 3 ; highly fragmented files or really huge directories +XFS_DINODE_FMT_UUID = 4 ; uuid_t + + +; size of the unlinked inode hash table in the agi +XFS_AGI_UNLINKED_BUCKETS = 64 + + +; possible extent states +; enum xfs_exntst_t (xfs_bmap_btree.h) +XFS_EXT_NORM = 0 +XFS_EXT_UNWRITTEN = 1 +XFS_EXT_DMAPI_OFFLINE = 2 +XFS_EXT_INVALID = 3 + + +; values for inode core flags / di_flags (xfs_dinode.h) +XFS_DIFLAG_REALTIME_BIT = 0 ; file's blocks come from rt area +XFS_DIFLAG_PREALLOC_BIT = 1 ; file space has been preallocated +XFS_DIFLAG_NEWRTBM_BIT = 2 ; for rtbitmap inode, new format +XFS_DIFLAG_IMMUTABLE_BIT = 3 ; inode is immutable +XFS_DIFLAG_APPEND_BIT = 4 ; inode is append-only +XFS_DIFLAG_SYNC_BIT = 5 ; inode is written synchronously +XFS_DIFLAG_NOATIME_BIT = 6 ; do not update atime +XFS_DIFLAG_NODUMP_BIT = 7 ; do not dump +XFS_DIFLAG_RTINHERIT_BIT = 8 ; create with realtime bit set +XFS_DIFLAG_PROJINHERIT_BIT = 9 ; create with parents projid +XFS_DIFLAG_NOSYMLINKS_BIT = 10 ; disallow symlink creation +XFS_DIFLAG_EXTSIZE_BIT = 11 ; inode extent size allocator hint +XFS_DIFLAG_EXTSZINHERIT_BIT = 12 ; inherit inode extent size +XFS_DIFLAG_NODEFRAG_BIT = 13 ; do not reorganize/defragment +XFS_DIFLAG_FILESTREAM_BIT = 14 ; use filestream allocator +XFS_DIFLAG_REALTIME = (1 SHL XFS_DIFLAG_REALTIME_BIT) +XFS_DIFLAG_PREALLOC = (1 SHL XFS_DIFLAG_PREALLOC_BIT) +XFS_DIFLAG_NEWRTBM = (1 SHL XFS_DIFLAG_NEWRTBM_BIT) +XFS_DIFLAG_IMMUTABLE = (1 SHL XFS_DIFLAG_IMMUTABLE_BIT) +XFS_DIFLAG_APPEND = (1 SHL XFS_DIFLAG_APPEND_BIT) +XFS_DIFLAG_SYNC = (1 SHL XFS_DIFLAG_SYNC_BIT) +XFS_DIFLAG_NOATIME = (1 SHL XFS_DIFLAG_NOATIME_BIT) +XFS_DIFLAG_NODUMP = (1 SHL XFS_DIFLAG_NODUMP_BIT) +XFS_DIFLAG_RTINHERIT = (1 SHL XFS_DIFLAG_RTINHERIT_BIT) +XFS_DIFLAG_PROJINHERIT = (1 SHL XFS_DIFLAG_PROJINHERIT_BIT) +XFS_DIFLAG_NOSYMLINKS = (1 SHL XFS_DIFLAG_NOSYMLINKS_BIT) +XFS_DIFLAG_EXTSIZE = (1 SHL XFS_DIFLAG_EXTSIZE_BIT) +XFS_DIFLAG_EXTSZINHERIT = (1 SHL XFS_DIFLAG_EXTSZINHERIT_BIT) +XFS_DIFLAG_NODEFRAG = (1 SHL XFS_DIFLAG_NODEFRAG_BIT) +XFS_DIFLAG_FILESTREAM = (1 SHL XFS_DIFLAG_FILESTREAM_BIT) + + +; superblock _ondisk_ structure (xfs_sb.h) +; this is _not_ the partition structure +; for XFS partition structure see XFS below +struct xfs_sb + sb_magicnum dd ? ; signature, must be XFS_SB_MAGIC + sb_blocksize dd ? ; block is the minimal file system unit, in bytes + sb_dblocks dq ? ; number of data blocks + sb_rblocks dq ? ; number of realtime blocks (not supported yet!) + sb_rextents dq ? ; number of realtime extents (not supported yet!) + sb_uuid rb 16 ; file system unique identifier + sb_logstart dq ? ; starting block of log (for internal journal; journals on separate devices are not supported!) + sb_rootino dq ? ; root inode number + sb_rbmino dq ? ; bitmap inode for realtime extents (ignored) + sb_rsumino dq ? ; summary inode for rt bitmap (ignored) + sb_rextsize dd ? ; realtime extent size, blocks + sb_agblocks dd ? ; size of an allocation group (the last one may be smaller!) + sb_agcount dd ? ; number of allocation groups + sb_rbmblocks dd ? ; number of rt bitmap blocks + sb_logblocks dd ? ; number of log blocks + sb_versionnum dw ? ; header version == XFS_SB_VERSION + sb_sectsize dw ? ; volume sector size in bytes (only 512B sectors are supported) + sb_inodesize dw ? ; inode size, bytes + sb_inopblock dw ? ; inodes per block + sb_fname rb 12 ; inodes per block (aka label) + sb_blocklog db ? ; log2 of sb_blocksize + sb_sectlog db ? ; log2 of sb_blocksize + sb_inodelog db ? ; log2 of sb_inodesize + sb_inopblog db ? ; log2 of sb_inopblock + sb_agblklog db ? ; log2 of sb_agblocks (rounded up!) + sb_rextslog db ? ; log2 of sb_rextents + sb_inprogress db ? ; mkfs is in progress, don't mount + sb_imax_pct db ? ; max % of fs for inode space + ; statistics + sb_icount dq ? ; allocated inodes + sb_ifree dq ? ; free inodes + sb_fdblocks dq ? ; free data blocks + sb_frextents dq ? ; free realtime extents + + sb_uquotino dq ? ; user quota inode + sb_gquotino dq ? ; group quota inode + sb_qflags dw ? ; quota flags + sb_flags db ? ; misc. flags + sb_shared_vn db ? ; shared version number + sb_inoalignmt dd ? ; inode chunk alignment, fsblocks + sb_unit dd ? ; stripe or raid unit + sb_width dd ? ; stripe or raid width + sb_dirblklog db ? ; log2 of dir block size (fsbs) + sb_logsectlog db ? ; log2 of the log sector size + sb_logsectsize dw ? ; sector size for the log, bytes + sb_logsunit dd ? ; stripe unit size for the log + sb_features2 dd ? ; additional feature bits +ends + + +; allocation group inode (xfs_ag.h) +struct xfs_agi + agi_magicnum dd ? ; magic number == XFS_AGI_MAGIC + agi_versionnum dd ? ; header version == XFS_AGI_VERSION + agi_seqno dd ? ; sequence number starting from 0 + agi_length dd ? ; size in blocks of a.g. + agi_count dd ? ; count of allocated inodes + agi_root dd ? ; root of inode btree + agi_level dd ? ; levels in inode btree + agi_freecount dd ? ; number of free inodes + agi_newino dd ? ; new inode just allocated + agi_dirino dd ? ; last directory inode chunk + agi_unlinked rd XFS_AGI_UNLINKED_BUCKETS ; Hash table of inodes which have been unlinked but are still being referenced +ends + + +; superblock structure of b+tree node/leaf (same structure, bb_level matters) +struct xfs_btree_sblock + bb_magic dd ? + bb_level dw ? ; distinguishes nodeds and leaves + bb_numrecs dw ? + bb_leftsib dd ? + bb_rightsib dd ? +ends + + +; record of b+tree inode +struct xfs_inobt_rec + ir_startino dd ? + ir_freecount dd ? + ir_free dq ? +ends + + +; structure to store create, access and modification time in inode core +struct xfs_timestamp + t_sec dd ? + t_nsec dd ? ; nanoseconds +ends + + +; inode core structure: basic information about file +struct xfs_dinode_core + di_magic dw ? ; inode magic = XFS_DINODE_MAGIC + di_mode dw ? ; mode and type of file + di_version db ? ; inode version + di_format db ? ; format of di_c data + di_onlink dw ? ; old number of links to file + di_uid dd ? ; owner's user id + di_gid dd ? ; owner's group id + di_nlink dd ? ; number of links to file + di_projid dw ? ; owner's project id + di_pad rb 8 ; unused, zeroed space + di_flushiter dw ? ; incremented on flush + di_atime xfs_timestamp ; time last accessed + di_mtime xfs_timestamp ; time last modified + di_ctime xfs_timestamp ; time created/inode modified + di_size dq ? ; number of bytes in file + di_nblocks dq ? ; number of direct & btree blocks used + di_extsize dd ? ; basic/minimum extent size for file + di_nextents dd ? ; number of extents in data fork + di_anextents dw ? ; number of extents in attribute fork + di_forkoff db ? ; attr fork offs, <<3 for 64b align + di_aformat db ? ; format of attr fork's data + di_dmevmask dd ? ; DMIG event mask + di_dmstate dw ? ; DMIG state info + di_flags dw ? ; random flags, XFS_DIFLAG_... + di_gen dd ? ; generation number +ends + + +; shortform dir header +struct xfs_dir2_sf_hdr + count db ? ; the number of directory entries, used only if each inode number fits 4 bytes; zero otherwise + i8count db ? ; the number of directory entries, used only when count is zero + parent dq ? ; parent inode number: xfs_dir2_inou_t (4 or 8 bytes) +ends + + +; shortform dir entry +struct xfs_dir2_sf_entry + namelen db ? ; actual name length (ASCII) + offset rb 2 ; saved offset + name db ? ; name, variable size +; inumber dq ? ; xfs_dir2_inou_t +ends + + +; active entry in a data block +; aligned to 8 bytes +; tag appears as the last 2 bytes +struct xfs_dir2_data_entry + inumber dq ? ; inode number + namelen db ? ; name length + name db ? ; name bytes, no null +; tag dw ? ; starting offset of us +ends + + +; unused entry in a data block +; aligned to 8 bytes +; tag appears as the last 2 bytes +struct xfs_dir2_data_unused + freetag dw ? ; XFS_DIR2_DATA_FREE_TAG + length dw ? ; total free length +; tag dw ? ; starting offset of us +ends + + +; generic data entry +struct xfs_dir2_data_union + union + xentry xfs_dir2_data_entry + unused xfs_dir2_data_unused + ends +ends + + +; describe a free area in the data block +; the freespace will be formatted as a xfs_dir2_data_unused_t +struct xfs_dir2_data_free + offset dw ? ; start of freespace + length dw ? ; length of freespace +ends + + +; header for the data blocks +; always at the beginning of a directory-sized block +; the code knows that XFS_DIR2_DATA_FD_COUNT is 3 +struct xfs_dir2_data_hdr + magic dd ? ; XFS_DIR2_DATA_MAGIC or XFS_DIR2_BLOCK_MAGIC + bestfree xfs_dir2_data_free + bestfree2 xfs_dir2_data_free + bestfree3 xfs_dir2_data_free +ends + + +; leaf block entry +struct xfs_dir2_leaf_entry + hashval dd ? ; hash value of name + address dd ? ; address of data entry +ends + + +; the tail of directory block +struct xfs_dir2_block_tail + count dd ? ; count of leaf entries + stale dd ? ; count of stale leaf entries +ends + + +; generic single-block structure, for xfs_db +struct xfs_dir2_block + hdr xfs_dir2_data_hdr + u xfs_dir2_data_union +; leaf xfs_dir2_leaf_entry +; tail xfs_dir2_block_tail +ends + + +; +struct xfs_dir2_data + hdr xfs_dir2_data_hdr ; magic XFS_DIR2_DATA_MAGIC + u xfs_dir2_data_union +ends + + +; +struct xfs_da_blkinfo + forw dd ? ; previous block in list + back dd ? ; following block in list + magic dw ? ; validity check on block + pad dw ? ; unused +ends + + +; leaf block header +struct xfs_dir2_leaf_hdr + info xfs_da_blkinfo ; header for da routines + count dw ? ; count of entries + stale dw ? ; count of stale entries +ends + + +; leaf block tail +struct xfs_dir2_leaf_tail + bestcount dd ? +ends + + +; leaf block +; bests and tail are at the end of the block for single-leaf only +; (magic = XFS_DIR2_LEAF1_MAGIC not XFS_DIR2_LEAFN_MAGIC) +struct xfs_dir2_leaf + hdr xfs_dir2_leaf_hdr ; leaf header + ents xfs_dir2_leaf_entry ; entries +; bests dw ? ; best free counts +; tail xfs_dir2_leaf_tail ; leaf tail +ends + + +; header of 'free' block part +struct xfs_dir2_free_hdr + magic dd ? ; XFS_DIR2_FREE_MAGIC + firstdb dd ? ; db of first entry + nvalid dd ? ; count of valid entries + nused dd ? ; count of used entries +ends + + +; 'free' part of directiry block +struct xfs_dir2_free + hdr xfs_dir2_free_hdr ; block header + bests dw ? ; best free counts + ; unused entries are -1 (XFS_NULL) +ends + + +; b+tree node header +struct xfs_da_node_hdr + info xfs_da_blkinfo + count dw ? + level dw ? +ends + + +; b+tree node +struct xfs_da_node_entry + hashval dd ? ; hash value for this descendant + before dd ? ; Btree block before this key +ends + + +; +struct xfs_da_intnode + hdr xfs_da_node_hdr + btree xfs_da_node_entry +ends + + +; packet extent +struct xfs_bmbt_rec + l0 dq ? + l1 dq ? +ends + + +; unpacked extent +struct xfs_bmbt_irec + br_startoff dq ? ; starting file offset + br_startblock dq ? ; starting block number + br_blockcount dd ? ; number of blocks + br_state dd ? ; extent state +ends + + +; bmap root header, on-disk form only +struct xfs_bmdr_block + bb_level dw ? ; 0 is a leaf + bb_numrecs dw ? ; current number of data records +ends + + +; key structure for non-leaf levels of the tree +struct xfs_bmbt_key + br_startoff dq ? ; starting file offset +ends + + +sizeof.xfs_bmbt_ptr = 8 ; workaround +sizeof.xfs_bmdr_ptr = 8 ; workaround + + +; long form header: bmap btrees +; xfs_btree_lblock is xfs_bmbt_block (xfs_btree.h) +struct xfs_bmbt_block + bb_magic dd ? ; magic number for block type + bb_level dw ? ; 0 is a leaf + bb_numrecs dw ? ; current number of data records + bb_leftsib dq ? ; left sibling block or NULLDFSBNO + bb_rightsib dq ? ; right sibling block or NULLDFSBNO +ends + + +; high level inode structure +struct xfs_inode + di_core xfs_dinode_core ; main info, aka core + di_next_unlinked dd ? ; unlinked but still used inode (if any, XFS_NULL otherwise) + di_u db ? ; data fork inode part +; di_a db ? ; data attribute +ends + + +; internal data for every XFS partition +; this _is_ XFS partition structure +; most fields are unpacked or bswap'ed values from the superblock, so see xfs_sb structure above +struct XFS PARTITION + Lock MUTEX ? ; access mutex + blocksize dd ? + sectsize dd ? + dirblocksize dd ? + rootino dq ? + cur_block dd ? + cur_inode dd ? + cur_sect dd ? + cur_dirblock dd ? + tmp_inode dd ? + versionnum dd ? + features2 dd ? + inodesize dd ? + inopblock dd ? + blocklog dd ? + sectlog dd ? + inodelog dd ? + inopblog dd ? + agblklog dd ? + blockmsectlog dd ? + inodetoblocklog dd ? + dirblklog dd ? + sectpblock dd ? + agblocks dd ? + ; helpers, temporary vars, etc + agblockmask dq ? + extent xfs_bmbt_irec + left_extents dd ? + left_leaves dd ? + bytes_to_read dd ? + bytes_read dd ? + entries_read dd ? + file_offset dq ? + max_dirblockaddr dd ? + next_block_num dq ? + dir2_leaf_offset_blocks dd ? + dir2_free_offset_blocks dd ? + cur_inode_save dd ? + bytes_left_in_file dq ? + ro_nextents dd ? + bb_ptrs dd ? + maxnumrecs dd ? + buffer_pos dd ? + eof dd ? +ends diff --git a/kernel/branches/kolibri-process/gui/button.inc b/kernel/branches/kolibri-process/gui/button.inc new file mode 100644 index 000000000..511a16b7b --- /dev/null +++ b/kernel/branches/kolibri-process/gui/button.inc @@ -0,0 +1,508 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + +;============================================================================== +;///// public functions /////////////////////////////////////////////////////// +;============================================================================== + +button.MAX_BUTTONS = 4095 + +struct SYS_BUTTON + pslot dw ? + id_lo dw ? + left dw ? + width dw ? + top dw ? + height dw ? + id_hi dw ? + dw ? +ends + +align 4 +;------------------------------------------------------------------------------ +syscall_button: ;///// system function 8 ////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Define/undefine GUI button object +;------------------------------------------------------------------------------ +;; Define button: +;> ebx = pack[16(x), 16(width)] +;> ecx = pack[16(y), 16(height)] +;> edx = pack[8(flags), 24(button identifier)] +;> flags bits: +;> 7 (31) = 0 +;> 6 (30) = don't draw button +;> 5 (29) = don't draw button frame when pressed +;> esi = button color +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Undefine button: +;> edx = pack[8(flags), 24(button identifier)] +;> flags bits: +;> 7 (31) = 1 +;------------------------------------------------------------------------------ + ; do we actually need to undefine the button? + test edx, 0x80000000 + jnz .remove_button + + ; do we have free button slots available? + mov edi, [BTN_ADDR] + mov eax, [edi] + cmp eax, button.MAX_BUTTONS + jge .exit + + ; does it have positive size? (otherwise it doesn't have sense) + or bx, bx + jle .exit + or cx, cx + jle .exit + + ; make coordinates clientbox-relative + push eax + mov eax, [current_slot] + rol ebx, 16 + add bx, word[eax + APPDATA.wnd_clientbox.left] + rol ebx, 16 + rol ecx, 16 + add cx, word[eax + APPDATA.wnd_clientbox.top] + rol ecx, 16 + pop eax + + ; basic checks passed, define the button + push ebx ecx edx + inc eax + mov [edi], ax + shl eax, 4 + add edi, eax + ; NOTE: this code doesn't rely on SYS_BUTTON struct, please revise it + ; if you change something + mov ax, [CURRENT_TASK] + stosw + mov ax, dx + stosw ; button id number: bits 0-15 + mov eax, ebx + rol eax, 16 + stosd ; x start | x size + mov eax, ecx + rol eax, 16 + stosd ; y start | y size + mov eax, edx + shr eax, 16 + stosw ; button id number: bits 16-31 + pop edx ecx ebx + + ; do we also need to draw the button? + test edx, 0x40000000 + jnz .exit + + ; draw button body + + pushad + + ; calculate window-relative coordinates + movzx edi, cx + shr ebx, 16 + shr ecx, 16 + mov eax, [TASK_BASE] + add ebx, [eax - twdw + WDATA.box.left] + add ecx, [eax - twdw + WDATA.box.top] + mov eax, ebx + shl eax, 16 + mov ax, bx + add ax, word[esp + 16] + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + ; calculate initial color + mov ecx, esi + cmp [buttontype], 0 + je @f + call button._.incecx2 + + ; set button height counter + @@: + mov edx, edi + + .next_line: + call button._.button_dececx + push edi + xor edi, edi +; call [draw_line] + call __sys_draw_line + pop edi + add ebx, 0x00010001 + dec edx + jnz .next_line + + popad + + ; draw button frame + + push ebx ecx + + ; calculate window-relative coordinates + shr ebx, 16 + shr ecx, 16 + mov eax, [TASK_BASE] + add ebx, [eax - twdw + WDATA.box.left] + add ecx, [eax - twdw + WDATA.box.top] + + ; top border + mov eax, ebx + shl eax, 16 + mov ax, bx + add ax, [esp + 4] + mov ebx, ecx + shl ebx, 16 + mov bx, cx + push ebx + xor edi, edi + mov ecx, esi + call button._.incecx +; call [draw_line] + call __sys_draw_line + + ; bottom border + movzx edx, word[esp + 4 + 0] + add ebx, edx + shl edx, 16 + add ebx, edx + mov ecx, esi + call button._.dececx +; call [draw_line] + call __sys_draw_line + + ; left border + pop ebx + push edx + mov edx, eax + shr edx, 16 + mov ax, dx + mov edx, ebx + shr edx, 16 + mov bx, dx + add bx, [esp + 4 + 0] + pop edx + mov ecx, esi + call button._.incecx +; call [draw_line] + call __sys_draw_line + + ; right border + mov dx, [esp + 4] + add ax, dx + shl edx, 16 + add eax, edx + add ebx, 0x00010000 + mov ecx, esi + call button._.dececx +; call [draw_line] + call __sys_draw_line + + pop ecx ebx + + .exit: + ret + +; FIXME: mutex needed +syscall_button.remove_button: + and edx, 0x00ffffff + mov edi, [BTN_ADDR] + mov ebx, [edi] + inc ebx + imul esi, ebx, sizeof.SYS_BUTTON + add esi, edi + xor ecx, ecx + add ecx, -sizeof.SYS_BUTTON + add esi, sizeof.SYS_BUTTON + + .next_button: + dec ebx + jz .exit + + add ecx, sizeof.SYS_BUTTON + add esi, -sizeof.SYS_BUTTON + + ; does it belong to our process? + mov ax, [CURRENT_TASK] + cmp ax, [esi + SYS_BUTTON.pslot] + jne .next_button + + ; does the identifier match? + mov eax, dword[esi + SYS_BUTTON.id_hi - 2] + mov ax, [esi + SYS_BUTTON.id_lo] + and eax, 0x00ffffff + cmp edx, eax + jne .next_button + + ; okay, undefine it + push ebx + mov ebx, esi + lea eax, [esi + sizeof.SYS_BUTTON] + call memmove + dec dword[edi] + add ecx, -sizeof.SYS_BUTTON + pop ebx + jmp .next_button + + .exit: + ret + +align 4 +;------------------------------------------------------------------------------ +sys_button_activate_handler: ;///////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = pack[8(process slot), 24(button id)] +;> ebx = pack[16(button x coord), 16(button y coord)] +;> cl = mouse button mask this system button was pressed with +;------------------------------------------------------------------------------ + call button._.find_button + or eax, eax + jz .exit + + mov ebx, dword[eax + SYS_BUTTON.id_hi - 2] + call button._.negative_button + + .exit: + ret + +align 4 +;------------------------------------------------------------------------------ +sys_button_deactivate_handler: ;/////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = pack[8(process slot), 24(button id)] +;> ebx = pack[16(button x coord), 16(button y coord)] +;> cl = mouse button mask this system button was pressed with +;------------------------------------------------------------------------------ + call button._.find_button + or eax, eax + jz .exit + + mov ebx, dword[eax + SYS_BUTTON.id_hi - 2] + call button._.negative_button + + .exit: + ret + +align 4 +;------------------------------------------------------------------------------ +sys_button_perform_handler: ;////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = pack[8(process slot), 24(button id)] +;> ebx = pack[16(button x coord), 16(button y coord)] +;> cl = mouse button mask this system button was pressed with +;------------------------------------------------------------------------------ + shl eax, 8 + mov al, cl + movzx ebx, byte[BTN_COUNT] + mov [BTN_BUFF + ebx * 4], eax + inc bl + mov [BTN_COUNT], bl + ret + +;============================================================================== +;///// private functions ////////////////////////////////////////////////////// +;============================================================================== + +;------------------------------------------------------------------------------ +button._.find_button: ;//////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Find system button by specified process slot, id and coordinates +;------------------------------------------------------------------------------ +;> eax = pack[8(process slot), 24(button id)] or 0 +;> ebx = pack[16(button x coord), 16(button y coord)] +;------------------------------------------------------------------------------ +;< eax = pointer to SYS_BUTTON struct or 0 +;------------------------------------------------------------------------------ + push ecx edx esi edi + + mov edx, eax + shr edx, 24 + and eax, 0x0ffffff + + mov edi, [BTN_ADDR] + mov ecx, [edi] + imul esi, ecx, sizeof.SYS_BUTTON + add esi, edi + inc ecx + add esi, sizeof.SYS_BUTTON + + .next_button: + dec ecx + jz .not_found + + add esi, -sizeof.SYS_BUTTON + + ; does it belong to our process? + cmp dx, [esi + SYS_BUTTON.pslot] + jne .next_button + + ; does id match? + mov edi, dword[esi + SYS_BUTTON.id_hi - 2] + mov di, [esi + SYS_BUTTON.id_lo] + and edi, 0x0ffffff + cmp eax, edi + jne .next_button + + ; does coordinates match? + mov edi, dword[esi + SYS_BUTTON.left - 2] + mov di, [esi + SYS_BUTTON.top] + cmp ebx, edi + jne .next_button + + ; okay, return it + mov eax, esi + jmp .exit + + .not_found: + xor eax, eax + + .exit: + pop edi esi edx ecx + ret + +;------------------------------------------------------------------------------ +button._.dececx: ;///////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + sub cl, 0x20 + jnc @f + xor cl, cl + @@: + sub ch, 0x20 + jnc @f + xor ch, ch + @@: + rol ecx, 16 + sub cl, 0x20 + jnc @f + xor cl, cl + @@: + rol ecx, 16 + ret + +;------------------------------------------------------------------------------ +button._.incecx: ;///////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + add cl, 0x20 + jnc @f + or cl, -1 + @@: + add ch, 0x20 + jnc @f + or ch, -1 + @@: + rol ecx, 16 + add cl, 0x20 + jnc @f + or cl, -1 + @@: + rol ecx, 16 + ret + +;------------------------------------------------------------------------------ +button._.incecx2: ;//////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + add cl, 0x14 + jnc @f + or cl, -1 + @@: + add ch, 0x14 + jnc @f + or ch, -1 + @@: + rol ecx, 16 + add cl, 0x14 + jnc @f + or cl, -1 + @@: + rol ecx, 16 + ret + +;------------------------------------------------------------------------------ +button._.button_dececx: ;////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + cmp [buttontype], 1 + jne .finish + + push eax + mov al, 1 + cmp edi, 20 + jg @f + mov al, 2 + + @@: + sub cl, al + jnc @f + xor cl, cl + @@: + sub ch, al + jnc @f + xor ch, ch + @@: + rol ecx, 16 + sub cl, al + jnc @f + xor cl, cl + @@: + rol ecx, 16 + + pop eax + + .finish: + ret + +;------------------------------------------------------------------------------ +button._.negative_button: ;//////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Invert system button border +;------------------------------------------------------------------------------ + ; if requested, do not display button border on press. + test ebx, 0x20000000 + jnz .exit + + pushad + + xchg esi, eax + + movzx ecx, [esi + SYS_BUTTON.pslot] + shl ecx, 5 + add ecx, window_data + + mov eax, dword[esi + SYS_BUTTON.left] + mov ebx, dword[esi + SYS_BUTTON.top] + add eax, [ecx + WDATA.box.left] + add ebx, [ecx + WDATA.box.top] + push eax ebx + pop edx ecx + rol eax, 16 + rol ebx, 16 + add ax, cx + add bx, dx + + mov esi, 0x01000000 + call draw_rectangle.forced + + popad + + .exit: + ret diff --git a/kernel/branches/kolibri-process/gui/char.mt b/kernel/branches/kolibri-process/gui/char.mt new file mode 100644 index 000000000..1663b817d Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char.mt differ diff --git a/kernel/branches/kolibri-process/gui/char2.mt b/kernel/branches/kolibri-process/gui/char2.mt new file mode 100644 index 000000000..d27525166 Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char2.mt differ diff --git a/kernel/branches/kolibri-process/gui/char2_et.mt b/kernel/branches/kolibri-process/gui/char2_et.mt new file mode 100644 index 000000000..73993dde9 Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char2_et.mt differ diff --git a/kernel/branches/kolibri-process/gui/char2_sp.mt b/kernel/branches/kolibri-process/gui/char2_sp.mt new file mode 100644 index 000000000..d27525166 Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char2_sp.mt differ diff --git a/kernel/branches/kolibri-process/gui/char_et.mt b/kernel/branches/kolibri-process/gui/char_et.mt new file mode 100644 index 000000000..908eea231 Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char_et.mt differ diff --git a/kernel/branches/kolibri-process/gui/char_sp.mt b/kernel/branches/kolibri-process/gui/char_sp.mt new file mode 100644 index 000000000..c536d146b Binary files /dev/null and b/kernel/branches/kolibri-process/gui/char_sp.mt differ diff --git a/kernel/branches/kolibri-process/gui/event.inc b/kernel/branches/kolibri-process/gui/event.inc new file mode 100644 index 000000000..a849aafd0 --- /dev/null +++ b/kernel/branches/kolibri-process/gui/event.inc @@ -0,0 +1,616 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3565 $ + +WINDOW_MOVE_AND_RESIZE_FLAGS = \ + mouse.WINDOW_RESIZE_N_FLAG + \ + mouse.WINDOW_RESIZE_W_FLAG + \ + mouse.WINDOW_RESIZE_S_FLAG + \ + mouse.WINDOW_RESIZE_E_FLAG + \ + mouse.WINDOW_MOVE_FLAG + +uglobal +align 4 + event_start dd ? + event_end dd ? + event_uid dd 0 +endg +EV_SPACE = 512 +FreeEvents = event_start-EVENT.fd ; "виртуальный" event, используются только поля: + ; FreeEvents.fd=event_start и FreeEvents.bk=event_end +;----------------------------------------------------------------------------- +align 4 +init_events: ;; used from kernel.asm + stdcall kernel_alloc, EV_SPACE*sizeof.EVENT + or eax, eax + jz .fail + ; eax - current event, ebx - previos event below + mov ecx, EV_SPACE ; current - in allocated space + mov ebx, FreeEvents ; previos - начало списка + push ebx ; оно же и конец потом будет +;-------------------------------------- +align 4 +@@: + mov [ebx+EVENT.fd], eax + mov [eax+EVENT.bk], ebx + mov ebx, eax ; previos <- current + add eax, sizeof.EVENT ; new current + loop @b + pop eax ; вот оно концом и стало + mov [ebx+EVENT.fd], eax + mov [eax+EVENT.bk], ebx +;-------------------------------------- +align 4 +.fail: + ret +;----------------------------------------------------------------------------- +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) +;param: +; esi - event data +; ecx - flags +;retval: +; eax - event (=0 => fail) +; edx - uid +;scratched: ebx,ecx,esi,edi + mov ebx, [current_slot] + add ebx, APP_OBJ_OFFSET + mov edx, [TASK_BASE] + mov edx, [edx+TASKDATA.pid] + pushfd + cli +;-------------------------------------- +align 4 +set_event: ;; INTERNAL use !!! don't use for Call +;info: +; Берем новый 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) +; edx - pid (copied to EVENT.pid) +; esi - event data (copied to EVENT.code indirect, =0 => skip) +;retval: +; eax - event (=0 => fail) +; edx - uid +;scratched: ebx,ecx,esi,edi + mov eax, FreeEvents + cmp eax, [eax+EVENT.fd] + jne @f ; not empty ??? + pushad + call init_events + popad + jz RemoveEventTo.break ; POPF+RET +;-------------------------------------- +align 4 +@@: + mov eax, [eax+EVENT.fd] + mov [eax+EVENT.magic], 'EVNT' + mov [eax+EVENT.destroy], destroy_event.internal + mov [eax+EVENT.state], ecx + mov [eax+EVENT.pid], edx + inc [event_uid] + Mov [eax+EVENT.id],edx,[event_uid] + or esi, esi + jz RemoveEventTo + lea edi, [eax+EVENT.code] + mov ecx, (sizeof.EVENT -EVENT.code)/4 + cld + rep movsd +;-------------------------------------- +align 4 +RemoveEventTo: ;; INTERNAL use !!! don't use for Call +;param: +; 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 ; - а не дурак ли я? + 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 + mov [ebx+EVENT.fd], ecx ; OldLeft.fd=OldRight + mov [ecx+EVENT.bk], ebx ; OldRight.bk=OldLeft +;-------------------------------------- +align 4 +.break: + popfd + ret +;----------------------------------------------------------------------------- +align 4 +NotDummyTest: ;; INTERNAL use (not returned for fail !!!) + pop edi + call DummyTest ; not returned for fail !!! + mov ebx, eax + mov eax, [ebx+EVENT.pid] + push edi +;-------------------------------------- +align 4 +.small: ; криво как-то... + pop edi + pushfd + cli + call pid_to_slot ; saved all registers (eax - retval) + shl eax, 8 + jz RemoveEventTo.break ; POPF+RET + jmp edi ; штатный возврат +;----------------------------------------------------------------------------- +align 4 +raise_event: ;; EXPORT use +;info: +; Устанавливаем данные EVENT.code +; Если там флаг EVENT_SIGNALED уже активен - больше ничего +; Иначе: этот флаг взводится, за исключением случая наличия флага EVENT_WATCHED в edx +; В этом случае EVENT_SIGNALED взводится лишь при наличие EVENT_WATCHED в самом событии +;param: +; eax - event +; ebx - uid (for Dummy testing) +; edx - flags +; esi - event data (=0 => skip) +;scratched: ebx,ecx,esi,edi + call NotDummyTest ; not returned for fail !!! + or esi, esi + jz @f + lea edi, [ebx+EVENT.code] + mov ecx, (sizeof.EVENT -EVENT.code)/4 + cld + rep movsd +;-------------------------------------- +align 4 +@@: + test byte[ebx+EVENT.state+3], EVENT_SIGNALED shr 24 + jnz RemoveEventTo.break ; POPF+RET + bt edx, 28 ;EVENT_WATCHED + jnc @f + test byte[ebx+EVENT.state+3], EVENT_WATCHED shr 24 + jz RemoveEventTo.break ; POPF+RET +;-------------------------------------- +align 4 +@@: + or byte[ebx+EVENT.state+3], EVENT_SIGNALED shr 24 + add eax, SLOT_BASE+APP_EV_OFFSET + xchg eax, ebx + jmp RemoveEventTo +;----------------------------------------------------------------------------- +align 4 +clear_event: ;; EXPORT use +;info: +; +;param: +; eax - event +; ebx - uid (for Dummy testing) +;scratched: ebx,ecx + call NotDummyTest ; not returned for fail !!! + add eax, SLOT_BASE+APP_OBJ_OFFSET + and byte[ebx+EVENT.state+3], not((EVENT_SIGNALED+EVENT_WATCHED)shr 24) + xchg eax, ebx + jmp RemoveEventTo +;----------------------------------------------------------------------------- +align 4 +send_event: ;; EXPORT use +;info: +; Создает новый 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) +;retval: +; eax - event (=0 => fail) +; edx - uid +;warning: +; may be used as CDECL with such prefix... +; mov esi,[esp+8] +; mov eax,[esp+4] +; but not as STDCALL :( +;scratched: ebx,ecx,esi,edi + mov edx, eax + call NotDummyTest.small ; not returned for fail !!! + lea ebx, [eax+SLOT_BASE+APP_EV_OFFSET] + mov ecx, EVENT_SIGNALED + jmp set_event +;----------------------------------------------------------------------------- +align 4 +DummyTest: ;; INTERNAL use (not returned for fail !!!) +;param: +; eax - event +; ebx - uid (for Dummy testing) + cmp [eax+EVENT.magic], 'EVNT' + jne @f + cmp [eax+EVENT.id], ebx + je .ret +;-------------------------------------- +align 4 +@@: + pop eax + xor eax, eax +;-------------------------------------- +align 4 +.ret: + ret +;----------------------------------------------------------------------------- +align 4 +Wait_events: + or ebx, -1; infinite timeout +;-------------------------------------- +align 4 +Wait_events_ex: +;info: +; Ожидание "абстрактного" события через перевод слота в 5-ю позицию. +; Абстрактность заключена в том, что факт события определяется функцией APPDATA.wait_test, +; которая задается клиентом и может быть фактически любой. +; Это позволяет shed-у надежно определить факт события, и не совершать "холостых" переключений, +; предназначенных для разборок типа "свой/чужой" внутри задачи. +;param: +; edx - wait_test, клиентская ф-я тестирования (адрес кода) +; ecx - wait_param, дополнительный параметр, возможно необходимый для [wait_test] +; ebx - wait_timeout +;retval: +; eax - результат вызова [wait_test] (=0 => timeout) +;scratched: esi + mov esi, [current_slot] + mov [esi+APPDATA.wait_param], ecx + pushad + mov ebx, esi;пока это вопрос, чего куды сувать.......... + pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет + cli ; право рассчитывать на закрытые прерывания, как при вызове из shed + call edx + popfd + mov [esp+28], eax + popad + or eax, eax + jnz @f ;RET + mov [esi+APPDATA.wait_test], edx + mov [esi+APPDATA.wait_timeout], ebx + Mov [esi+APPDATA.wait_begin],eax,[timer_ticks] + mov eax, [TASK_BASE] + mov [eax+TASKDATA.state], 5 + call change_task + mov eax, [esi+APPDATA.wait_param] +;-------------------------------------- +align 4 +@@: + ret +;----------------------------------------------------------------------------- +align 4 +wait_event: ;; EXPORT use +;info: +; Ожидание флага 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) +;scratched: ecx,edx,esi + call DummyTest + mov ecx, eax ; wait_param + mov edx, get_event_alone ; wait_test + call Wait_events ; timeout ignored + jmp wait_finish +;----------------------------------------------------------------------------- +align 4 +wait_event_timeout: +;param: +; eax - event +; ebx - uid (for Dummy testing) +; ecx - timeout in timer ticks +;retval: +; eax - EVENT handle or 0 if timeout + call DummyTest + mov ebx, ecx + mov ecx, eax ; wait_param + mov edx, get_event_alone ; wait_test + call Wait_events_ex + test eax, eax + jnz wait_finish + ret +;----------------------------------------------------------------------------- +align 4 +get_event_ex: ;; f68:14 +;info: +; Ожидание любого события в очереди EventList текущего слота +; Данные события code - копируются в память приложения (косвенно по edi) +; При активном флаге MANUAL_RESET - больше ничего +; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются, +; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота, +; а при не активном - уничтожается штатно (destroy_event.internal) +;param: +; edi - адрес в коде приложения для копирования данных из EVENT.code +;retval: +; eax - собственно EVENT (будем называть это его хэндлом) +;scratched: ebx,ecx,edx,esi,edi + mov edx, get_event_queue ; wait_test + call Wait_events ; timeout ignored + lea esi, [eax+EVENT.code] + mov ecx, (sizeof.EVENT-EVENT.code)/4 + cld + rep movsd + mov byte[edi-(sizeof.EVENT-EVENT.code)+2], cl;clear priority field +;-------------------------------------- +align 4 +wait_finish: + test byte[eax+EVENT.state+3], MANUAL_RESET shr 24 + jnz get_event_queue.ret ; RET + and byte[eax+EVENT.state+3], not((EVENT_SIGNALED+EVENT_WATCHED)shr 24) + test byte[eax+EVENT.state+3], MANUAL_DESTROY shr 24 + jz destroy_event.internal + mov ebx, [current_slot] + add ebx, APP_OBJ_OFFSET + pushfd + cli + jmp RemoveEventTo +;----------------------------------------------------------------------------- +align 4 +destroy_event: ;; EXPORT use +;info: +; Переносим EVENT в список FreeEvents, чистим поля magic,destroy,pid,id +;param: +; eax - event +; ebx - uid (for Dummy testing) +;retval: +; eax - адрес объекта EVENT (=0 => fail) +;scratched: ebx,ecx + call DummyTest ; not returned for fail !!! +;-------------------------------------- +align 4 +.internal: + xor ecx, ecx ; clear common header + pushfd + cli + mov [eax+EVENT.magic], ecx + mov [eax+EVENT.destroy], ecx + mov [eax+EVENT.pid], ecx + mov [eax+EVENT.id], ecx + mov ebx, FreeEvents + jmp RemoveEventTo +;----------------------------------------------------------------------------- +align 4 +get_event_queue: +;info: +; клиентская ф-я тестирования для 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 слота тестирования +;retval: +; eax - адрес объекта EVENT (=0 => fail) + add ebx, APP_EV_OFFSET + mov eax, [ebx+APPOBJ.bk] ; выбираем с конца, по принципу FIFO + cmp eax, ebx ; empty ??? + je get_event_alone.ret0 +;-------------------------------------- +align 4 +.ret: + ret +;----------------------------------------------------------------------------- +align 4 +get_event_alone: +;info: +; клиентская ф-я тестирования для 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 слота тестирования +;retval: +; eax - адрес объекта EVENT (=0 => fail) + mov eax, [ebx+APPDATA.wait_param] + test byte[eax+EVENT.state+3], EVENT_SIGNALED shr 24 + jnz .ret + or byte[eax+EVENT.state+3], EVENT_WATCHED shr 24 +;-------------------------------------- +align 4 +.ret0: + xor eax, eax; NO event!!! +;-------------------------------------- +align 4 +.ret: + ret +;----------------------------------------------------------------------------- +align 4 +sys_sendwindowmsg: ;; f72 + dec ebx + jnz .ret ;subfunction==1 ? + pushfd + cli + sub ecx, 2 + je .sendkey + dec ecx + jnz .retf +;-------------------------------------- +align 4 +.sendbtn: + cmp byte[BTN_COUNT], 1 + jae .result ;overflow + inc byte[BTN_COUNT] + shl edx, 8 + mov [BTN_BUFF], edx + jmp .result +;-------------------------------------- +align 4 +.sendkey: + movzx eax, byte[KEY_COUNT] + cmp al, 120 + jae .result ;overflow + inc byte[KEY_COUNT] + mov [KEY_COUNT+1+eax], dl +;-------------------------------------- +align 4 +.result: + setae byte[esp+32+4] ;считаем, что исходно: dword[esp+32+4]==72 +;-------------------------------------- +align 4 +.retf: + popfd +;-------------------------------------- +align 4 +.ret: + ret +;----------------------------------------------------------------------------- +align 4 +sys_getevent: ;; f11 + mov ebx, [current_slot];пока это вопрос, чего куды сувать.......... + pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет + cli ; право рассчитывать на закрытые прерывания, как при вызове из shed + call get_event_for_app + popfd + mov [esp+32], eax + ret +;----------------------------------------------------------------------------- +align 4 +sys_waitforevent: ;; f10 + or ebx, -1; infinite timeout +;-------------------------------------- +align 4 +sys_wait_event_timeout: ;; f23 + call unprotect_from_terminate + mov edx, get_event_for_app; wait_test + call Wait_events_ex ; ebx - timeout + mov [esp+32], eax + call protect_from_terminate + ret +;----------------------------------------------------------------------------- +align 4 +get_event_for_app: ;; used from f10,f11,f23 +;info: +; клиентская ф-я тестирования для приложений (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 слота тестирования +;retval: +; 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] + mov ecx, [edi+TASKDATA.event_mask] + and ecx, 0x7FFFFFFF +;-------------------------------------- +align 4 +.loop: ; пока не исчерпаем все биты маски + bsr eax, ecx ; находим ненулевой бит маски (31 -> 0) + jz .no_events ; исчерпали все биты маски, но ничего не нашли ??? + btr ecx, eax ; сбрасываем проверяемый бит маски + ; переходим на обработчик этого (eax) бита + cmp eax, 10 + jae .loop ; eax=[10..31], ignored (event 11...32) + + cmp eax, 3 + je .loop ; eax=3, ignored (event 4) + + cmp eax, 4 + je .FlagAutoReset ; eax=4, retvals=eax+1 (event 5) + + cmp eax, 5 + je .mouse_check ; eax=5, retvals=eax+1 (event 6) + + 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) +;-------------------------------------- +align 4 +.WndRedraw: ; eax=0, retval WndRedraw=1 + cmp [edi-twdw+WDATA.fl_redraw], al;al==0 + jne .result + jmp .loop +;-------------------------------------- +align 4 +.no_events: + xor eax, eax + ret +;-------------------------------------- +align 4 +.mouse_check: ; Mouse 5+1=6 + push eax + mov eax, [TASK_BASE] + mov eax, [eax + TASKDATA.event_mask] + test eax, 0x80000000 ; bit 31: active/inactive filter f.40 + jz @f + pop eax + jmp .FlagAutoReset +;-------------------------------------- +align 4 +@@: +; If the window is captured and moved by the user, then no mouse events!!! + mov al, [mouse.active_sys_window.action] + and al, WINDOW_MOVE_AND_RESIZE_FLAGS + test al, al + pop eax + jnz .loop +;-------------------------------------- +align 4 +.FlagAutoReset: ; retvals: BgrRedraw=5, IPC=7, Stack=8, Debug=9 + btr [ebx+APPDATA.event_mask], eax + jnc .loop +;-------------------------------------- +align 4 +.result: ; retval = eax+1 + inc eax + ret +;-------------------------------------- +align 4 +.BtKy: + movzx edx, bh + movzx edx, word[WIN_STACK+edx*2] + je .Keys ; eax=1, retval Keys=2 +;-------------------------------------- +align 4 +.Buttons: ; eax=2, retval Buttons=3 + cmp byte[BTN_COUNT], 0 + je .loop ; empty ??? + cmp edx, [TASK_COUNT] + jne .loop ; not Top ??? + mov edx, [BTN_BUFF] + shr edx, 8 + cmp edx, 0xFFFF ;-ID for Minimize-Button of Form + jne .result + mov [window_minimize], 1 + call wakeup_osloop + dec byte[BTN_COUNT] + jmp .loop +;-------------------------------------- +align 4 +.Keys: ; eax==1 + cmp edx, [TASK_COUNT] + jne @f ; not Top ??? + cmp [KEY_COUNT], al; al==1 + jae .result ; not empty ??? +;-------------------------------------- +align 4 +@@: + mov edx, hotkey_buffer +;-------------------------------------- +align 4 +@@: + cmp [edx], bh ; bh - slot for testing + je .result + add edx, 8 + cmp edx, hotkey_buffer+120*8 + jb @b + jmp .loop +;end. +;----------------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-process/gui/font.inc b/kernel/branches/kolibri-process/gui/font.inc new file mode 100644 index 000000000..9374ce61f --- /dev/null +++ b/kernel/branches/kolibri-process/gui/font.inc @@ -0,0 +1,251 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3927 $ + +;------------------------------------------------------------------------------ +align 4 +dtext_asciiz_esi: ; for skins title out + push eax + xor eax, eax + inc eax + jmp dtext.1 +;------------------------------------------------------------------------------ +align 4 +dtext: +; ebx x & y +; ecx style ( 0xX0000000 ) & color ( 0x00RRGGBB ) +; X = ABnnb: +; nn = font +; A = 0 <=> output esi characters; otherwise output ASCIIZ string +; B = 1 <=> fill background with color eax +; edx start of text +; edi 1 force or user area for redirect + push eax + xor eax, eax +;-------------------------------------- +align 4 +.1: + pushad + movsx eax, bx ; eax=y + sar ebx, 16 ; ebx=x + xchg eax, ebx ; eax=x, ebx=y + cmp esi, 255 + jb .loop + + mov esi, 255 +;-------------------------------------- +align 4 +.loop: + test ecx, ecx + js .test_asciiz + + dec esi + js .end + + jmp @f +;-------------------------------------- +align 4 +.test_asciiz: + cmp byte [edx], 0 + jz .end + + cmp byte [esp+28], 1 + jne @f + + dec esi + js .end +;-------------------------------------- +align 4 +@@: + inc edx + pushad + movzx edx, byte [edx-1] + test ecx, 0x10000000 + jnz .font2 + + mov esi, 9 + lea ebp, [FONT_I+8*edx+edx] +;-------------------------------------- +align 4 +.symloop1: + mov dl, byte [ebp] + or dl, 1 shl 6 +;-------------------------------------- +align 4 +.pixloop1: + shr dl, 1 + jz .pixloop1end + + jnc .nopix + + test ecx, 0x08000000 ; redirect the output to the user area + jz @f + + call draw_text_to_user_area + jmp .pixloop1cont +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel + jmp .pixloop1cont +;-------------------------------------- +align 4 +.nopix: + test ecx, 0x40000000 + jz .pixloop1cont + + push ecx + mov ecx, [esp+4+20h+20h] + + test ecx, 0x08000000 ; redirect the output to the user area + jz @f + + call draw_text_to_user_area + pop ecx + jmp .pixloop1cont +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel + pop ecx +;-------------------------------------- +align 4 +.pixloop1cont: + inc eax + jmp .pixloop1 +;-------------------------------------- +align 4 +.pixloop1end: + sub eax, 6 + inc ebx + inc ebp + dec esi + jnz .symloop1 + + popad + add eax, 6 + jmp .loop +;-------------------------------------- +align 4 +.font2: + add edx, edx + lea ebp, [FONT_II+4*edx+edx+1] + push 9 + movzx esi, byte [ebp-1] +;-------------------------------------- +align 4 +.symloop2: + mov dl, byte [ebp] + push esi +;-------------------------------------- +align 4 +.pixloop2: + shr dl, 1 + jnc .nopix2 + + test ecx, 0x08000000 ; redirect the output to the user area + jz @f + + call draw_text_to_user_area + jmp .pixloop2cont +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel + jmp .pixloop2cont +;-------------------------------------- +align 4 +.nopix2: + test ecx, 0x40000000 + jz .pixloop2cont + + push ecx + mov ecx, [esp+12+20h+20h] + + test ecx, 0x08000000 ; redirect the output to the user area + jz @f + + call draw_text_to_user_area + pop ecx + jmp .pixloop2cont +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel + pop ecx +;-------------------------------------- +align 4 +.pixloop2cont: + inc eax + dec esi + jnz .pixloop2 + + pop esi + sub eax, esi + inc ebx + inc ebp + dec dword [esp] + jnz .symloop2 + + pop eax + add dword [esp+28], esi + popad + jmp .loop +;-------------------------------------- +align 4 +.end: + popad + pop eax + ret +;------------------------------------------------------------------------------ +; eax = x coordinate +; ebx = y coordinate +; ecx = ?? RR GG BB +; edi = user area +align 4 +draw_text_to_user_area: + pushad + imul ebx, [edi+0] + add eax, ebx + shl eax, 2 + add eax, edi + add eax, 8 + and ecx, 0xffffff + or ecx, 0xff000000 ; not transparent + mov [eax], ecx ; store pixel + popad + ret +;------------------------------------------------------------------------------ +align 4 +FONT_I: + if lang eq sp + file 'char_sp.mt' + else if lang eq et + file 'char_et.mt' + else + file 'char.mt' + end if +;------------------------------------------------------------------------------ +align 4 +FONT_II: + if lang eq sp + file 'char2_sp.mt' + else if lang eq et + file 'char2_et.mt' + else + file 'char2.mt' + end if +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-process/gui/mouse.inc b/kernel/branches/kolibri-process/gui/mouse.inc new file mode 100644 index 000000000..036c03683 --- /dev/null +++ b/kernel/branches/kolibri-process/gui/mouse.inc @@ -0,0 +1,711 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2010-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4424 $ + +include 'mousepointer.inc' + +;============================================================================== +;///// public functions /////////////////////////////////////////////////////// +;============================================================================== + +mouse.LEFT_BUTTON_FLAG = 0001b +mouse.RIGHT_BUTTON_FLAG = 0010b +mouse.MIDDLE_BUTTON_FLAG = 0100b + +mouse.BUTTONS_MASK = \ + mouse.LEFT_BUTTON_FLAG or \ + mouse.RIGHT_BUTTON_FLAG or \ + mouse.MIDDLE_BUTTON_FLAG + +mouse.WINDOW_RESIZE_N_FLAG = 000001b +mouse.WINDOW_RESIZE_W_FLAG = 000010b +mouse.WINDOW_RESIZE_S_FLAG = 000100b +mouse.WINDOW_RESIZE_E_FLAG = 001000b +mouse.WINDOW_MOVE_FLAG = 010000b + +mouse.WINDOW_RESIZE_SW_FLAG = \ + mouse.WINDOW_RESIZE_S_FLAG or \ + mouse.WINDOW_RESIZE_W_FLAG +mouse.WINDOW_RESIZE_SE_FLAG = \ + mouse.WINDOW_RESIZE_S_FLAG or \ + mouse.WINDOW_RESIZE_E_FLAG + +align 4 +;------------------------------------------------------------------------------ +mouse_check_events: ;////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Check if mouse buttons state or cursor position has changed and call +;? appropriate handlers +;------------------------------------------------------------------------------ + push eax ebx + + mov al, [BTN_DOWN] + mov bl, [mouse.state.buttons] + and al, mouse.BUTTONS_MASK + mov cl, al + xchg cl, [mouse.state.buttons] + xor bl, al + push eax ebx + + ; did any mouse button changed its state? + or bl, bl + jz .check_position + + ; yes it did, is that the first button of all pressed down? + or cl, cl + jnz .check_buttons_released + + ; yes it is, activate window user is pointing at, if needed + call mouse._.activate_sys_window_under_cursor + + ; NOTE: this code wouldn't be necessary if we knew window did + ; already redraw itself after call above + or eax, eax + jnz .exit + + ; is there any system button under cursor? + call mouse._.find_sys_button_under_cursor + or eax, eax + jz .check_buttons_released + + ; yes there is, activate it and exit + mov [mouse.active_sys_button.pbid], eax + mov [mouse.active_sys_button.coord], ebx + mov cl, [mouse.state.buttons] + mov [mouse.active_sys_button.buttons], cl + call sys_button_activate_handler + jmp .exit + + .check_buttons_released: + cmp [mouse.state.buttons], 0 + jnz .buttons_changed + + ; did we press some button earlier? + cmp [mouse.active_sys_button.pbid], 0 + je .buttons_changed + + ; yes we did, deactivate it + xor eax, eax + xchg eax, [mouse.active_sys_button.pbid] + mov ebx, [mouse.active_sys_button.coord] + mov cl, [mouse.active_sys_button.buttons] + push eax ebx + call sys_button_deactivate_handler + pop edx ecx + + ; is the button under cursor the one we deactivated? + call mouse._.find_sys_button_under_cursor + cmp eax, ecx + jne .exit + cmp ebx, edx + jne .exit + + ; yes it is, perform associated action + mov cl, [mouse.active_sys_button.buttons] + call sys_button_perform_handler + jmp .exit + + .buttons_changed: + test byte[esp], mouse.LEFT_BUTTON_FLAG + jz @f + mov eax, [esp + 4] + call .call_left_button_handler + + @@: + test byte[esp], mouse.RIGHT_BUTTON_FLAG + jz @f + mov eax, [esp + 4] + call .call_right_button_handler + + @@: + test byte[esp], mouse.MIDDLE_BUTTON_FLAG + jz .check_position + mov eax, [esp + 4] + call .call_middle_button_handler + + .check_position: + movzx eax, word[MOUSE_X] + movzx ebx, word[MOUSE_Y] + cmp eax, [mouse.state.pos.x] + jne .position_changed + cmp ebx, [mouse.state.pos.y] + je .exit + + .position_changed: + xchg eax, [mouse.state.pos.x] + xchg ebx, [mouse.state.pos.y] + + call mouse._.move_handler + + .exit: + add esp, 8 + pop ebx eax + ret + + .call_left_button_handler: + test eax, mouse.LEFT_BUTTON_FLAG + jnz mouse._.left_button_press_handler + jmp mouse._.left_button_release_handler + + .call_right_button_handler: + test eax, mouse.RIGHT_BUTTON_FLAG + jnz mouse._.right_button_press_handler + jmp mouse._.right_button_release_handler + + .call_middle_button_handler: + test eax, mouse.MIDDLE_BUTTON_FLAG + jnz mouse._.middle_button_press_handler + jmp mouse._.middle_button_release_handler + +;============================================================================== +;///// private functions ////////////////////////////////////////////////////// +;============================================================================== + +uglobal + mouse.state: + .pos POINT + .buttons db ? + + ; NOTE: since there's no unique and lifetime-constant button identifiers, + ; we're using two dwords to identify each of them: + ; * pbid - process slot (high 8 bits) and button id (low 24 bits) pack + ; * coord - left (high 16 bits) and top (low 16 bits) coordinates pack + align 4 + mouse.active_sys_button: + .pbid dd ? + .coord dd ? + .buttons db ? + + align 4 + mouse.active_sys_window: + .pslot dd ? + .old_box BOX + .new_box BOX + .delta POINT + .last_ticks dd ? + .action db ? +endg + +align 4 +;------------------------------------------------------------------------------ +mouse._.left_button_press_handler: ;/////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when left mouse button has been pressed down +;------------------------------------------------------------------------------ + test [mouse.state.buttons], not mouse.LEFT_BUTTON_FLAG + jnz .exit + + call mouse._.find_sys_window_under_cursor + call mouse._.check_sys_window_actions + mov [mouse.active_sys_window.action], al + or eax, eax + jz .exit + + xchg eax, edx + test dl, mouse.WINDOW_MOVE_FLAG + jz @f + + mov eax, [timer_ticks] + mov ebx, eax + xchg ebx, [mouse.active_sys_window.last_ticks] + sub eax, ebx + cmp eax, 50 + jg @f + + mov [mouse.active_sys_window.last_ticks], 0 + call sys_window_maximize_handler + jmp .exit + + @@: + test [edi + WDATA.fl_wstate], WSTATE_MAXIMIZED + jnz .exit + mov [mouse.active_sys_window.pslot], esi + lea eax, [edi + WDATA.box] + mov ebx, mouse.active_sys_window.old_box + mov ecx, sizeof.BOX + call memmove + mov ebx, mouse.active_sys_window.new_box + call memmove + test edx, mouse.WINDOW_MOVE_FLAG + jz @f + + call .calculate_n_delta + call .calculate_w_delta + jmp .call_window_handler + + @@: + test dl, mouse.WINDOW_RESIZE_W_FLAG + jz @f + call .calculate_w_delta + + @@: + test dl, mouse.WINDOW_RESIZE_S_FLAG + jz @f + call .calculate_s_delta + + @@: + test dl, mouse.WINDOW_RESIZE_E_FLAG + jz .call_window_handler + call .calculate_e_delta + + .call_window_handler: +; mov eax, mouse.active_sys_window.old_box +; call sys_window_start_moving_handler + + .exit: + ret + + .calculate_n_delta: + mov eax, [mouse.state.pos.y] + sub eax, [mouse.active_sys_window.old_box.top] + mov [mouse.active_sys_window.delta.y], eax + ret + + .calculate_w_delta: + mov eax, [mouse.state.pos.x] + sub eax, [mouse.active_sys_window.old_box.left] + mov [mouse.active_sys_window.delta.x], eax + ret + + .calculate_s_delta: + mov eax, [mouse.active_sys_window.old_box.top] + add eax, [mouse.active_sys_window.old_box.height] + sub eax, [mouse.state.pos.y] + mov [mouse.active_sys_window.delta.y], eax + ret + + .calculate_e_delta: + mov eax, [mouse.active_sys_window.old_box.left] + add eax, [mouse.active_sys_window.old_box.width] + sub eax, [mouse.state.pos.x] + mov [mouse.active_sys_window.delta.x], eax + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.left_button_release_handler: ;///////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when left mouse button has been released +;------------------------------------------------------------------------------ + xor esi, esi + xchg esi, [mouse.active_sys_window.pslot] + or esi, esi + jz .exit + + mov eax, esi + shl eax, 5 + add eax, window_data + WDATA.box + mov ebx, mouse.active_sys_window.old_box + mov ecx, sizeof.BOX + call memmove + + mov eax, mouse.active_sys_window.old_box + mov ebx, mouse.active_sys_window.new_box + call sys_window_end_moving_handler + + .exit: + and [mouse.active_sys_window.action], 0 + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.right_button_press_handler: ;////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when right mouse button has been pressed down +;------------------------------------------------------------------------------ + test [mouse.state.buttons], not mouse.RIGHT_BUTTON_FLAG + jnz .exit + + call mouse._.find_sys_window_under_cursor + call mouse._.check_sys_window_actions + test al, mouse.WINDOW_MOVE_FLAG + jz .exit + + call sys_window_rollup_handler + + .exit: + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.right_button_release_handler: ;//////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when right mouse button has been released +;------------------------------------------------------------------------------ + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.middle_button_press_handler: ;///////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when middle mouse button has been pressed down +;------------------------------------------------------------------------------ + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.middle_button_release_handler: ;/////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when middle mouse button has been released +;------------------------------------------------------------------------------ + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.move_handler: ;//////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Called when cursor has been moved +;------------------------------------------------------------------------------ +;> eax = old x coord +;> ebx = old y coord +;------------------------------------------------------------------------------ + cmp [mouse.active_sys_button.pbid], 0 + jnz .exit + + mov esi, [mouse.active_sys_window.pslot] + or esi, esi + jz .exit + + mov eax, mouse.active_sys_window.new_box + mov ebx, mouse.active_sys_window.old_box + mov ecx, sizeof.BOX + call memmove + + mov dl, [mouse.active_sys_window.action] + test dl, mouse.WINDOW_MOVE_FLAG + jz .check_resize_w + + mov eax, [mouse.state.pos.x] + sub eax, [mouse.active_sys_window.delta.x] + mov [mouse.active_sys_window.new_box.left], eax + mov eax, [mouse.state.pos.y] + sub eax, [mouse.active_sys_window.delta.y] + mov [mouse.active_sys_window.new_box.top], eax + + mov eax, [mouse.active_sys_window.new_box.left] + or eax, eax + jge @f + xor eax, eax + mov [mouse.active_sys_window.new_box.left], eax + @@: + add eax, [mouse.active_sys_window.new_box.width] + cmp eax, [Screen_Max_X] + jl @f + sub eax, [Screen_Max_X] + sub [mouse.active_sys_window.new_box.left], eax + @@: + mov eax, [mouse.active_sys_window.new_box.top] + or eax, eax + jge @f + xor eax, eax + mov [mouse.active_sys_window.new_box.top], eax + @@: + add eax, [mouse.active_sys_window.new_box.height] + cmp eax, [Screen_Max_Y] + jle .call_window_handler + sub eax, [Screen_Max_Y] + sub [mouse.active_sys_window.new_box.top], eax + jmp .call_window_handler + + .check_resize_w: + test dl, mouse.WINDOW_RESIZE_W_FLAG + jz .check_resize_s + + mov eax, [mouse.state.pos.x] + sub eax, [mouse.active_sys_window.delta.x] + mov [mouse.active_sys_window.new_box.left], eax + sub eax, [mouse.active_sys_window.old_box.left] + sub [mouse.active_sys_window.new_box.width], eax + + mov eax, [mouse.active_sys_window.new_box.width] + sub eax, 127 + jge @f + add [mouse.active_sys_window.new_box.left], eax + mov [mouse.active_sys_window.new_box.width], 127 + @@: + mov eax, [mouse.active_sys_window.new_box.left] + or eax, eax + jge .check_resize_s + add [mouse.active_sys_window.new_box.width], eax + xor eax, eax + mov [mouse.active_sys_window.new_box.left], eax + + .check_resize_s: + test dl, mouse.WINDOW_RESIZE_S_FLAG + jz .check_resize_e + + mov eax, [mouse.state.pos.y] + add eax, [mouse.active_sys_window.delta.y] + sub eax, [mouse.active_sys_window.old_box.top] + mov [mouse.active_sys_window.new_box.height], eax + + push eax + mov edi, esi + shl edi, 5 + add edi, window_data + call window._.get_rolledup_height + mov ecx, eax + pop eax + mov eax, [mouse.active_sys_window.new_box.height] + cmp eax, ecx + jge @f + mov eax, ecx + mov [mouse.active_sys_window.new_box.height], eax + @@: + add eax, [mouse.active_sys_window.new_box.top] + cmp eax, [Screen_Max_Y] + jle .check_resize_e + sub eax, [Screen_Max_Y] + neg eax + add [mouse.active_sys_window.new_box.height], eax + mov ecx, [Screen_Max_Y] + cmp ecx, eax + jge .check_resize_e + mov [mouse.active_sys_window.new_box.height], ecx + + .check_resize_e: + test dl, mouse.WINDOW_RESIZE_E_FLAG + jz .call_window_handler + + mov eax, [mouse.state.pos.x] + add eax, [mouse.active_sys_window.delta.x] + sub eax, [mouse.active_sys_window.old_box.left] + mov [mouse.active_sys_window.new_box.width], eax + + mov eax, [mouse.active_sys_window.new_box.width] + cmp eax, 127 + jge @f + mov eax, 127 + mov [mouse.active_sys_window.new_box.width], eax + @@: + add eax, [mouse.active_sys_window.new_box.left] + cmp eax, [Screen_Max_X] + jle .call_window_handler + sub eax, [Screen_Max_X] + neg eax + add [mouse.active_sys_window.new_box.width], eax + mov ecx, [Screen_Max_X] + cmp ecx, eax + jge .call_window_handler + mov [mouse.active_sys_window.new_box.width], ecx + + .call_window_handler: + mov eax, mouse.active_sys_window.old_box + mov ebx, mouse.active_sys_window.new_box + + push esi + mov esi, mouse.active_sys_window.old_box + mov edi, mouse.active_sys_window.new_box + mov ecx, sizeof.BOX / 4 + repe + cmpsd + pop esi + je .exit + + mov [mouse.active_sys_window.last_ticks], 0 + call sys_window_moving_handler + + .exit: + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.find_sys_window_under_cursor: ;//////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Find system window object which is currently visible on screen and has +;? mouse cursor within its bounds +;------------------------------------------------------------------------------ +;< esi = process slot +;< edi = pointer to WDATA struct +;------------------------------------------------------------------------------ + mov esi, [mouse.state.pos.y] + mov esi, [d_width_calc_area + esi*4] + + add esi, [_WinMapAddress] + add esi, [mouse.state.pos.x] + movzx esi, byte[esi] + mov edi, esi + shl edi, 5 + add edi, window_data + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.activate_sys_window_under_cursor: ;//////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + ; activate and redraw window under cursor (if necessary) + call mouse._.find_sys_window_under_cursor + movzx esi, word[WIN_STACK + esi * 2] + lea esi, [WIN_POS + esi * 2] + jmp waredraw + +align 4 +;------------------------------------------------------------------------------ +mouse._.find_sys_button_under_cursor: ;//////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Find system button object which is currently visible on screen and has +;? mouse cursor within its bounds +;------------------------------------------------------------------------------ +;< eax = pack[8(process slot), 24(button id)] or 0 +;< ebx = pack[16(button x coord), 16(button y coord)] +;------------------------------------------------------------------------------ + push ecx edx esi edi + + call mouse._.find_sys_window_under_cursor + mov edx, esi + + ; check if any process button contains cursor + mov eax, [BTN_ADDR] + mov ecx, [eax] + imul esi, ecx, sizeof.SYS_BUTTON + add esi, eax + inc ecx + add esi, sizeof.SYS_BUTTON + + .next_button: + dec ecx + jz .not_found + + add esi, -sizeof.SYS_BUTTON + + ; does it belong to our process? + cmp dx, [esi + SYS_BUTTON.pslot] + jne .next_button + + ; does it contain cursor coordinates? + mov eax, [mouse.state.pos.x] + sub eax, [edi + WDATA.box.left] + sub ax, [esi + SYS_BUTTON.left] + jl .next_button + sub ax, [esi + SYS_BUTTON.width] + jge .next_button + mov eax, [mouse.state.pos.y] + sub eax, [edi + WDATA.box.top] + sub ax, [esi + SYS_BUTTON.top] + jl .next_button + sub ax, [esi + SYS_BUTTON.height] + jge .next_button + + ; okay, return it + shl edx, 24 + mov eax, dword[esi + SYS_BUTTON.id_hi - 2] + mov ax, [esi + SYS_BUTTON.id_lo] + and eax, 0x0ffffff + or eax, edx + mov ebx, dword[esi + SYS_BUTTON.left - 2] + mov bx, [esi + SYS_BUTTON.top] + jmp .exit + + .not_found: + xor eax, eax + xor ebx, ebx + + .exit: + pop edi esi edx ecx + ret + +align 4 +;------------------------------------------------------------------------------ +mouse._.check_sys_window_actions: ;//////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;< eax = action flags or 0 +;------------------------------------------------------------------------------ + ; is window movable? + test byte[edi + WDATA.cl_titlebar + 3], 0x01 + jnz .no_action + + mov eax, [mouse.state.pos.x] + mov ebx, [mouse.state.pos.y] + sub eax, [edi + WDATA.box.left] + sub ebx, [edi + WDATA.box.top] + + ; is there a window titlebar under cursor? + push eax + call window._.get_titlebar_height + cmp ebx, eax + pop eax + jl .move_action + + ; no there isn't, can it be resized then? + mov dl, [edi + WDATA.fl_wstyle] + and dl, 0x0f + ; NOTE: dangerous optimization, revise if window types changed; + ; this currently implies only types 2 and 3 could be resized + test dl, 2 + jz .no_action + + mov ecx, [edi + WDATA.box.width] + add ecx, -window.BORDER_SIZE + mov edx, [edi + WDATA.box.height] + add edx, -window.BORDER_SIZE + + ; is it rolled up? + test [edi + WDATA.fl_wstate], WSTATE_ROLLEDUP + jnz .resize_w_or_e_action + + cmp eax, window.BORDER_SIZE + jl .resize_w_action + cmp eax, ecx + jg .resize_e_action + cmp ebx, edx + jle .no_action + + .resize_s_action: + cmp eax, window.BORDER_SIZE + 10 + jl .resize_sw_action + add ecx, -10 + cmp eax, ecx + jge .resize_se_action + mov eax, mouse.WINDOW_RESIZE_S_FLAG + jmp .exit + + .resize_w_or_e_action: + cmp eax, window.BORDER_SIZE + 10 + jl .resize_w_action.direct + add ecx, -10 + cmp eax, ecx + jg .resize_e_action.direct + jmp .no_action + + .resize_w_action: + add edx, -10 + cmp ebx, edx + jge .resize_sw_action + .resize_w_action.direct: + mov eax, mouse.WINDOW_RESIZE_W_FLAG + jmp .exit + + .resize_e_action: + add edx, -10 + cmp ebx, edx + jge .resize_se_action + .resize_e_action.direct: + mov eax, mouse.WINDOW_RESIZE_E_FLAG + jmp .exit + + .resize_sw_action: + mov eax, mouse.WINDOW_RESIZE_SW_FLAG + jmp .exit + + .resize_se_action: + mov eax, mouse.WINDOW_RESIZE_SE_FLAG + jmp .exit + + .move_action: + mov eax, mouse.WINDOW_MOVE_FLAG + jmp .exit + + .no_action: + xor eax, eax + + .exit: + ret diff --git a/kernel/branches/kolibri-process/gui/mousepointer.inc b/kernel/branches/kolibri-process/gui/mousepointer.inc new file mode 100644 index 000000000..2d9eac0d2 --- /dev/null +++ b/kernel/branches/kolibri-process/gui/mousepointer.inc @@ -0,0 +1,250 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +iglobal + +align 4 +mousepointer: +db 0x00,0x00,0x00,0x74,0x74,0x74,0x6e,0x6e,0x6e,0x6f +db 0x6f,0x6f,0x71,0x71,0x71,0x75,0x75,0x75,0x79,0x79 +db 0x79,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63 +db 0x66,0x66,0x66,0x6c,0x6c,0x6c,0x72,0x72,0x72,0x78 +db 0x78,0x78,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0 +db 0xc0,0xc0,0x00,0x00,0x00,0x54,0x54,0x54,0x57,0x57 +db 0x57,0x5f,0x5f,0x5f,0x68,0x68,0x68,0x71,0x71,0x71 +db 0x77,0x77,0x77,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0x00,0x00,0x00,0x47,0x47,0x47,0x50 +db 0x50,0x50,0x5b,0x5b,0x5b,0x67,0x67,0x67,0x70,0x70 +db 0x70,0x77,0x77,0x77,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0xff,0xff,0xff,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x3f,0x3f,0x3f +db 0x4b,0x4b,0x4b,0x59,0x59,0x59,0x66,0x66,0x66,0x70 +db 0x70,0x70,0x77,0x77,0x77,0x7c,0x7c,0x7c,0x7e,0x7e +db 0x7e,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x3a,0x3a +db 0x3a,0x49,0x49,0x49,0x59,0x59,0x59,0x66,0x66,0x66 +db 0x70,0x70,0x70,0x77,0x77,0x77,0x7c,0x7c,0x7c,0x7e +db 0x7e,0x7e,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x39 +db 0x39,0x39,0x49,0x49,0x49,0x59,0x59,0x59,0x66,0x66 +db 0x66,0x71,0x71,0x71,0x78,0x78,0x78,0x7c,0x7c,0x7c +db 0x7e,0x7e,0x7e,0x80,0x80,0x80,0x80,0x80,0x80,0xff +db 0xff,0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00 +db 0x39,0x39,0x39,0x4a,0x4a,0x4a,0x5a,0x5a,0x5a,0x68 +db 0x68,0x68,0x72,0x72,0x72,0x79,0x79,0x79,0x7d,0x7d +db 0x7d,0x7f,0x7f,0x7f,0x80,0x80,0x80,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00 +db 0x00,0x3c,0x3c,0x3c,0x4e,0x4e,0x4e,0x5e,0x5e,0x5e +db 0x6b,0x6b,0x6b,0x75,0x75,0x75,0x7a,0x7a,0x7a,0x7e +db 0x7e,0x7e,0x80,0x80,0x80,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00 +db 0x00,0x00,0x43,0x43,0x43,0x55,0x55,0x55,0x64,0x64 +db 0x64,0x70,0x70,0x70,0x78,0x78,0x78,0x7d,0x7d,0x7d +db 0x80,0x80,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xc0 +db 0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x4e,0x4e,0x4e,0x5f,0x5f,0x5f,0x6d +db 0x6d,0x6d,0x76,0x76,0x76,0x7c,0x7c,0x7c,0x80,0x80 +db 0x80,0xff,0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00 +db 0xff,0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x14 +db 0x14,0x14,0x1b,0x1b,0x1b,0x29,0x29,0x29,0x3a,0x3a +db 0x3a,0x4c,0x4c,0x4c,0x5d,0x5d,0x5d,0x6c,0x6c,0x6c +db 0x75,0x75,0x75,0x7b,0x7b,0x7b,0x80,0x80,0x80,0xc0 +db 0xc0,0xc0,0x00,0x00,0x00,0x2f,0x2f,0x2f,0x80,0x80 +db 0x80,0xff,0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00 +db 0x21,0x21,0x21,0x2e,0x2e,0x2e,0x40,0x40,0x40,0x52 +db 0x52,0x52,0x62,0x62,0x62,0x6f,0x6f,0x6f,0x77,0x77 +db 0x77,0x7c,0x7c,0x7c,0x80,0x80,0x80,0x00,0x00,0x00 +db 0x47,0x47,0x47,0x3b,0x3b,0x3b,0x80,0x80,0x80,0xff +db 0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x25,0x25 +db 0x25,0x30,0x30,0x30,0x42,0x42,0x42,0x54,0x54,0x54 +db 0x64,0x64,0x64,0x70,0x70,0x70,0x78,0x78,0x78,0x7d +db 0x7d,0x7d,0x00,0x00,0x00,0x62,0x62,0x62,0x52,0x52 +db 0x52,0x4a,0x4a,0x4a,0x43,0x43,0x43,0x80,0x80,0x80 +db 0xff,0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x33 +db 0x33,0x33,0x42,0x42,0x42,0x54,0x54,0x54,0x64,0x64 +db 0x64,0x71,0x71,0x71,0x79,0x79,0x79,0x7d,0x7d,0x7d +db 0x72,0x72,0x72,0x6b,0x6b,0x6b,0x5f,0x5f,0x5f,0x5a +db 0x5a,0x5a,0x54,0x54,0x54,0x80,0x80,0x80,0xff,0xff +db 0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x35,0x35,0x35 +db 0x41,0x41,0x41,0x53,0x53,0x53,0x63,0x63,0x63,0x70 +db 0x70,0x70,0x78,0x78,0x78,0x7d,0x7d,0x7d,0x77,0x77 +db 0x77,0x73,0x73,0x73,0x6c,0x6c,0x6c,0x68,0x68,0x68 +db 0x62,0x62,0x62,0x5a,0x5a,0x5a,0x80,0x80,0x80,0xff +db 0xff,0xff,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x41,0x41 +db 0x41,0x52,0x52,0x52,0x62,0x62,0x62,0x6f,0x6f,0x6f +db 0x78,0x78,0x78,0x7d,0x7d,0x7d,0x7b,0x7b,0x7b,0x79 +db 0x79,0x79,0x74,0x74,0x74,0x72,0x72,0x72,0x6e,0x6e +db 0x6e,0x66,0x66,0x66,0x80,0x80,0x80,0xc0,0xc0,0xc0 +db 0xc0,0xc0,0xc0,0x00,0x00,0x00,0x44,0x44,0x44,0x52 +db 0x52,0x52,0x62,0x62,0x62,0x6e,0x6e,0x6e,0x77,0x77 +db 0x77,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x7c,0x7c,0x7c +db 0x7a,0x7a,0x7a,0x79,0x79,0x79,0x75,0x75,0x75,0x6f +db 0x6f,0x6f,0x65,0x65,0x65,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x48,0x48,0x48,0x4b,0x4b,0x4b,0x56,0x56,0x56 +db 0x65,0x65,0x65,0x70,0x70,0x70,0x78,0x78,0x78,0x7d +db 0x7d,0x7d,0x80,0x80,0x80,0x7f,0x7f,0x7f,0x7e,0x7e +db 0x7e,0x7d,0x7d,0x7d,0x7a,0x7a,0x7a,0x76,0x76,0x76 +db 0x6f,0x6f,0x6f,0x65,0x65,0x65,0x5c,0x5c,0x5c,0x56 +db 0x56,0x56,0x58,0x58,0x58,0x60,0x60,0x60,0x6b,0x6b +db 0x6b,0x73,0x73,0x73,0x7a,0x7a,0x7a,0x7d,0x7d,0x7d +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7f +db 0x7f,0x7f,0x7d,0x7d,0x7d,0x7a,0x7a,0x7a,0x76,0x76 +db 0x76,0x70,0x70,0x70,0x6a,0x6a,0x6a,0x66,0x66,0x66 +db 0x66,0x66,0x66,0x6c,0x6c,0x6c,0x72,0x72,0x72,0x78 +db 0x78,0x78,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x7f,0x7f,0x7f,0x7d,0x7d,0x7d,0x7b,0x7b,0x7b,0x77 +db 0x77,0x77,0x73,0x73,0x73,0x71,0x71,0x71,0x71,0x71 +db 0x71,0x74,0x74,0x74,0x78,0x78,0x78,0x7b,0x7b,0x7b +db 0x7d,0x7d,0x7d,0x7f,0x7f,0x7f,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x7f,0x7f,0x7f,0x7d,0x7d,0x7d,0x7c,0x7c,0x7c +db 0x7a,0x7a,0x7a,0x78,0x78,0x78,0x78,0x78,0x78,0x7a +db 0x7a,0x7a,0x7c,0x7c,0x7c,0x7e,0x7e,0x7e,0x7f,0x7f +db 0x7f,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +db 0x80,0x80,0x7f,0x7f,0x7f,0x7e,0x7e,0x7e,0x7e,0x7e +db 0x7e,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7e,0x7e,0x7e +db 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x80,0x80,0x80,0x80 +db 0x80,0x80 + +mousepointer1: +db 0xff,0xff,0xff,0x06,0x06,0x06,0x0a,0x0a +db 0x0a,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0xff,0xff,0xff,0xff,0xff,0xff,0x19,0x19,0x19,0x16 +db 0x16,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2e,0x2e,0x2e +db 0x23,0x23,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x3f +db 0x3f,0x29,0x29,0x29,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x47 +db 0x47,0x47,0x2c,0x2c,0x2c,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0x48,0x48,0x48,0x2c,0x2c,0x2c,0x16,0x16,0x16,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0x48,0x48,0x48,0x2c,0x2c,0x2c,0x16,0x16,0x16 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0x48,0x48,0x48,0x2c,0x2c,0x2c,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0x48,0x48,0x48,0x2c,0x2c,0x2c,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0x47,0x47,0x47,0x29,0x29,0x29 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0x40,0x40,0x40,0x23,0x23 +db 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xaa,0xaa,0xaa,0x9f,0x9f,0x9f,0x8c,0x8c,0x8c +db 0x70,0x70,0x70,0x4f,0x4f,0x4f,0x30,0x30,0x30,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x8f,0x8f,0x8f +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0xff,0x9c,0x9c,0x9c,0x87,0x87,0x87,0x6c,0x6c +db 0x6c,0x4f,0x4f,0x4f,0x32,0x32,0x32,0x19,0x19,0x19 +db 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff +db 0xff,0xff,0x69,0x69,0x69,0x84,0x84,0x84,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0x92,0x92,0x92,0x79,0x79,0x79,0x59,0x59,0x59,0x3c +db 0x3c,0x3c,0x24,0x24,0x24,0x11,0x11,0x11,0x00,0x00 +db 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x37,0x37,0x37 +db 0x5d,0x5d,0x5d,0x70,0x70,0x70,0x76,0x76,0x76,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0xff,0x75,0x75,0x75,0x51,0x51,0x51,0x31,0x31,0x31 +db 0x19,0x19,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x16,0x16,0x16,0x2d,0x2d,0x2d,0x49,0x49 +db 0x49,0x53,0x53,0x53,0x54,0x54,0x54,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x78 +db 0x78,0x78,0x54,0x54,0x54,0x30,0x30,0x30,0x16,0x16 +db 0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x0f,0x0f,0x0f,0x1f,0x1f,0x1f,0x30,0x30,0x30,0x33 +db 0x33,0x33,0x33,0x33,0x33,0x3b,0x3b,0x3b,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff +db 0x62,0x62,0x62,0x3b,0x3b,0x3b,0x1c,0x1c,0x1c,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08 +db 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x24,0x24,0x24,0xff,0xff,0xff,0xff +db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6e,0x6e +db 0x6e,0x48,0x48,0x48,0x25,0x25,0x25,0x0e,0x0e,0x0e +db 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x04,0x00 +db 0x00,0x00,0x0a,0x0a,0x0a,0x09,0x09,0x09,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x29,0x29,0x29,0xff,0xff,0xff +db 0xff,0xff,0xff,0x7c,0x7c,0x7c,0x71,0x71,0x71,0x50 +db 0x50,0x50,0x2b,0x2b,0x2b,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x02,0x02,0x02,0x04,0x04,0x04,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x36,0x56,0x56 +db 0x56,0x69,0x69,0x69,0x64,0x64,0x64,0x4a,0x4a,0x4a +db 0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x05,0x05,0x05 +db 0x00,0x00,0x00,0x21,0x21,0x21,0x39,0x39,0x39,0x49 +db 0x49,0x49,0x48,0x48,0x48,0x35,0x35,0x35,0x1d,0x1d +db 0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x1d,0x1d,0x1d,0x27,0x27,0x27 +db 0x27,0x27,0x27,0x1d,0x1d,0x1d,0x0f,0x0f,0x0f,0x06 +db 0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +db 0x00,0x00,0x00,0x00 + +endg diff --git a/kernel/branches/kolibri-process/gui/skincode.inc b/kernel/branches/kolibri-process/gui/skincode.inc new file mode 100644 index 000000000..19a02d37e --- /dev/null +++ b/kernel/branches/kolibri-process/gui/skincode.inc @@ -0,0 +1,525 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3711 $ + + +include "skindata.inc" + +;skin_data = 0x00778000 +;------------------------------------------------------------------------------ +align 4 +read_skin_file: + stdcall load_file, ebx + test eax, eax + jz .notfound + + cmp dword [eax], 'SKIN' + jnz .noskin + + xchg eax, [skin_data] + test eax, eax + jz @f + + stdcall kernel_free, eax +@@: + call parse_skin_data + xor eax, eax + ret +;-------------------------------------- +align 4 +.notfound: + xor eax, eax + inc eax + ret +;-------------------------------------- +align 4 +.noskin: + stdcall kernel_free, eax + mov eax, 2 + ret +;------------------------------------------------------------------------------ +struct SKIN_HEADER + ident dd ? + version dd ? + params dd ? + buttons dd ? + bitmaps dd ? +ends + +struct SKIN_PARAMS + skin_height dd ? + margin.right dw ? + margin.left dw ? + margin.bottom dw ? + margin.top dw ? + colors.inner dd ? + colors.outer dd ? + colors.frame dd ? + colors_1.inner dd ? + colors_1.outer dd ? + colors_1.frame dd ? + dtp.size dd ? + dtp.data rb 40 +ends + +struct SKIN_BUTTONS + type dd ? +; position + left dw ? + top dw ? +; size + width dw ? + height dw ? +ends + +struct SKIN_BITMAPS + kind dw ? + type dw ? + data dd ? +ends +;------------------------------------------------------------------------------ +align 4 +load_default_skin: + mov [_skinh], 22 + mov ebx, _skin_file_default + call read_skin_file + ret +;------------------------------------------------------------------------------ +align 4 +parse_skin_data: + mov ebp, [skin_data] + cmp [ebp+SKIN_HEADER.ident], 'SKIN' + jne .exit + + mov edi, skin_udata + mov ecx, (skin_udata.end-skin_udata)/4 + xor eax, eax + cld + rep stosd + + mov ebx, [ebp+SKIN_HEADER.params] + add ebx, [skin_data] + mov eax, [ebx+SKIN_PARAMS.skin_height] + mov [_skinh], eax + mov eax, [ebx+SKIN_PARAMS.colors.inner] + mov [skin_active.colors.inner], eax + mov eax, [ebx+SKIN_PARAMS.colors.outer] + mov [skin_active.colors.outer], eax + mov eax, [ebx+SKIN_PARAMS.colors.frame] + mov [skin_active.colors.frame], eax + mov eax, [ebx+SKIN_PARAMS.colors_1.inner] + mov [skin_inactive.colors.inner], eax + mov eax, [ebx+SKIN_PARAMS.colors_1.outer] + mov [skin_inactive.colors.outer], eax + mov eax, [ebx+SKIN_PARAMS.colors_1.frame] + mov [skin_inactive.colors.frame], eax + lea esi, [ebx+SKIN_PARAMS.dtp.data] + mov edi, common_colours + mov ecx, [ebx+SKIN_PARAMS.dtp.size] + and ecx, 127 + rep movsb + mov eax, dword[ebx+SKIN_PARAMS.margin.right] + mov dword[_skinmargins+0], eax + mov eax, dword[ebx+SKIN_PARAMS.margin.bottom] + mov dword[_skinmargins+4], eax + + mov ebx, [ebp+SKIN_HEADER.bitmaps] + add ebx, [skin_data] +;-------------------------------------- +align 4 +.lp1: + cmp dword[ebx], 0 + je .end_bitmaps + movzx eax, [ebx+SKIN_BITMAPS.kind] + movzx ecx, [ebx+SKIN_BITMAPS.type] + dec eax + jnz .not_left + xor eax, eax + mov edx, skin_active.left.data + or ecx, ecx + jnz @f + mov edx, skin_inactive.left.data +;-------------------------------------- +align 4 +@@: + jmp .next_bitmap +;-------------------------------------- +align 4 +.not_left: + dec eax + jnz .not_oper + mov esi, [ebx+SKIN_BITMAPS.data] + add esi, [skin_data] + mov eax, [esi+0] + neg eax + mov edx, skin_active.oper.data + or ecx, ecx + jnz @f + mov edx, skin_inactive.oper.data +;-------------------------------------- +align 4 +@@: + jmp .next_bitmap +;-------------------------------------- +align 4 +.not_oper: + dec eax + jnz .not_base + mov eax, [skin_active.left.width] + mov edx, skin_active.base.data + or ecx, ecx + jnz @f + mov eax, [skin_inactive.left.width] + mov edx, skin_inactive.base.data +;-------------------------------------- +align 4 +@@: + jmp .next_bitmap +;-------------------------------------- +align 4 +.not_base: + add ebx, 8 + jmp .lp1 +;-------------------------------------- +align 4 +.next_bitmap: + mov ecx, [ebx+SKIN_BITMAPS.data] + add ecx, [skin_data] + mov [edx+4], eax + mov eax, [ecx+0] + mov [edx+8], eax + add ecx, 8 + mov [edx+0], ecx + add ebx, 8 + jmp .lp1 +;-------------------------------------- +align 4 +.end_bitmaps: + mov ebx, [ebp+SKIN_HEADER.buttons] + add ebx, [skin_data] +;-------------------------------------- +align 4 +.lp2: + cmp dword[ebx], 0 + je .end_buttons + mov eax, [ebx+SKIN_BUTTONS.type] + dec eax + jnz .not_close + mov edx, skin_btn_close + jmp .next_button +;-------------------------------------- +align 4 +.not_close: + dec eax + jnz .not_minimize + mov edx, skin_btn_minimize + jmp .next_button +;-------------------------------------- +align 4 +.not_minimize: + add ebx, 12 + jmp .lp2 +;-------------------------------------- +align 4 +.next_button: + movsx eax, [ebx+SKIN_BUTTONS.left] + mov [edx+SKIN_BUTTON.left], eax + movsx eax, [ebx+SKIN_BUTTONS.top] + mov [edx+SKIN_BUTTON.top], eax + movsx eax, [ebx+SKIN_BUTTONS.width] + mov [edx+SKIN_BUTTON.width], eax + movsx eax, [ebx+SKIN_BUTTONS.height] + mov [edx+SKIN_BUTTON.height], eax + add ebx, 12 + jmp .lp2 +;-------------------------------------- +align 4 +.end_buttons: +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +drawwindow_IV_caption: + + mov ebp, skin_active + or al, al + jnz @f + mov ebp, skin_inactive +;-------------------------------------- +align 4 +@@: + mov esi, [esp+4] + mov eax, [esi+WDATA.box.width] ; window width + mov edx, [ebp+SKIN_DATA.left.left] + shl edx, 16 + mov ecx, [ebp+SKIN_DATA.left.width] + shl ecx, 16 + add ecx, [_skinh] + + mov ebx, [ebp+SKIN_DATA.left.data] + or ebx, ebx + jz @f + call sys_putimage.forced +;-------------------------------------- +align 4 +@@: + mov esi, [esp+4] + mov eax, [esi+WDATA.box.width] + sub eax, [ebp+SKIN_DATA.left.width] + sub eax, [ebp+SKIN_DATA.oper.width] + cmp eax, [ebp+SKIN_DATA.base.left] + jng .non_base + xor edx, edx + mov ecx, [ebp+SKIN_DATA.base.width] + jecxz .non_base + div ecx + + inc eax + + mov ebx, [ebp+SKIN_DATA.base.data] + mov ecx, [ebp+SKIN_DATA.base.width] + shl ecx, 16 + add ecx, [_skinh] + mov edx, [ebp+SKIN_DATA.base.left] + sub edx, [ebp+SKIN_DATA.base.width] + shl edx, 16 +;-------------------------------------- +align 4 +.baseskinloop: + shr edx, 16 + add edx, [ebp+SKIN_DATA.base.width] + shl edx, 16 + + push eax ebx ecx edx + + or ebx, ebx + jz @f + call sys_putimage.forced +;-------------------------------------- +align 4 +@@: + pop edx ecx ebx eax + + dec eax + jnz .baseskinloop +;-------------------------------------- +align 4 +.non_base: + + mov esi, [esp+4] + mov edx, [esi+WDATA.box.width] + sub edx, [ebp+SKIN_DATA.oper.width] + inc edx + shl edx, 16 + mov ebx, [ebp+SKIN_DATA.oper.data] + + mov ecx, [ebp+SKIN_DATA.oper.width] + shl ecx, 16 + add ecx, [_skinh] + + or ebx, ebx + jz @f + call sys_putimage.forced +;-------------------------------------- +align 4 +@@: + ret +;------------------------------------------------------------------------------ +align 4 +drawwindow_IV: +;param1 - aw_yes + pusha + + push edx + + mov edi, edx + + mov ebp, skin_active + cmp byte [esp+32+4+4], 0 + jne @f + mov ebp, skin_inactive +;-------------------------------------- +align 4 +@@: + mov eax, [edi+WDATA.box.left] + shl eax, 16 + mov ax, word [edi+WDATA.box.left] + add ax, word [edi+WDATA.box.width] + mov ebx, [edi+WDATA.box.top] + shl ebx, 16 + mov bx, word [edi+WDATA.box.top] + add bx, word [edi+WDATA.box.height] + mov esi, [ebp+SKIN_DATA.colors.outer] + or esi, 1 shl 25 ; 0x02000000 used for draw_rectangle without top line + ror ebx, 16 + add ebx, [_skinh] + sub bx, 1 + rol ebx, 16 + call draw_rectangle + mov ecx, 3 +;-------------------------------------- +align 4 +_dw3l: + add eax, 1*65536-1 + add ebx, 0*65536-1 + test ax, ax + js no_skin_add_button + test bx, bx + js no_skin_add_button + mov esi, [ebp+SKIN_DATA.colors.frame];[edi+24] + or esi, 1 shl 25; 0x02000000 used for draw_rectangle without top line + call draw_rectangle + dec ecx + jnz _dw3l + mov esi, [ebp+SKIN_DATA.colors.inner] + or esi, 1 shl 25; 0x02000000 used for draw_rectangle without top line + add eax, 1*65536-1 + add ebx, 0*65536-1 + test ax, ax + js no_skin_add_button + test bx, bx + js no_skin_add_button + test [edi + WDATA.fl_wstate], WSTATE_ROLLEDUP + jnz @f + call draw_rectangle +;-------------------------------------- +align 4 +@@: + mov eax, [skin_data] + cmp [eax], dword 'SKIN' + je @f + xor eax, eax + xor ebx, ebx + mov esi, [esp] + mov ecx, [esi+WDATA.box.width] + inc ecx + mov edx, [_skinh] + mov edi, [common_colours+4]; standard grab color +; call [drawbar] + call vesa20_drawbar + jmp draw_clientbar +;-------------------------------------- +align 4 +@@: + mov al, [esp+32+4+4] + call drawwindow_IV_caption +;-------------------------------------- +align 4 +draw_clientbar: + mov esi, [esp] + + mov edx, [esi+WDATA.box.top] ; WORK AREA + add edx, 21+5 + mov ebx, [esi+WDATA.box.top] + add ebx, [esi+WDATA.box.height] + cmp edx, ebx + jg _noinside2 + mov eax, 5 + mov ebx, [_skinh] + mov ecx, [esi+WDATA.box.width] + mov edx, [esi+WDATA.box.height] + sub ecx, 4 + sub edx, 4 + mov edi, [esi+WDATA.cl_workarea] + test edi, 0x40000000 + jnz _noinside2 +; call [drawbar] + call vesa20_drawbar +;-------------------------------------- +align 4 +_noinside2: + mov eax, [skin_data] + cmp [eax], dword 'SKIN' + jne no_skin_add_button +;* close button + mov edi, [BTN_ADDR] + movzx eax, word [edi] + cmp eax, 1000 + jge no_skin_add_button + inc eax + mov [edi], ax + + shl eax, 4 + add eax, edi + + mov bx, [CURRENT_TASK] + mov [eax], bx + + add eax, 2 ; save button id number + mov bx, 1 + mov [eax], bx + add eax, 2 ; x start + xor ebx, ebx + cmp [skin_btn_close.left], 0 + jge _bCx_at_right + mov ebx, [esp] + mov ebx, [ebx+WDATA.box.width] + inc ebx +;-------------------------------------- +align 4 +_bCx_at_right: + add ebx, [skin_btn_close.left] + mov [eax], bx + add eax, 2 ; x size + mov ebx, [skin_btn_close.width] + dec ebx + mov [eax], bx + add eax, 2 ; y start + mov ebx, [skin_btn_close.top] + mov [eax], bx + add eax, 2 ; y size + mov ebx, [skin_btn_close.height] + dec ebx + mov [eax], bx +;* minimize button + mov edi, [BTN_ADDR] + movzx eax, word [edi] + cmp eax, 1000 + jge no_skin_add_button + inc eax + mov [edi], ax + + shl eax, 4 + add eax, edi + + mov bx, [CURRENT_TASK] + mov [eax], bx + + add eax, 2 ; save button id number + mov bx, 65535;999 + mov [eax], bx + add eax, 2 ; x start + xor ebx, ebx + cmp [skin_btn_minimize.left], 0 + jge _bMx_at_right + mov ebx, [esp] + mov ebx, [ebx+WDATA.box.width] + inc ebx +;-------------------------------------- +align 4 +_bMx_at_right: + add ebx, [skin_btn_minimize.left] + mov [eax], bx + add eax, 2 ; x size + mov ebx, [skin_btn_minimize.width] + dec ebx + mov [eax], bx + add eax, 2 ; y start + mov ebx, [skin_btn_minimize.top] + mov [eax], bx + add eax, 2 ; y size + mov ebx, [skin_btn_minimize.height] + dec ebx + mov [eax], bx +;-------------------------------------- +align 4 +no_skin_add_button: + pop edi + popa + ret 4 +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-process/gui/skindata.inc b/kernel/branches/kolibri-process/gui/skindata.inc new file mode 100644 index 000000000..4e091a391 --- /dev/null +++ b/kernel/branches/kolibri-process/gui/skindata.inc @@ -0,0 +1,64 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; +; WINDOW SKIN DATA +; + +iglobal + _skin_file_default db '/sys/DEFAULT.SKN',0 +endg + +struct SKIN_DATA + colors.inner dd ? + colors.outer dd ? + colors.frame dd ? + left.data dd ? + left.left dd ? + left.width dd ? + oper.data dd ? + oper.left dd ? + oper.width dd ? + base.data dd ? + base.left dd ? + base.width dd ? +ends + +struct SKIN_BUTTON + left dd ? + top dd ? + width dd ? + height dd ? +ends + +uglobal + +align 4 + +skin_udata: + _skinh dd ? + + _skinmargins: ; rw 4 + .right dw ? + .left dw ? + .bottom dw ? + .top dw ? + + skin_btn_close SKIN_BUTTON + skin_btn_minimize SKIN_BUTTON + + skin_active SKIN_DATA + skin_inactive SKIN_DATA + +align 4 + +skin_udata.end: + +endg diff --git a/kernel/branches/kolibri-process/gui/window.inc b/kernel/branches/kolibri-process/gui/window.inc new file mode 100644 index 000000000..7f2594f78 --- /dev/null +++ b/kernel/branches/kolibri-process/gui/window.inc @@ -0,0 +1,2421 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4424 $ + +;============================================================================== +;///// public functions /////////////////////////////////////////////////////// +;============================================================================== + +window.BORDER_SIZE = 5 + +macro FuncTable name, table_name, [label] +{ + common + align 4 + \label name#.#table_name dword + forward + dd name#.#label + common + name#.sizeof.#table_name = $ - name#.#table_name +} + +uglobal + common_colours rd 32 + draw_limits RECT +endg + +align 4 +;------------------------------------------------------------------------------ +syscall_draw_window: ;///// system function 0 ///////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + mov eax, edx + shr eax, 24 + and al, 0x0f + cmp al, 5 + jae .exit + + push eax + call window._.sys_set_window + pop eax + + or al, al + jnz @f + + ; type I - original style + call drawwindow_I + jmp window._.draw_window_caption.2 +;-------------------------------------- +align 4 +@@: + dec al + jnz @f + + ; type II - only reserve area, no draw +; call sys_window_mouse +; call [draw_pointer] + call __sys_draw_pointer + jmp .exit +;-------------------------------------- +align 4 +@@: + dec al + jnz @f + + ; type III - new style + call drawwindow_III + jmp window._.draw_window_caption.2 + + ; type IV & V - skinned window (resizable & not) +;-------------------------------------- +align 4 +@@: + mov eax, [TASK_COUNT] + movzx eax, word[WIN_POS + eax * 2] + cmp eax, [CURRENT_TASK] + setz al + movzx eax, al + push eax + call drawwindow_IV + jmp window._.draw_window_caption.2 +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +syscall_display_settings: ;///// system function 48 /////////////////////////// +;------------------------------------------------------------------------------ +;; Redraw screen: +;< ebx = 0 +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Set button style: +;< ebx = 1 +;< ecx = 0 (flat) or 1 (with gradient) +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Set system color palette: +;< ebx = 2 +;< ecx = pointer to color table +;< edx = size of color table +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Get system color palette: +;< ebx = 3 +;< ecx = pointer to color table buffer +;< edx = size of color table buffer +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Get skinned caption height: +;< ebx = 4 +;> eax = height in pixels +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Get screen working area: +;< ebx = 5 +;> eax = pack[16(left), 16(right)] +;> ebx = pack[16(top), 16(bottom)] +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Set screen working area: +;< ebx = 6 +;< ecx = pack[16(left), 16(right)] +;< edx = pack[16(top), 16(bottom)] +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Get skin margins: +;< ebx = 7 +;> eax = pack[16(left), 16(right)] +;> ebx = pack[16(top), 16(bottom)] +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Set skin: +;< ebx = 8 +;< ecx = pointer to FileInfoBlock struct +;> eax = FS error code +;------------------------------------------------------------------------------ + cmp ebx, .sizeof.ftable / 4 + ja @f + jmp [.ftable + ebx * 4] +;-------------------------------------- +align 4 +@@: + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.00: + xor eax, eax + inc ebx + cmp [windowtypechanged], ebx + jne .exit + mov [windowtypechanged], eax + + jmp syscall_display_settings._.redraw_whole_screen +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.01: + and ecx, 1 + cmp ecx, [buttontype] + je .exit + mov [buttontype], ecx + mov [windowtypechanged], ebx +;-------------------------------------- +align 4 + .exit: + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.02: + dec ebx + mov esi, ecx + and edx, 127 + mov edi, common_colours + mov ecx, edx + rep movsb + mov [windowtypechanged], ebx + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.03: + mov edi, ecx + and edx, 127 + mov esi, common_colours + mov ecx, edx + rep movsb + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.04: + mov eax, [_skinh] + mov [esp + 32], eax + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.05: + mov eax, [screen_workarea.left - 2] + mov ax, word[screen_workarea.right] + mov [esp + 32], eax + mov eax, [screen_workarea.top - 2] + mov ax, word[screen_workarea.bottom] + mov [esp + 20], eax + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.06: + xor esi, esi + + mov edi, [Screen_Max_X] + mov eax, ecx + movsx ebx, ax + sar eax, 16 + cmp eax, ebx + jge .check_horizontal + inc esi + or eax, eax + jge @f + xor eax, eax +;-------------------------------------- +align 4 +@@: + mov [screen_workarea.left], eax + cmp ebx, edi + jle @f + mov ebx, edi +;-------------------------------------- +align 4 +@@: + mov [screen_workarea.right], ebx +;-------------------------------------- +align 4 +.check_horizontal: + mov edi, [Screen_Max_Y] + mov eax, edx + movsx ebx, ax + sar eax, 16 + cmp eax, ebx + jge .check_if_redraw_needed + inc esi + or eax, eax + jge @f + xor eax, eax +;-------------------------------------- +align 4 +@@: + mov [screen_workarea.top], eax + cmp ebx, edi + jle @f + mov ebx, edi +;-------------------------------------- +align 4 +@@: + mov [screen_workarea.bottom], ebx +;-------------------------------------- +align 4 +.check_if_redraw_needed: + or esi, esi + jz .exit + + call repos_windows + jmp syscall_display_settings._.calculate_whole_screen +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.07: + mov eax, [_skinmargins + 0] + mov [esp + 32], eax + mov eax, [_skinmargins + 4] + mov [esp + 20], eax + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings.08: + mov ebx, ecx + call read_skin_file + mov [esp + 32], eax + test eax, eax + jnz .exit + + call syscall_display_settings._.calculate_whole_screen + jmp syscall_display_settings._.redraw_whole_screen +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings._.calculate_whole_screen: + xor eax, eax + xor ebx, ebx + mov ecx, [Screen_Max_X] + mov edx, [Screen_Max_Y] + jmp calculatescreen +;------------------------------------------------------------------------------ +align 4 +syscall_display_settings._.redraw_whole_screen: + xor eax, eax + mov [draw_limits.left], eax + mov [draw_limits.top], eax + mov eax, [Screen_Max_X] + mov [draw_limits.right], eax + mov eax, [Screen_Max_Y] + mov [draw_limits.bottom], eax + mov eax, window_data + jmp redrawscreen +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +syscall_set_window_shape: ;///// system function 50 /////////////////////////// +;------------------------------------------------------------------------------ +;; Set window shape address: +;> ebx = 0 +;> ecx = shape data address +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +;; Set window shape scale: +;> ebx = 1 +;> ecx = scale power (resulting scale is 2^ebx) +;------------------------------------------------------------------------------ + mov edi, [current_slot] + + test ebx, ebx + jne .shape_scale + mov [edi + APPDATA.wnd_shape], ecx +;-------------------------------------- +align 4 +.shape_scale: + dec ebx + jnz .exit + mov [edi + APPDATA.wnd_shape_scale], ecx +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +syscall_move_window: ;///// system function 67 //////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + mov edi, [CURRENT_TASK] + shl edi, 5 + add edi, window_data + + test [edi + WDATA.fl_wdrawn], 1 + jz .exit + + test [edi + WDATA.fl_wstate], WSTATE_MAXIMIZED + jnz .exit + + cmp ebx, -1 + jne @f + mov ebx, [edi + WDATA.box.left] +;-------------------------------------- +align 4 +@@: + cmp ecx, -1 + jne @f + mov ecx, [edi + WDATA.box.top] +;-------------------------------------- +align 4 +@@: + cmp edx, -1 + jne @f + mov edx, [edi + WDATA.box.width] +;-------------------------------------- +align 4 +@@: + cmp esi, -1 + jne @f + mov esi, [edi + WDATA.box.height] +;-------------------------------------- +align 4 +@@: + push esi edx ecx ebx + mov eax, esp + mov bl, [edi + WDATA.fl_wstate] +;-------------------------------------- +align 4 +@@: + cmp [REDRAW_BACKGROUND], byte 0 + jz @f + call change_task + jmp @b +;-------------------------------------- +align 4 +@@: + call window._.set_window_box + add esp, sizeof.BOX + + ; NOTE: do we really need this? to be reworked +; mov byte[DONT_DRAW_MOUSE], 0 ; mouse pointer +; mov byte[MOUSE_BACKGROUND], 0 ; no mouse under +; mov byte[MOUSE_DOWN], 0 ; react to mouse up/down + + ; NOTE: do we really need this? to be reworked +; call [draw_pointer] +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +syscall_window_settings: ;///// system function 71 ///////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + dec ebx ; subfunction #1 - set window caption + jnz .exit_fail + + ; NOTE: only window owner thread can set its caption, + ; so there's no parameter for PID/TID + + mov edi, [CURRENT_TASK] + shl edi, 5 + + mov [edi * 8 + SLOT_BASE + APPDATA.wnd_caption], ecx + or [edi + window_data + WDATA.fl_wstyle], WSTYLE_HASCAPTION + + call window._.draw_window_caption + + xor eax, eax ; eax = 0 (success) + ret + +; .get_window_caption: +; dec eax ; subfunction #2 - get window caption +; jnz .exit_fail + + ; not implemented yet +;-------------------------------------- +align 4 +.exit_fail: + xor eax, eax + inc eax ; eax = 1 (fail) + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +set_window_defaults: ;///////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + mov byte [window_data + 0x20 + WDATA.cl_titlebar + 3], 1 ; desktop is not movable + push eax ecx + xor eax, eax + mov ecx, WIN_STACK +;-------------------------------------- +align 4 +@@: + inc eax + add ecx, 2 + ; process no + mov [ecx + 0x000], ax + ; positions in stack + mov [ecx + 0x400], ax + cmp ecx, WIN_POS - 2 + jne @b + pop ecx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +calculatescreen: ;///////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Scan all windows from bottom to top, calling `setscreen` for each one +;? intersecting given screen area +;------------------------------------------------------------------------------ +;> eax = left +;> ebx = top +;> ecx = right +;> edx = bottom +;------------------------------------------------------------------------------ + push esi + pushfd + cli + + mov esi, 1 + call window._.set_screen + + push ebp + + mov ebp, [TASK_COUNT] + cmp ebp, 1 + jbe .exit + + push edx ecx ebx eax +;-------------------------------------- +align 4 +.next_window: + movzx edi, word[WIN_POS + esi * 2] + shl edi, 5 + + cmp [CURRENT_TASK + edi + TASKDATA.state], TSTATE_FREE + je .skip_window + + add edi, window_data + test [edi + WDATA.fl_wstate], WSTATE_MINIMIZED + jnz .skip_window + + mov eax, [edi + WDATA.box.left] + cmp eax, [esp + RECT.right] + jg .skip_window + mov ebx, [edi + WDATA.box.top] + cmp ebx, [esp + RECT.bottom] + jg .skip_window + mov ecx, [edi + WDATA.box.width] + add ecx, eax + cmp ecx, [esp + RECT.left] + jl .skip_window + mov edx, [edi + WDATA.box.height] + add edx, ebx + cmp edx, [esp + RECT.top] + jl .skip_window + + cmp eax, [esp + RECT.left] + jae @f + mov eax, [esp + RECT.left] +;-------------------------------------- +align 4 +@@: + cmp ebx, [esp + RECT.top] + jae @f + mov ebx, [esp + RECT.top] +;-------------------------------------- +align 4 +@@: + cmp ecx, [esp + RECT.right] + jbe @f + mov ecx, [esp + RECT.right] +;-------------------------------------- +align 4 +@@: + cmp edx, [esp + RECT.bottom] + jbe @f + mov edx, [esp + RECT.bottom] +;-------------------------------------- +align 4 +@@: + push esi + movzx esi, word[WIN_POS + esi * 2] + call window._.set_screen + pop esi +;-------------------------------------- +align 4 +.skip_window: + inc esi + dec ebp + jnz .next_window + + pop eax ebx ecx edx +;-------------------------------------- +align 4 +.exit: + pop ebp + inc [_display.mask_seqno] + popfd + pop esi + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +repos_windows: ;/////////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + mov ecx, [TASK_COUNT] + mov edi, window_data + sizeof.WDATA * 2 + call force_redraw_background + dec ecx + jle .exit +;-------------------------------------- +align 4 +.next_window: + mov [edi + WDATA.fl_redraw], 1 + test [edi + WDATA.fl_wstate], WSTATE_MAXIMIZED + jnz .fix_maximized + + mov eax, [edi + WDATA.box.left] + add eax, [edi + WDATA.box.width] + mov ebx, [Screen_Max_X] + cmp eax, ebx + jle .fix_vertical + mov eax, [edi + WDATA.box.width] + sub eax, ebx + jle @f + mov [edi + WDATA.box.width], ebx +;-------------------------------------- +align 4 +@@: + sub ebx, [edi + WDATA.box.width] + mov [edi + WDATA.box.left], ebx +;-------------------------------------- +align 4 +.fix_vertical: + mov eax, [edi + WDATA.box.top] + add eax, [edi + WDATA.box.height] + mov ebx, [Screen_Max_Y] + cmp eax, ebx + jle .fix_client_box + mov eax, [edi + WDATA.box.height] + sub eax, ebx + jle @f + mov [edi + WDATA.box.height], ebx +;-------------------------------------- +align 4 +@@: + sub ebx, [edi + WDATA.box.height] + mov [edi + WDATA.box.top], ebx +;-------------------------------------- +align 4 +.fix_client_box: + call window._.set_window_clientbox + add edi, sizeof.WDATA + loop .next_window +;-------------------------------------- +align 4 +.exit: + ret +;-------------------------------------- +align 4 +.fix_maximized: + mov eax, [screen_workarea.left] + mov [edi + WDATA.box.left], eax + sub eax, [screen_workarea.right] + neg eax + mov [edi + WDATA.box.width], eax + mov eax, [screen_workarea.top] + mov [edi + WDATA.box.top], eax + test [edi + WDATA.fl_wstate], WSTATE_ROLLEDUP + jnz .fix_client_box + sub eax, [screen_workarea.bottom] + neg eax + mov [edi + WDATA.box.height], eax + jmp .fix_client_box +;------------------------------------------------------------------------------ +;align 4 +;------------------------------------------------------------------------------ +;sys_window_mouse: ;//////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + ; NOTE: commented out since doesn't provide necessary functionality + ; anyway, to be reworked +; push eax +; +; mov eax, [timer_ticks] +; cmp [new_window_starting], eax +; jb .exit +; +; mov byte[MOUSE_BACKGROUND], 0 +; mov byte[DONT_DRAW_MOUSE], 0 +; +; mov [new_window_starting], eax +; +; .exit: +; pop eax +; ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +draw_rectangle: ;////////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;> eax = pack[16(left), 16(right)] +;> ebx = pack[16(top), 16(bottom)] +;> esi = color +; ?? RR GG BB ; 0x01000000 negation +; ; 0x02000000 used for draw_rectangle without top line +; ; for example drawwindow_III and drawwindow_IV +;------------------------------------------------------------------------------ + push eax ebx ecx edi + + xor edi, edi +;-------------------------------------- +align 4 +.flags_set: + push ebx + + ; set line color + mov ecx, esi + ; draw top border + rol ebx, 16 + push ebx + rol ebx, 16 + pop bx + test ecx, 1 shl 25 + jnz @f + sub ecx, 1 shl 25 +; call [draw_line] + call __sys_draw_line +;-------------------------------------- +align 4 +@@: + ; draw bottom border + mov ebx, [esp - 2] + pop bx +; call [draw_line] + call __sys_draw_line + + pop ebx + add ebx, 1 * 65536 - 1 + + ; draw left border + rol eax, 16 + push eax + rol eax, 16 + pop ax +; call [draw_line] + call __sys_draw_line + + ; draw right border + mov eax, [esp - 2] + pop ax +; call [draw_line] + call __sys_draw_line + + pop edi ecx ebx eax + ret +;-------------------------------------- +align 4 +.forced: + push eax ebx ecx edi + xor edi, edi + inc edi + jmp .flags_set +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +drawwindow_I_caption: ;//////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + push [edx + WDATA.cl_titlebar] + mov esi, edx + + mov edx, [esi + WDATA.box.top] + mov eax, edx + lea ebx, [edx + 21] + inc edx + add eax, [esi + WDATA.box.height] + + cmp ebx, eax + jbe @f + mov ebx, eax +;-------------------------------------- +align 4 +@@: + push ebx + + xor edi, edi +;-------------------------------------- +align 4 +.next_line: + mov ebx, edx + shl ebx, 16 + add ebx, edx + mov eax, [esi + WDATA.box.left] + inc eax + shl eax, 16 + add eax, [esi + WDATA.box.left] + add eax, [esi + WDATA.box.width] + dec eax + mov ecx, [esi + WDATA.cl_titlebar] + test ecx, 0x80000000 + jz @f + sub ecx, 0x00040404 + mov [esi + WDATA.cl_titlebar], ecx +;-------------------------------------- +align 4 +@@: + and ecx, 0x00ffffff +; call [draw_line] + call __sys_draw_line + inc edx + cmp edx, [esp] + jb .next_line + + add esp, 4 + pop [esi + WDATA.cl_titlebar] + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +drawwindow_I: ;//////////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + pushad + + ; window border + + mov eax, [edx + WDATA.box.left - 2] + mov ax, word[edx + WDATA.box.left] + add ax, word[edx + WDATA.box.width] + mov ebx, [edx + WDATA.box.top - 2] + mov bx, word[edx + WDATA.box.top] + add bx, word[edx + WDATA.box.height] + + mov esi, [edx + WDATA.cl_frames] + call draw_rectangle + + ; window caption + + call drawwindow_I_caption + + ; window client area + + ; do we need to draw it? + mov edi, [esi + WDATA.cl_workarea] + test edi, 0x40000000 + jnz .exit + + ; does client area have a positive size on screen? + mov edx, [esi + WDATA.box.top] + add edx, 21 + 5 + mov ebx, [esi + WDATA.box.top] + add ebx, [esi + WDATA.box.height] + cmp edx, ebx + jg .exit + + ; okay, let's draw it + mov eax, 1 + mov ebx, 21 + mov ecx, [esi + WDATA.box.width] + mov edx, [esi + WDATA.box.height] +; call [drawbar] + call vesa20_drawbar +;-------------------------------------- +align 4 +.exit: + popad + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +drawwindow_III_caption: ;///////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + mov ecx, [edx + WDATA.cl_titlebar] + push ecx + mov esi, edx + mov edx, [esi + WDATA.box.top] + add edx, 4 + mov ebx, [esi + WDATA.box.top] + add ebx, 20 + mov eax, [esi + WDATA.box.top] + add eax, [esi + WDATA.box.height] + + cmp ebx, eax + jb @f + mov ebx, eax +;-------------------------------------- +align 4 +@@: + push ebx + + xor edi, edi +;-------------------------------------- +align 4 +.next_line: + mov ebx, edx + shl ebx, 16 + add ebx, edx + mov eax, [esi + WDATA.box.left] + shl eax, 16 + add eax, [esi + WDATA.box.left] + add eax, [esi + WDATA.box.width] + add eax, 4 * 65536 - 4 + mov ecx, [esi + WDATA.cl_titlebar] + test ecx, 0x40000000 + jz @f + add ecx, 0x00040404 +;-------------------------------------- +align 4 +@@: + test ecx, 0x80000000 + jz @f + sub ecx, 0x00040404 +;-------------------------------------- +align 4 +@@: + mov [esi + WDATA.cl_titlebar], ecx + and ecx, 0x00ffffff +; call [draw_line] + call __sys_draw_line + inc edx + cmp edx, [esp] + jb .next_line + + add esp, 4 + pop [esi + WDATA.cl_titlebar] + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +drawwindow_III: ;////////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + pushad + + ; window border + + mov eax, [edx + WDATA.box.left - 2] + mov ax, word[edx + WDATA.box.left] + add ax, word[edx + WDATA.box.width] + mov ebx, [edx + WDATA.box.top - 2] + mov bx, word[edx + WDATA.box.top] + add bx, word[edx + WDATA.box.height] + + mov esi, [edx + WDATA.cl_frames] + shr esi, 1 + and esi, 0x007f7f7f + call draw_rectangle + + push esi + mov ecx, 3 + mov esi, [edx + WDATA.cl_frames] +;-------------------------------------- +align 4 +.next_frame: + add eax, 1 * 65536 - 1 + add ebx, 1 * 65536 - 1 + call draw_rectangle + dec ecx + jnz .next_frame + + pop esi + add eax, 1 * 65536 - 1 + add ebx, 1 * 65536 - 1 + call draw_rectangle + + ; window caption + + call drawwindow_III_caption + + ; window client area + + ; do we need to draw it? + mov edi, [esi + WDATA.cl_workarea] + test edi, 0x40000000 + jnz .exit + + ; does client area have a positive size on screen? + mov edx, [esi + WDATA.box.top] + add edx, 21 + 5 + mov ebx, [esi + WDATA.box.top] + add ebx, [esi + WDATA.box.height] + cmp edx, ebx + jg .exit + + ; okay, let's draw it + mov eax, 5 + mov ebx, 20 + mov ecx, [esi + WDATA.box.width] + mov edx, [esi + WDATA.box.height] + sub ecx, 4 + sub edx, 4 +; call [drawbar] + call vesa20_drawbar +;-------------------------------------- +align 4 +.exit: + popad + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +waredraw: ;//////////////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Activate window, redrawing if necessary +;------------------------------------------------------------------------------ + push -1 + mov eax, [TASK_COUNT] + lea eax, [WIN_POS + eax * 2] + cmp eax, esi + pop eax + je .exit + + ; is it overlapped by another window now? + push ecx + call window._.check_window_draw + test ecx, ecx + pop ecx + jz .do_not_draw + + ; yes it is, activate and update screen buffer + call window._.window_activate + + pushad + mov edi, [TASK_COUNT] + movzx esi, word[WIN_POS + edi * 2] + shl esi, 5 + add esi, window_data + + mov eax, [esi + WDATA.box.left] + mov ebx, [esi + WDATA.box.top] + mov ecx, [esi + WDATA.box.width] + mov edx, [esi + WDATA.box.height] + + add ecx, eax + add edx, ebx + + mov edi, [TASK_COUNT] + movzx esi, word[WIN_POS + edi * 2] + call window._.set_screen + inc [_display.mask_seqno] + popad + + ; tell application to redraw itself + mov [edi + WDATA.fl_redraw], 1 + xor eax, eax + jmp .exit +;-------------------------------------- +align 4 +.do_not_draw: + ; no it's not, just activate the window + call window._.window_activate + xor eax, eax + ret + +;-------------------------------------- +align 4 +.exit: + inc eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +minimize_all_window: + push ebx ecx edx esi edi + pushfd + cli + xor edx, edx + mov eax, 2 ; we do not minimize the kernel thread N1 + mov ebx, [TASK_COUNT] +;-------------------------------------- +align 4 +.loop: + movzx edi, word[WIN_POS + eax * 2] + shl edi, 5 +; it is a unused slot? + cmp dword [edi+CURRENT_TASK+TASKDATA.state], 9 + je @f +; it is a hidden thread? + lea esi, [edi*8+SLOT_BASE+APPDATA.app_name] + cmp [esi], byte '@' + je @f +; is it already minimized? + test [edi + window_data+WDATA.fl_wstate], WSTATE_MINIMIZED + jnz @f +; no it's not, let's do that + or [edi + window_data+WDATA.fl_wstate], WSTATE_MINIMIZED + inc edx +;-------------------------------------- +align 4 +@@: + inc eax + cmp eax, ebx + jbe .loop +; If nothing has changed + test edx, edx + jz @f + + push edx + call syscall_display_settings._.calculate_whole_screen + call syscall_display_settings._.redraw_whole_screen + pop edx +;-------------------------------------- +align 4 +@@: + mov eax, edx + popfd + pop edi esi edx ecx ebx + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +minimize_window: ;///////////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;> eax = window number on screen +;------------------------------------------------------------------------------ +;# corrupts [dl*] +;------------------------------------------------------------------------------ + push edi + pushfd + cli + + ; is it already minimized? + movzx edi, word[WIN_POS + eax * 2] + shl edi, 5 + add edi, window_data + test [edi + WDATA.fl_wstate], WSTATE_MINIMIZED + jnz .exit + + push eax ebx ecx edx esi + + ; no it's not, let's do that + or [edi + WDATA.fl_wstate], WSTATE_MINIMIZED +; If the window width is 0, then the action is not needed. + cmp [edi + WDATA.box.width], dword 0 + je @f +; If the window height is 0, then the action is not needed. + cmp [edi + WDATA.box.height], dword 0 + je @f + + mov eax, [edi + WDATA.box.left] + mov [draw_limits.left], eax + mov ecx, eax + add ecx, [edi + WDATA.box.width] + mov [draw_limits.right], ecx + mov ebx, [edi + WDATA.box.top] + mov [draw_limits.top], ebx + mov edx, ebx + add edx, [edi + WDATA.box.height] + mov [draw_limits.bottom], edx + +; DEBUGF 1, "K : minimize_window\n" +; DEBUGF 1, "K : dl_left %x\n",[draw_limits.left] +; DEBUGF 1, "K : dl_right %x\n",[draw_limits.right] +; DEBUGF 1, "K : dl_top %x\n",[draw_limits.top] +; DEBUGF 1, "K : dl_bottom %x\n",[draw_limits.bottom] + call calculatescreen +; xor esi, esi +; xor eax, eax + mov eax, edi + call redrawscreen +;-------------------------------------- +align 4 +@@: + pop esi edx ecx ebx eax +;-------------------------------------- +align 4 +.exit: + popfd + pop edi + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +restore_minimized_window: ;//////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;> eax = window number on screen +;------------------------------------------------------------------------------ +;# corrupts [dl*] +;------------------------------------------------------------------------------ + pushad + pushfd + cli + + ; is it already restored? + movzx esi, word[WIN_POS + eax * 2] + mov edi, esi + shl edi, 5 + add edi, window_data + test [edi + WDATA.fl_wstate], WSTATE_MINIMIZED + jz .exit + + ; no it's not, let's do that + mov [edi + WDATA.fl_redraw], 1 + and [edi + WDATA.fl_wstate], not WSTATE_MINIMIZED + mov ebp, window._.set_screen + cmp eax, [TASK_COUNT] + jz @f + mov ebp, calculatescreen +;-------------------------------------- +align 4 +@@: + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.top] + mov ecx, [edi + WDATA.box.width] + mov edx, [edi + WDATA.box.height] + add ecx, eax + add edx, ebx + call ebp + inc [_display.mask_seqno] +;-------------------------------------- +align 4 +.exit: + popfd + popad + ret +;------------------------------------------------------------------------------ +align 4 +; TODO: remove this proc +;------------------------------------------------------------------------------ +window_check_events: ;///////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + ; do we have window minimize/restore request? + cmp [window_minimize], 0 + je .exit + + ; okay, minimize or restore top-most window and exit + mov eax, [TASK_COUNT] + mov bl, 0 + xchg [window_minimize], bl + dec bl + jnz @f + call minimize_window + jmp .exit +;-------------------------------------- +align 4 +@@: + call restore_minimized_window +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +sys_window_maximize_handler: ;///////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> esi = process slot +;------------------------------------------------------------------------------ + mov edi, esi + shl edi, 5 + add edi, window_data + + ; can window change its height? + ; only types 2 and 3 can be resized + mov dl, [edi + WDATA.fl_wstyle] + test dl, 2 + jz .exit + + ; toggle normal/maximized window state + mov bl, [edi + WDATA.fl_wstate] + xor bl, WSTATE_MAXIMIZED + + ; calculate and set appropriate window bounds + test bl, WSTATE_MAXIMIZED + jz .restore_size + + mov eax, [screen_workarea.left] + mov ecx, [screen_workarea.top] + push [screen_workarea.bottom] \ + [screen_workarea.right] \ + ecx \ + eax + sub [esp + BOX.width], eax + sub [esp + BOX.height], ecx + mov eax, esp + jmp .set_box +;-------------------------------------- +align 4 +.restore_size: + mov eax, esi + shl eax, 8 + add eax, SLOT_BASE + APPDATA.saved_box + push [eax + BOX.height] \ + [eax + BOX.width] \ + [eax + BOX.top] \ + [eax + BOX.left] + mov eax, esp +;-------------------------------------- +align 4 +.set_box: + test bl, WSTATE_ROLLEDUP + jz @f + + xchg eax, ecx + call window._.get_rolledup_height + mov [ecx + BOX.height], eax + xchg eax, ecx +;-------------------------------------- +align 4 +@@: + call window._.set_window_box + add esp, sizeof.BOX +;-------------------------------------- +align 4 +.exit: + inc [_display.mask_seqno] + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +sys_window_rollup_handler: ;/////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> esi = process slot +;------------------------------------------------------------------------------ + mov edx, esi + shl edx, 8 + add edx, SLOT_BASE + + ; toggle normal/rolled up window state + mov bl, [edi + WDATA.fl_wstate] + xor bl, WSTATE_ROLLEDUP + + ; calculate and set appropriate window bounds + test bl, WSTATE_ROLLEDUP + jz .restore_size + + call window._.get_rolledup_height + push eax \ + [edi + WDATA.box.width] \ + [edi + WDATA.box.top] \ + [edi + WDATA.box.left] + mov eax, esp + jmp .set_box +;-------------------------------------- +align 4 +.restore_size: + test bl, WSTATE_MAXIMIZED + jnz @f + add esp, -sizeof.BOX + lea eax, [edx + APPDATA.saved_box] + jmp .set_box +;-------------------------------------- +align 4 +@@: + mov eax, [screen_workarea.top] + push [screen_workarea.bottom] \ + [edi + WDATA.box.width] \ + eax \ + [edi + WDATA.box.left] + sub [esp + BOX.height], eax + mov eax, esp +;-------------------------------------- +align 4 +.set_box: + call window._.set_window_box + add esp, sizeof.BOX + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +;sys_window_start_moving_handler: ;///////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = old (original) window box +;> esi = process slot +;------------------------------------------------------------------------------ +; mov edi, eax +; call window._.draw_negative_box +; ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +sys_window_end_moving_handler: ;/////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = old (original) window box +;> ebx = new (final) window box +;> esi = process slot +;------------------------------------------------------------------------------ +; mov edi, ebx +; call window._.end_moving__box + + mov edi, esi + shl edi, 5 + add edi, window_data + + mov eax, ebx + mov bl, [edi + WDATA.fl_wstate] + call window._.set_window_box + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +sys_window_moving_handler: ;/////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = old (from previous call) window box +;> ebx = new (current) window box +;> esi = process_slot +;------------------------------------------------------------------------------ + mov edi, eax + call window._.draw_negative_box + mov edi, ebx + call window._.draw_negative_box + ret +;============================================================================== +;///// private functions ////////////////////////////////////////////////////// +;============================================================================== + +iglobal + FuncTable syscall_display_settings, ftable, \ + 00, 01, 02, 03, 04, 05, 06, 07, 08 + + align 4 + window_topleft dd \ + 1, 21, \ ;type 0 + 0, 0, \ ;type 1 + 5, 20, \ ;type 2 + 5, ?, \ ;type 3 {set by skin} + 5, ? ;type 4 {set by skin} +endg + +;uglobal + ; NOTE: commented out since doesn't provide necessary functionality anyway, + ; to be reworked +; new_window_starting dd ? +;endg +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.invalidate_screen: ;////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = old (original) window box +;> ebx = new (final) window box +;> edi = pointer to WDATA struct +;------------------------------------------------------------------------------ + push eax ebx + + ; TODO: do we really need `draw_limits`? + ; Yes, they are used by background drawing code. + +; we need only to restore the background windows at the old place! + mov ecx, [ebx + BOX.left] + mov [draw_limits.left], ecx + add ecx, [ebx + BOX.width] + mov [draw_limits.right], ecx + mov ecx, [ebx + BOX.top] + mov [draw_limits.top], ecx + add ecx, [ebx + BOX.height] + mov [draw_limits.bottom], ecx +; recalculate screen buffer at old position + push ebx + mov edx, [eax + BOX.height] + mov ecx, [eax + BOX.width] + mov ebx, [eax + BOX.top] + mov eax, [eax + BOX.left] + add ecx, eax + add edx, ebx + call calculatescreen + pop eax +; recalculate screen buffer at new position + mov edx, [eax + BOX.height] + mov ecx, [eax + BOX.width] + mov ebx, [eax + BOX.top] + mov eax, [eax + BOX.left] + add ecx, eax + add edx, ebx + call calculatescreen + + mov eax, edi + call redrawscreen + + ; tell window to redraw itself + mov [edi + WDATA.fl_redraw], 1 + + pop ebx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.set_window_box: ;///////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> eax = pointer to BOX struct +;> bl = new window state flags +;> edi = pointer to WDATA struct +;------------------------------------------------------------------------------ + push eax ebx esi + +; don't do anything if the new box is identical to the old + cmp bl, [edi + WDATA.fl_wstate] + jnz @f + mov esi, eax + push edi +if WDATA.box + add edi, WDATA.box +end if + mov ecx, 4 + repz cmpsd + pop edi + jz .exit +;-------------------------------------- +align 4 +@@: + add esp, -sizeof.BOX + mov ebx, esp +if WDATA.box + lea esi, [edi + WDATA.box] +else + mov esi, edi ; optimization for WDATA.box = 0 +end if + xchg eax, esi + mov ecx, sizeof.BOX + call memmove + xchg eax, esi + xchg ebx, esi + call memmove + mov eax, ebx + mov ebx, esi + + call window._.check_window_position + call window._.set_window_clientbox + call window._.invalidate_screen + + add esp, sizeof.BOX + + mov cl, [esp + 4] + mov ch, cl + xchg cl, [edi + WDATA.fl_wstate] + + or cl, ch + test cl, WSTATE_MAXIMIZED + jnz .exit + + mov eax, edi + sub eax, window_data + shl eax, 3 + add eax, SLOT_BASE + + lea ebx, [edi + WDATA.box] + xchg esp, ebx + + pop [eax + APPDATA.saved_box.left] \ + [eax + APPDATA.saved_box.top] \ + [eax + APPDATA.saved_box.width] \ + edx + + xchg esp, ebx + + test ch, WSTATE_ROLLEDUP + jnz .exit + + mov [eax + APPDATA.saved_box.height], edx +;-------------------------------------- +align 4 +.exit: + pop esi ebx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.set_window_clientbox: ;/////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> edi = pointer to WDATA struct +;------------------------------------------------------------------------------ + push eax ecx edi + + mov eax, [_skinh] + mov [window_topleft + 8 * 3 + 4], eax + mov [window_topleft + 8 * 4 + 4], eax + + mov ecx, edi + sub edi, window_data + shl edi, 3 + test [ecx + WDATA.fl_wstyle], WSTYLE_CLIENTRELATIVE + jz .whole_window + + movzx eax, [ecx + WDATA.fl_wstyle] + and eax, 0x0F + mov eax, [eax * 8 + window_topleft + 0] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.left], eax + shl eax, 1 + neg eax + add eax, [ecx + WDATA.box.width] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.width], eax + + movzx eax, [ecx + WDATA.fl_wstyle] + and eax, 0x0F + push [eax * 8 + window_topleft + 0] + mov eax, [eax * 8 + window_topleft + 4] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.top], eax + neg eax + sub eax, [esp] + add eax, [ecx + WDATA.box.height] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.height], eax + add esp, 4 + jmp .exit +;-------------------------------------- +align 4 +.whole_window: + xor eax, eax + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.left], eax + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.top], eax + mov eax, [ecx + WDATA.box.width] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.width], eax + mov eax, [ecx + WDATA.box.height] + mov [edi + SLOT_BASE + APPDATA.wnd_clientbox.height], eax +;-------------------------------------- +align 4 +.exit: + pop edi ecx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.sys_set_window: ;///////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;< edx = pointer to WDATA struct +;------------------------------------------------------------------------------ + mov eax, [CURRENT_TASK] + shl eax, 5 + add eax, window_data + + ; save window colors + mov [eax + WDATA.cl_workarea], edx + mov [eax + WDATA.cl_titlebar], esi + mov [eax + WDATA.cl_frames], edi + + mov edi, eax + + ; was it already defined before? + test [edi + WDATA.fl_wdrawn], 1 + jnz .set_client_box + or [edi + WDATA.fl_wdrawn], 1 +; After first draw_window we need redraw mouse necessarily! +; 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] +; add eax, 100 +; mov [new_window_starting], eax + + ; no it wasn't, performing initial window definition + movzx eax, bx + mov [edi + WDATA.box.width], eax + movzx eax, cx + mov [edi + WDATA.box.height], eax + sar ebx, 16 + sar ecx, 16 + mov [edi + WDATA.box.left], ebx + mov [edi + WDATA.box.top], ecx + + call window._.check_window_position + + push ecx edi + + mov cl, [edi + WDATA.fl_wstyle] + mov eax, [edi + WDATA.cl_frames] + + sub edi, window_data + shl edi, 3 + add edi, SLOT_BASE + + and cl, 0x0F + cmp cl, 3 + je @f + cmp cl, 4 + je @f + + xor eax, eax +;-------------------------------------- +align 4 +@@: + mov [edi + APPDATA.wnd_caption], eax + + mov esi, [esp] + add edi, APPDATA.saved_box + movsd + movsd + movsd + movsd + + pop edi ecx + + mov esi, [CURRENT_TASK] + movzx esi, word[WIN_STACK + esi * 2] + lea esi, [WIN_POS + esi * 2] + call waredraw + + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.top] + mov ecx, [edi + WDATA.box.width] + mov edx, [edi + WDATA.box.height] + add ecx, eax + add edx, ebx + call calculatescreen + + mov byte[KEY_COUNT], 0 ; empty keyboard buffer + mov byte[BTN_COUNT], 0 ; empty button buffer +;-------------------------------------- +align 4 +.set_client_box: + ; update window client box coordinates + call window._.set_window_clientbox + + ; reset window redraw flag and exit + mov [edi + WDATA.fl_redraw], 0 + mov edx, edi + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.check_window_position: ;////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Check if window is inside screen area +;------------------------------------------------------------------------------ +;> edi = pointer to WDATA +;------------------------------------------------------------------------------ + push eax ebx ecx edx esi + + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.top] + mov ecx, [edi + WDATA.box.width] + mov edx, [edi + WDATA.box.height] + + mov esi, [Screen_Max_X] + cmp ecx, esi + ja .fix_width_high +;-------------------------------------- +align 4 +.check_left: + or eax, eax + jl .fix_left_low + add eax, ecx + cmp eax, esi + jg .fix_left_high +;-------------------------------------- +align 4 +.check_height: + mov esi, [Screen_Max_Y] + cmp edx, esi + ja .fix_height_high +;-------------------------------------- +align 4 +.check_top: + or ebx, ebx + jl .fix_top_low + add ebx, edx + cmp ebx, esi + jg .fix_top_high +;-------------------------------------- +align 4 +.exit: + pop esi edx ecx ebx eax + ret +;-------------------------------------- +align 4 +.fix_width_high: + mov ecx, esi + mov [edi + WDATA.box.width], esi + jmp .check_left +;-------------------------------------- +align 4 +.fix_left_low: + xor eax, eax + mov [edi + WDATA.box.left], eax + jmp .check_height +;-------------------------------------- +align 4 +.fix_left_high: + mov eax, esi + sub eax, ecx + mov [edi + WDATA.box.left], eax + jmp .check_height +;-------------------------------------- +align 4 +.fix_height_high: + mov edx, esi + mov [edi + WDATA.box.height], esi + jmp .check_top +;-------------------------------------- +align 4 +.fix_top_low: + xor ebx, ebx + mov [edi + WDATA.box.top], ebx + jmp .exit +;-------------------------------------- +align 4 +.fix_top_high: + mov ebx, esi + sub ebx, edx + mov [edi + WDATA.box.top], ebx + jmp .exit +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.get_titlebar_height: ;//////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> edi = pointer to WDATA +;------------------------------------------------------------------------------ + mov al, [edi + WDATA.fl_wstyle] + and al, 0x0f + cmp al, 0x03 + jne @f + mov eax, [_skinh] + ret +;-------------------------------------- +align 4 +@@: + mov eax, 21 + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.get_rolledup_height: ;//////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ +;> edi = pointer to WDATA +;------------------------------------------------------------------------------ + mov al, [edi + WDATA.fl_wstyle] + and al, 0x0f + cmp al, 0x03 + jb @f + mov eax, [_skinh] + add eax, 3 + ret +;-------------------------------------- +align 4 +@@: + or al, al + jnz @f + mov eax, 21 + ret +;-------------------------------------- +align 4 +@@: + mov eax, 21 + 2 + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.set_screen: ;///////////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Reserve window area in screen buffer +;------------------------------------------------------------------------------ +;> eax = left +;> ebx = top +;> ecx = right +;> edx = bottom +;> esi = process number +;------------------------------------------------------------------------------ +virtual at esp + ff_x dd ? + ff_y dd ? + ff_width dd ? + ff_xsz dd ? + ff_ysz dd ? + ff_scale dd ? +end virtual + + pushad + + cmp esi, 1 + jz .check_for_shaped_window + mov edi, esi + shl edi, 5 + cmp [window_data + edi + WDATA.box.width], 0 + jnz .check_for_shaped_window + cmp [window_data + edi + WDATA.box.height], 0 + jz .exit +;-------------------------------------- +align 4 +.check_for_shaped_window: + mov edi, esi + shl edi, 8 + add edi, SLOT_BASE + cmp [edi + APPDATA.wnd_shape], 0 + jne .shaped_window + + ; get x&y size + sub ecx, eax + sub edx, ebx + inc ecx + inc edx + + ; get WinMap start + push esi + mov esi, [_display.width] + mov edi, [d_width_calc_area + ebx*4] + + add edi, eax + add edi, [_WinMapAddress] + pop eax + mov ah, al + push ax + shl eax, 16 + pop ax +;-------------------------------------- +align 4 +.next_line: + push ecx + shr ecx, 2 + rep stosd + mov ecx, [esp] + and ecx, 3 + rep stosb + pop ecx + add edi, esi + sub edi, ecx + dec edx + jnz .next_line + + jmp .exit +;-------------------------------------- +align 4 +.shaped_window: + ; for (y=0; y <= x_size; y++) + ; for (x=0; x <= x_size; x++) + ; if (shape[coord(x,y,scale)]==1) + ; set_pixel(x, y, process_number); + + sub ecx, eax + sub edx, ebx + inc ecx + inc edx + + push [edi + APPDATA.wnd_shape_scale] ; push scale first -> for loop + + ; get WinMap start -> ebp + push eax + mov eax, [d_width_calc_area + ebx*4] + + add eax, [esp] + add eax, [_WinMapAddress] + mov ebp, eax + + mov edi, [edi + APPDATA.wnd_shape] + pop eax + + ; eax = x_start + ; ebx = y_start + ; ecx = x_size + ; edx = y_size + ; esi = process_number + ; edi = &shape + ; [scale] + push edx ecx ; for loop - x,y size + + mov ecx, esi + shl ecx, 5 + mov edx, [window_data + ecx + WDATA.box.top] + push [window_data + ecx + WDATA.box.width] ; for loop - width + mov ecx, [window_data + ecx + WDATA.box.left] + sub ebx, edx + sub eax, ecx + push ebx eax ; for loop - x,y + + add [ff_xsz], eax + add [ff_ysz], ebx + + mov ebx, [ff_y] +;-------------------------------------- +align 4 +.ff_new_y: + mov edx, [ff_x] +;-------------------------------------- +align 4 +.ff_new_x: + ; -- body -- + mov ecx, [ff_scale] + mov eax, [ff_width] + inc eax + shr eax, cl + push ebx edx + shr ebx, cl + shr edx, cl + imul eax, ebx + add eax, edx + pop edx ebx + add eax, edi + call .read_byte + test al, al + jz @f + mov eax, esi + mov [ebp], al + ; -- end body -- +;-------------------------------------- +align 4 +@@: + inc ebp + inc edx + cmp edx, [ff_xsz] + jb .ff_new_x + + sub ebp, [ff_xsz] + add ebp, [ff_x] + add ebp, [Screen_Max_X] ; screen.x + inc ebp + inc ebx + cmp ebx, [ff_ysz] + jb .ff_new_y + + add esp, 24 +;-------------------------------------- +align 4 +.exit: + popad + inc [_display.mask_seqno] + ret +;-------------------------------------- +align 4 +.read_byte: + ; eax - address + ; esi - slot + push eax ecx edx esi + xchg eax, esi + lea ecx, [esp + 12] + mov edx, 1 + call read_process_memory + pop esi edx ecx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.window_activate: ;//////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Activate window +;------------------------------------------------------------------------------ +;> esi = pointer to WIN_POS+ window data +;------------------------------------------------------------------------------ + push eax ebx + + ; if type of current active window is 3 or 4, it must be redrawn + mov ebx, [TASK_COUNT] + +; DEBUGF 1, "K : TASK_COUNT (0x%x)\n", ebx + + movzx ebx, word[WIN_POS + ebx * 2] + shl ebx, 5 + add eax, window_data + mov al, [window_data + ebx + WDATA.fl_wstyle] + and al, 0x0f + cmp al, 0x03 + je .set_window_redraw_flag + cmp al, 0x04 + jne .move_others_down +;-------------------------------------- +align 4 +.set_window_redraw_flag: + mov [window_data + ebx + WDATA.fl_redraw], 1 +;-------------------------------------- +align 4 +.move_others_down: + ; ax <- process no + movzx ebx, word[esi] + ; ax <- position in window stack + movzx ebx, word[WIN_STACK + ebx * 2] + + ; drop others + xor eax, eax +;-------------------------------------- +align 4 +.next_stack_window: + cmp eax, [TASK_COUNT] + jae .move_self_up + inc eax + +; push ebx +; xor ebx,ebx +; mov bx,[WIN_STACK + eax * 2] +; DEBUGF 1, "K : DEC WIN_STACK (0x%x)\n",ebx +; pop ebx + + cmp [WIN_STACK + eax * 2], bx + jbe .next_stack_window + dec word[WIN_STACK + eax * 2] + jmp .next_stack_window +;-------------------------------------- +align 4 +.move_self_up: + movzx ebx, word[esi] + ; number of processes + mov ax, [TASK_COUNT] + ; this is the last (and the upper) + mov [WIN_STACK + ebx * 2], ax + + ; update on screen - window stack + xor eax, eax +;-------------------------------------- +align 4 +.next_window_pos: + cmp eax, [TASK_COUNT] + jae .reset_vars + inc eax + movzx ebx, word[WIN_STACK + eax * 2] + mov [WIN_POS + ebx * 2], ax + jmp .next_window_pos +;-------------------------------------- +align 4 +.reset_vars: + mov byte[KEY_COUNT], 0 + mov byte[BTN_COUNT], 0 + mov word[MOUSE_SCROLL_H], 0 + mov word[MOUSE_SCROLL_V], 0 + + pop ebx eax + ret +;------------------------------------------------------------------------------ +window._.window_deactivate: ;//////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Deactivate window +;------------------------------------------------------------------------------ +;> esi = pointer to WIN_POS+ window data +;------------------------------------------------------------------------------ + push eax ebx +;-------------------------------------- +align 4 +.move_others_up: + ; ax <- process no + movzx ebx, word[esi] + ; ax <- position in window stack + movzx ebx, word[WIN_STACK + ebx * 2] + ; up others + xor eax, eax +;-------------------------------------- +align 4 +.next_stack_window: + cmp eax, [TASK_COUNT] + jae .move_self_down + inc eax + cmp [WIN_STACK + eax * 2], bx + jae .next_stack_window + inc word[WIN_STACK + eax * 2] + jmp .next_stack_window +;-------------------------------------- +align 4 +.move_self_down: + movzx ebx, word[esi] + ; this is the last (and the low) + mov [WIN_STACK + ebx * 2], word 1 + ; update on screen - window stack + xor eax, eax +;-------------------------------------- +align 4 +.next_window_pos: + cmp eax, [TASK_COUNT] + jae .reset_vars + inc eax + movzx ebx, word[WIN_STACK + eax * 2] + mov [WIN_POS + ebx * 2], ax + jmp .next_window_pos +;-------------------------------------- +align 4 +.reset_vars: + mov byte[KEY_COUNT], 0 + mov byte[BTN_COUNT], 0 + mov word[MOUSE_SCROLL_H], 0 + mov word[MOUSE_SCROLL_V], 0 + pop ebx eax + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.check_window_draw: ;////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Check if window is necessary to draw +;------------------------------------------------------------------------------ +;> edi = pointer to WDATA +;------------------------------------------------------------------------------ + mov cl, [edi + WDATA.fl_wstyle] + and cl, 0x0f + cmp cl, 3 + je .exit.redraw ; window type 3 + cmp cl, 4 + je .exit.redraw ; window type 4 + + push eax ebx edx esi + + mov eax, edi + sub eax, window_data + shr eax, 5 + + movzx eax, word[WIN_STACK + eax * 2] ; get value of the curr process + lea esi, [WIN_POS + eax * 2] ; get address of this process at 0xC400 +;-------------------------------------- +align 4 +.next_window: + add esi, 2 + + mov eax, [TASK_COUNT] + lea eax, word[WIN_POS + eax * 2] ; number of the upper window + + cmp esi, eax + ja .exit.no_redraw + + movzx edx, word[esi] + shl edx, 5 + cmp [CURRENT_TASK + edx + TASKDATA.state], TSTATE_FREE + je .next_window + + mov eax, [edi + WDATA.box.top] + mov ebx, [edi + WDATA.box.height] + add ebx, eax + + mov ecx, [window_data + edx + WDATA.box.top] + cmp ecx, ebx + jge .next_window + add ecx, [window_data + edx + WDATA.box.height] + cmp eax, ecx + jge .next_window + + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.width] + add ebx, eax + + mov ecx, [window_data + edx + WDATA.box.left] + cmp ecx, ebx + jge .next_window + add ecx, [window_data + edx + WDATA.box.width] + cmp eax, ecx + jge .next_window + + pop esi edx ebx eax +;-------------------------------------- +align 4 +.exit.redraw: + xor ecx, ecx + inc ecx + ret +;-------------------------------------- +align 4 +.exit.no_redraw: + pop esi edx ebx eax + xor ecx, ecx + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.draw_window_caption: ;//////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? +;------------------------------------------------------------------------------ + xor eax, eax + mov edx, [TASK_COUNT] + movzx edx, word[WIN_POS + edx * 2] + cmp edx, [CURRENT_TASK] + jne @f + inc eax +;-------------------------------------- +align 4 +@@: + mov edx, [CURRENT_TASK] + shl edx, 5 + add edx, window_data + movzx ebx, [edx + WDATA.fl_wstyle] + and bl, 0x0F + cmp bl, 3 + je .draw_caption_style_3 + cmp bl, 4 + je .draw_caption_style_3 + + jmp .not_style_3 +;-------------------------------------- +align 4 +.draw_caption_style_3: + push edx + call drawwindow_IV_caption + add esp, 4 + jmp .2 +;-------------------------------------- +align 4 +.not_style_3: + cmp bl, 2 + jne .not_style_2 + + call drawwindow_III_caption + jmp .2 +;-------------------------------------- +align 4 +.not_style_2: + cmp bl, 0 + jne .2 + + call drawwindow_I_caption +;-------------------------------------- +align 4 +.2: + mov edi, [CURRENT_TASK] + shl edi, 5 + test [edi + window_data + WDATA.fl_wstyle], WSTYLE_HASCAPTION + jz .exit + mov edx, [edi * 8 + SLOT_BASE + APPDATA.wnd_caption] + or edx, edx + jz .exit + + movzx eax, [edi + window_data + WDATA.fl_wstyle] + and al, 0x0F + cmp al, 3 + je .skinned + cmp al, 4 + je .skinned + + jmp .not_skinned +;-------------------------------------- +align 4 +.skinned: + mov ebp, [edi + window_data + WDATA.box.left - 2] + mov bp, word[edi + window_data + WDATA.box.top] + movzx eax, word[edi + window_data + WDATA.box.width] + sub ax, [_skinmargins.left] + sub ax, [_skinmargins.right] + push edx + cwde + cdq + mov ebx, 6 + idiv ebx + pop edx + or eax, eax + js .exit + + mov esi, eax + mov ebx, dword[_skinmargins.left - 2] + mov bx, word[_skinh] + sub bx, [_skinmargins.bottom] + sub bx, [_skinmargins.top] + sar bx, 1 + adc bx, 0 + add bx, [_skinmargins.top] + add bx, -3 + add ebx, ebp + jmp .dodraw +;-------------------------------------- +align 4 +.not_skinned: + cmp al, 1 + je .exit + + mov ebp, [edi + window_data + WDATA.box.left - 2] + mov bp, word[edi + window_data + WDATA.box.top] + movzx eax, word[edi + window_data + WDATA.box.width] + sub eax, 16 + push edx + cwde + cdq + mov ebx, 6 + idiv ebx + pop edx + or eax, eax + js .exit + + mov esi, eax + mov ebx, 0x00080007 + add ebx, ebp +;-------------------------------------- +align 4 +.dodraw: + mov ecx, [common_colours + 16] + or ecx, 0x80000000 + xor edi, edi + call dtext_asciiz_esi +;-------------------------------------- +align 4 +.exit: +; call [draw_pointer] + call __sys_draw_pointer + ret +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.draw_negative_box: ;////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Draw negative box +;------------------------------------------------------------------------------ +;> edi = pointer to BOX struct +;------------------------------------------------------------------------------ + push eax ebx esi + mov esi, 0x01000000 +;-------------------------------------- +align 4 +.1: + mov eax, [edi + BOX.left - 2] + mov ax, word[edi + BOX.left] + add ax, word[edi + BOX.width] + mov ebx, [edi + BOX.top - 2] + mov bx, word[edi + BOX.top] + add bx, word[edi + BOX.height] + call draw_rectangle.forced + pop esi ebx eax + ret +;------------------------------------------------------------------------------ +;align 4 +;------------------------------------------------------------------------------ +;window._.end_moving__box: ;////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? Draw positive box +;------------------------------------------------------------------------------ +;> edi = pointer to BOX struct +;------------------------------------------------------------------------------ +; push eax ebx esi +; xor esi, esi +; jmp window._.draw_negative_box.1 +;------------------------------------------------------------------------------ +align 4 +;------------------------------------------------------------------------------ +window._.get_rect: ;///////////////////////////////////////////////////// +;------------------------------------------------------------------------------ +;? void __fastcall get_window_rect(struct RECT* rc); +;------------------------------------------------------------------------------ +;> ecx = pointer to RECT +;------------------------------------------------------------------------------ + mov eax, [TASK_BASE] + + mov edx, [eax-twdw + WDATA.box.left] + mov [ecx+RECT.left], edx + + add edx, [eax-twdw + WDATA.box.width] + mov [ecx+RECT.right], edx + + mov edx, [eax-twdw + WDATA.box.top] + mov [ecx+RECT.top], edx + + add edx, [eax-twdw + WDATA.box.height] + mov [ecx+RECT.bottom], edx + ret +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-process/hid/keyboard.inc b/kernel/branches/kolibri-process/hid/keyboard.inc new file mode 100644 index 000000000..78e5580a6 --- /dev/null +++ b/kernel/branches/kolibri-process/hid/keyboard.inc @@ -0,0 +1,556 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3598 $ + + +VKEY_LSHIFT = 0000000000000001b +VKEY_RSHIFT = 0000000000000010b +VKEY_LCONTROL = 0000000000000100b +VKEY_RCONTROL = 0000000000001000b +VKEY_LALT = 0000000000010000b +VKEY_RALT = 0000000000100000b +VKEY_CAPSLOCK = 0000000001000000b +VKEY_NUMLOCK = 0000000010000000b +VKEY_SCRLOCK = 0000000100000000b +VKEY_LWIN = 0000001000000000b +VKEY_RWIN = 0000010000000000b + +VKEY_SHIFT = 0000000000000011b +VKEY_CONTROL = 0000000000001100b +VKEY_ALT = 0000000000110000b + +uglobal + align 4 + kb_state dd 0 + ext_code db 0 + + keyboard_mode db 0 + keyboard_data db 0 + + altmouseb db 0 + ctrl_alt_del db 0 + + kb_lights db 0 + old_kb_lights db 0 + +align 4 + hotkey_scancodes rd 256 ; we have 256 scancodes + hotkey_list rd 256*4 ; max 256 defined hotkeys + hotkey_buffer rd 120*2 ; buffer for 120 hotkeys +endg + +iglobal +hotkey_tests dd hotkey_test0 + dd hotkey_test1 + dd hotkey_test2 + dd hotkey_test3 + dd hotkey_test4 +hotkey_tests_num = 5 +endg +;--------------------------------------------------------------------- +hotkey_test0: + test al, al + setz al + ret +;--------------------------------------------------------------------- +hotkey_test1: + test al, al + setnp al + ret +;--------------------------------------------------------------------- +hotkey_test2: + cmp al, 3 + setz al + ret +;--------------------------------------------------------------------- +hotkey_test3: + cmp al, 1 + setz al + ret +;--------------------------------------------------------------------- +hotkey_test4: + cmp al, 2 + setz al + ret +;--------------------------------------------------------------------- +hotkey_do_test: + push eax + mov edx, [kb_state] + shr edx, cl + add cl, cl + mov eax, [eax+4] + shr eax, cl + and eax, 15 + cmp al, hotkey_tests_num + jae .fail + + xchg eax, edx + and al, 3 + call [hotkey_tests + edx*4] + cmp al, 1 + pop eax + ret +;-------------------------------------- +.fail: + stc + pop eax + ret +;--------------------------------------------------------------------- +align 4 +set_keyboard_data: + movzx eax, word[TASK_COUNT]; top window process + movzx eax, word[WIN_POS+eax*2] + shl eax, 8 + mov al, [SLOT_BASE+eax+APPDATA.keyboard_mode] + mov [keyboard_mode], al + + mov eax, ecx + + push ebx esi edi ebp + call send_scancode + pop ebp edi esi ebx + ret +;--------------------------------------------------------------------- +struct KEYBOARD +next dd ? +prev dd ? +functions dd ? +userdata dd ? +ends +struct KBDFUNC +strucsize dd ? +close dd ? +setlights dd ? +ends + +iglobal +keyboards: + dd keyboards + dd keyboards +endg +uglobal +keyboard_list_mutex MUTEX +endg + +register_keyboard: + push ebx + movi eax, sizeof.KEYBOARD + call malloc + test eax, eax + jz .nothing + mov ecx, [esp+4+4] + mov [eax+KEYBOARD.functions], ecx + mov ecx, [esp+8+4] + mov [eax+KEYBOARD.userdata], ecx + xchg eax, ebx + mov ecx, keyboard_list_mutex + call mutex_lock + mov ecx, keyboards + mov edx, [ecx+KEYBOARD.prev] + mov [ebx+KEYBOARD.next], ecx + mov [ebx+KEYBOARD.prev], edx + mov [edx+KEYBOARD.next], ebx + mov [ecx+KEYBOARD.prev], ebx + mov ecx, [ebx+KEYBOARD.functions] + cmp [ecx+KBDFUNC.strucsize], KBDFUNC.setlights + jbe .unlock + mov ecx, [ecx+KBDFUNC.setlights] + test ecx, ecx + jz .unlock + stdcall ecx, [ebx+KEYBOARD.userdata], dword [kb_lights] +.unlock: + mov ecx, keyboard_list_mutex + call mutex_unlock + xchg eax, ebx +.nothing: + pop ebx + ret 8 + +delete_keyboard: + push ebx + mov ebx, [esp+4+4] + mov ecx, keyboard_list_mutex + call mutex_lock + mov eax, [ebx+KEYBOARD.next] + mov edx, [ebx+KEYBOARD.prev] + mov [eax+KEYBOARD.prev], edx + mov [edx+KEYBOARD.next], eax + call mutex_unlock + mov ecx, [ebx+KEYBOARD.functions] + cmp [ecx+KBDFUNC.strucsize], KBDFUNC.close + jbe .nothing + mov ecx, [ecx+KBDFUNC.close] + test ecx, ecx + jz .nothing + stdcall ecx, [ebx+KEYBOARD.userdata] +.nothing: + pop ebx + ret 4 +;--------------------------------------------------------------------- +align 4 +irq1: + movzx eax, word[TASK_COUNT]; top window process + movzx eax, word[WIN_POS+eax*2] + shl eax, 8 + mov al, [SLOT_BASE+eax+APPDATA.keyboard_mode] + mov [keyboard_mode], al + + in al, 0x60 +;-------------------------------------- +send_scancode: + mov [keyboard_data], al +; ch = scancode +; cl = ext_code +; bh = 0 - normal key +; bh = 1 - modifier (Shift/Ctrl/Alt) +; bh = 2 - extended code + mov ch, al + cmp al, 0xE0 + je @f + + cmp al, 0xE1 + jne .normal_code +@@: + mov bh, 2 + mov [ext_code], al + jmp .writekey +;-------------------------------------- +.normal_code: + mov cl, 0 + xchg cl, [ext_code] + and al, 0x7F + mov bh, 1 +;-------------------------------------- +@@: + cmp al, 0x5B + jne @f + + cmp cl, 0xE0 + jne @f + + mov eax, VKEY_LWIN + mov bh, 0 + jmp .modifier +;-------------------------------------- +@@: + cmp al, 0x5C + jne @f + + cmp cl, 0xE0 + jne @f + + mov eax, VKEY_RWIN + mov bh, 0 + jmp .modifier +;-------------------------------------- +@@: + cmp al, 0x2A + jne @f + + cmp cl, 0xE0 + je .writekey + + mov eax, VKEY_LSHIFT + jmp .modifier +;-------------------------------------- +@@: + cmp al, 0x36 + jne @f + + cmp cl, 0xE0 + je .writekey + + mov eax, VKEY_RSHIFT + jmp .modifier +;-------------------------------------- +@@: + cmp al, 0x38 + jne @f + + mov eax, VKEY_LALT + test cl, cl + jz .modifier + + mov al, VKEY_RALT + jmp .modifier +;-------------------------------------- +@@: + cmp al, 0x1D + jne @f + + mov eax, VKEY_LCONTROL + test cl, cl + jz .modifier + + mov al, VKEY_RCONTROL + cmp cl, 0xE0 + jz .modifier + + mov [ext_code], cl + jmp .writekey +;-------------------------------------- +@@: + cmp al, 0x3A + jne @f + + mov bl, 4 + mov eax, VKEY_CAPSLOCK + jmp .no_key.xor +;-------------------------------------- +@@: + cmp al, 0x45 + jne @f + test cl, cl + jnz .writekey + + mov bl, 2 + mov eax, VKEY_NUMLOCK + jmp .no_key.xor +;-------------------------------------- +@@: + cmp al, 0x46 + jne @f + + mov bl, 1 + mov eax, VKEY_SCRLOCK + jmp .no_key.xor +;-------------------------------------- +@@: + xor ebx, ebx + test ch, ch + js .writekey + + movzx eax, ch ; plain key + mov bl, [keymap+eax] + mov edx, [kb_state] + test dl, VKEY_CONTROL ; ctrl alt del + jz .noctrlaltdel + + test dl, VKEY_ALT + jz .noctrlaltdel + + cmp ch, 53h + jne .noctrlaltdel + + mov [ctrl_alt_del], 1 + call wakeup_osloop +.noctrlaltdel: + test dl, VKEY_CONTROL ; ctrl on ? + jz @f + + sub bl, 0x60 +@@: + test dl, VKEY_CAPSLOCK ; caps lock on ? + jz .no_caps_lock + + test dl, VKEY_SHIFT ; shift on ? + jz .keymap_shif + + jmp @f +;-------------------------------------- +.no_caps_lock: + test dl, VKEY_SHIFT ; shift on ? + jz @f +.keymap_shif: + mov bl, [keymap_shift+eax] +@@: + test dl, VKEY_ALT ; alt on ? + jz @f + + mov bl, [keymap_alt+eax] +@@: + jmp .writekey +;-------------------------------------- +.modifier: + test ch, ch + js .modifier.up + or [kb_state], eax + jmp .writekey +;-------------------------------------- +.modifier.up: + not eax + and [kb_state], eax + jmp .writekey +;-------------------------------------- +.no_key.xor: + mov bh, 0 + test ch, ch + js .writekey + + xor [kb_state], eax + xor [kb_lights], bl +.writekey: + pushad +; test for system hotkeys + movzx eax, ch + cmp bh, 1 + ja .nohotkey + jb @f + + xor eax, eax +@@: + mov eax, [hotkey_scancodes + eax*4] +.hotkey_loop: + test eax, eax + jz .nohotkey + + mov cl, 0 + call hotkey_do_test + jc .hotkey_cont + + mov cl, 2 + call hotkey_do_test + jc .hotkey_cont + + mov cl, 4 + call hotkey_do_test + jnc .hotkey_found +.hotkey_cont: + mov eax, [eax] + jmp .hotkey_loop +;-------------------------------------- +.hotkey_found: + mov eax, [eax+8] +; put key in buffer for process in slot eax + mov edi, hotkey_buffer +@@: + cmp dword [edi], 0 + jz .found_free + + add edi, 8 + cmp edi, hotkey_buffer+120*8 + jb @b +; no free space - replace first entry + mov edi, hotkey_buffer +.found_free: + mov [edi], eax + movzx eax, ch + cmp bh, 1 + jnz @f + + xor eax, eax +@@: + mov [edi+4], ax + mov eax, [kb_state] + mov [edi+6], ax + + cmp [PID_lock_input], dword 0 + je .nohotkey + + popad + jmp .exit.irq1 +;-------------------------------------- +.nohotkey: + popad + + cmp [keyboard_mode], 0; return from keymap + jne .scancode + + test bh, bh + jnz .exit.irq1 + + test bl, bl + jz .exit.irq1 + + test [kb_state], VKEY_NUMLOCK + jz .dowrite + + cmp cl, 0xE0 + jz .dowrite + + cmp ch, 55 + jnz @f + + mov bl, 0x2A ;* + jmp .dowrite +;-------------------------------------- +@@: + cmp ch, 71 + jb .dowrite + + cmp ch, 83 + ja .dowrite + + movzx eax, ch + mov bl, [numlock_map + eax - 71] + jmp .dowrite +;-------------------------------------- +.scancode: + mov bl, ch +.dowrite: + movzx eax, byte[KEY_COUNT] + cmp al, 120 + jae .exit.irq1 + inc eax + mov [KEY_COUNT], al + mov [KEY_COUNT+eax], bl +.exit.irq1: + ret +;--------------------------------------------------------------------- +set_lights: + push ebx esi + mov ecx, keyboard_list_mutex + call mutex_lock + mov esi, keyboards +.loop: + mov esi, [esi+KEYBOARD.next] + cmp esi, keyboards + jz .done + mov eax, [esi+KEYBOARD.functions] + cmp dword [eax], KBDFUNC.setlights + jbe .loop + mov eax, [eax+KBDFUNC.setlights] + test eax, eax + jz .loop + stdcall eax, [esi+KEYBOARD.userdata], dword [kb_lights] + jmp .loop +.done: + mov ecx, keyboard_list_mutex + call mutex_unlock + pop esi ebx + ret + +ps2_set_lights: + stdcall disable_irq, 1 + mov al, 0xED + call kb_write + mov al, [esp+8] + call kb_write + stdcall enable_irq, 1 + ret 8 + +;// mike.dld ] +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 +.nothing: + ret +;--------------------------------------------------------------------- +numlock_map: + db 0x37 ;Num 7 + db 0x38 ;Num 8 + db 0x39 ;Num 9 + db 0x2D ;Num - + db 0x34 ;Num 4 + db 0x35 ;Num 5 + db 0x36 ;Num 6 + db 0x2B ;Num + + db 0x31 ;Num 1 + db 0x32 ;Num 2 + db 0x33 ;Num 3 + db 0x30 ;Num 0 + db 0x2E ;Num . +;--------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-process/hid/mousedrv.inc b/kernel/branches/kolibri-process/hid/mousedrv.inc new file mode 100644 index 000000000..9dbabe70b --- /dev/null +++ b/kernel/branches/kolibri-process/hid/mousedrv.inc @@ -0,0 +1,553 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3606 $ + + +; check mouse +; +; +; FB00 -> FB0F mouse memory 00 chunk count - FB0A-B x - FB0C-D y +; FB10 -> FB17 mouse color mem +; FB21 x move +; FB22 y move +; FB30 color temp +; FB28 high bits temp +; FB4A -> FB4D FB4A-B x-under - FB4C-D y-under +; FC00 -> FCFE com1/ps2 buffer +; FCFF com1/ps2 buffer count starting from FC00 + +uglobal +;-------------------------------------- +align 4 + mousecount dd 0x0 + mousedata dd 0x0 +Y_UNDER_sub_CUR_hot_y_add_curh: + dw 0 +Y_UNDER_subtraction_CUR_hot_y: + dw 0 +X_UNDER_sub_CUR_hot_x_add_curh: + dw 0 +X_UNDER_subtraction_CUR_hot_x: + dw 0 +endg + +iglobal +;-------------------------------------- +align 4 +mouse_delay dd 10 +mouse_speed_factor: + dd 3 +mouse_timer_ticks dd 0 +endg +;----------------------------------------------------------------------------- +align 4 +draw_mouse_under: + ; return old picture + cmp [_display.restore_cursor], 0 + je @F + + pushad + movzx eax, word [X_UNDER] + movzx ebx, word [Y_UNDER] + stdcall [_display.restore_cursor], eax, ebx + popad + ret +;-------------------------------------- +align 4 +@@: + pushad + xor ecx, ecx + xor edx, edx +;-------------------------------------- +align 4 +mres: + movzx eax, word [X_UNDER] + movzx ebx, word [Y_UNDER] + add eax, ecx + add ebx, edx + push ecx + push edx + push eax + push ebx + mov eax, edx + shl eax, 6 + shl ecx, 2 + add eax, ecx + add eax, mouseunder + mov ecx, [eax] + pop ebx + pop eax + mov edi, 1 ; force + or ecx, 0x04000000 ; don't save to mouseunder area +; call [putpixel] + call __sys_putpixel + pop edx + pop ecx + inc ecx + cmp ecx, 16 + jnz mres + xor ecx, ecx + inc edx + cmp edx, 24 + jnz mres + popad + ret +;----------------------------------------------------------------------------- +align 4 +save_draw_mouse: + cmp [_display.move_cursor], 0 + je .no_hw_cursor + pushad + + mov [X_UNDER], ax + mov [Y_UNDER], bx + movzx eax, word [MOUSE_Y] + movzx ebx, word [MOUSE_X] + push eax + push ebx + +; mov ecx, [Screen_Max_X] +; inc ecx +; mul ecx + mov eax, [d_width_calc_area + eax*4] + + add eax, [_WinMapAddress] + movzx edx, byte [ebx+eax] + shl edx, 8 + mov esi, [edx+SLOT_BASE+APPDATA.cursor] + + cmp esi, [current_cursor] + je .draw + + mov eax, [TASK_COUNT] + movzx eax, word [WIN_POS+eax*2] + shl eax, 8 + + cmp eax, edx + je @F + + mov esi, [def_cursor] + cmp esi, [current_cursor] + je .draw + +@@: + push esi + call [_display.select_cursor] + mov [current_cursor], esi +;-------------------------------------- +align 4 +.draw: + stdcall [_display.move_cursor], esi + popad + ret +;-------------------------------------- +;align 4 +;.fail: +; mov ecx, [def_cursor] +; mov [edx+SLOT_BASE+APPDATA.cursor], ecx +; stdcall [_display.move_cursor], ecx ; stdcall: [esp]=ebx,eax +; popad +; ret +;-------------------------------------- +align 4 +.no_hw_cursor: + pushad + ; save & draw + mov [X_UNDER], ax + mov [Y_UNDER], bx + push eax + push ebx + mov ecx, 0 + mov edx, 0 +;-------------------------------------- +align 4 +drm: + push eax + push ebx + push ecx + push edx + ; helloworld + push ecx + add eax, ecx; save picture under mouse + add ebx, edx + push ecx + or ecx, 0x04000000 ; don't load to mouseunder area + call getpixel + mov [COLOR_TEMP], ecx + pop ecx + mov eax, edx + shl eax, 6 + shl ecx, 2 + add eax, ecx + add eax, mouseunder + mov ebx, [COLOR_TEMP] + and ebx, 0xffffff + mov [eax], ebx + pop ecx + mov edi, edx ; y cycle + shl edi, 4 ; *16 bytes per row + add edi, ecx ; x cycle + mov esi, edi + add edi, esi + add edi, esi ; *3 + add edi, [MOUSE_PICTURE] ; we have our str address + mov esi, edi + add esi, 16*24*3 + push ecx + mov ecx, [COLOR_TEMP] + call combine_colors + and ecx, 0xffffff + mov [MOUSE_COLOR_MEM], ecx + pop ecx + pop edx + pop ecx + pop ebx + pop eax + add eax, ecx ; we have x coord+cycle + add ebx, edx ; and y coord+cycle + push ecx + mov ecx, [MOUSE_COLOR_MEM] + mov edi, 1 ; force + or ecx, 0x04000000 ; don't save to mouseunder area +; call [putpixel] + call __sys_putpixel + pop ecx + mov ebx, [esp+0] ; pure y coord again + mov eax, [esp+4] ; and x + inc ecx ; +1 cycle + cmp ecx, 16 ; if more than 16 + jnz drm + xor ecx, ecx + inc edx + cmp edx, 24 + jnz drm + add esp, 8 + popad + ret +;----------------------------------------------------------------------------- +align 4 +combine_colors: + ; in + ; ecx - color ( 00 RR GG BB ) + ; edi - ref to new color byte + ; esi - ref to alpha byte + ; + ; out + ; ecx - new color ( roughly (ecx*[esi]>>8)+([edi]*[esi]>>8) ) + push eax + push ebx + push edx + push ecx + xor ecx, ecx + ; byte 2 + mov eax, 0xff + sub al, [esi+0] + mov ebx, [esp] + shr ebx, 16 + and ebx, 0xff + mul ebx + shr eax, 8 + add ecx, eax + xor eax, eax + xor ebx, ebx + mov al, [edi+0] + mov bl, [esi+0] + mul ebx + shr eax, 8 + add ecx, eax + shl ecx, 8 + ; byte 1 + mov eax, 0xff + sub al, [esi+1] + mov ebx, [esp] + shr ebx, 8 + and ebx, 0xff + mul ebx + shr eax, 8 + add ecx, eax + xor eax, eax + xor ebx, ebx + mov al, [edi+1] + mov bl, [esi+1] + mul ebx + shr eax, 8 + add ecx, eax + shl ecx, 8 + ; byte 2 + mov eax, 0xff + sub al, [esi+2] + mov ebx, [esp] + and ebx, 0xff + mul ebx + shr eax, 8 + add ecx, eax + xor eax, eax + xor ebx, ebx + mov al, [edi+2] + mov bl, [esi+2] + mul ebx + shr eax, 8 + add ecx, eax + pop eax + pop edx + pop ebx + pop eax + ret +;----------------------------------------------------------------------------- +align 4 +check_mouse_area_for_getpixel: +; in: +; eax = x +; ebx = y +; out: +; ecx = new color + push eax ebx +; check for Y + xor ecx, ecx + mov cx, [Y_UNDER] ; [MOUSE_Y] + + cmp ebx, ecx + jb .no_mouse_area + add ecx, 23 ; mouse cursor Y size + cmp ebx, ecx + ja .no_mouse_area +; offset Y + sub bx, [Y_UNDER] ;[MOUSE_Y] +;-------------------------------------- +; check for X + xor ecx, ecx + mov cx, [X_UNDER] ;[MOUSE_X] + cmp eax, ecx + jb .no_mouse_area + add ecx, 15 ; mouse cursor X size + cmp eax, ecx + ja .no_mouse_area +; offset X + sub ax, [X_UNDER] ;[MOUSE_X] +;-------------------------------------- +; eax = offset x +; ebx = offset y + shl ebx, 6 ;y + shl eax, 2 ;x + add eax, ebx + add eax, mouseunder + mov ecx, [eax] + and ecx, 0xffffff + or ecx, 0xff000000 + pop ebx eax + ret +;-------------------------------------- +align 4 +.no_mouse_area: + xor ecx, ecx + pop ebx eax + ret +;----------------------------------------------------------------------------- +align 4 +check_mouse_area_for_putpixel: +; in: +; ecx = x shl 16 + y +; eax = color +; out: +; eax = new color + push eax +; check for Y + mov ax, [Y_UNDER] ; [MOUSE_Y] + cmp cx, ax + jb .no_mouse_area + add ax, 23 ; mouse cursor Y size + cmp cx, ax + ja .no_mouse_area +; offset Y + sub cx, [Y_UNDER] ;[MOUSE_Y] + mov ax, cx + shl eax, 16 +;-------------------------------------- +; check for X + mov ax, [X_UNDER] ;[MOUSE_X] + shr ecx, 16 + cmp cx, ax + jb .no_mouse_area + add ax, 15 ; mouse cursor X size + cmp cx, ax + ja .no_mouse_area +; offset X + sub cx, [X_UNDER] ;[MOUSE_X] + mov ax, cx +;-------------------------------------- +; eax = (offset y) shl 16 + (offset x) + + pop ecx + + push eax ebx + + mov ebx, eax + shr ebx, 16 ;y + and eax, 0xffff ;x + + shl ebx, 6 + shl eax, 2 + add eax, ebx + add eax, mouseunder + and ecx, 0xFFFFFF + mov [eax], ecx + + pop ebx eax + + push esi edi + rol eax, 16 + movzx edi, ax ; y cycle + shl edi, 4 ; *16 bytes per row + shr eax, 16 + add edi, eax ; x cycle + lea edi, [edi*3] + add edi, [MOUSE_PICTURE] ; we have our str address + mov esi, edi + add esi, 16*24*3 + call combine_colors + pop edi esi +;-------------------------------------- +align 4 +.end: + mov eax, ecx + ret +;-------------------------------------- +align 4 +.no_mouse_area: + pop eax + ret +;----------------------------------------------------------------------------- +align 4 +__sys_draw_pointer: + pushad + movzx ecx, word [X_UNDER] + movzx edx, word [Y_UNDER] + movzx ebx, word [MOUSE_Y] + movzx eax, word [MOUSE_X] + cmp [redrawmouse_unconditional], 0 + je @f + mov [redrawmouse_unconditional], 0 + jmp redrawmouse +;-------------------------------------- +align 4 +@@: + cmp eax, ecx + jne redrawmouse + cmp ebx, edx + je nodmp +;-------------------------------------- +align 4 +redrawmouse: + pushfd + cli + call draw_mouse_under + call save_draw_mouse + +; mov eax, [_display.select_cursor] +; test eax, eax +; jz @f + cmp [_display.select_cursor], select_cursor + jne @f + + xor eax, eax + mov esi, [current_cursor] + + mov ax, [Y_UNDER] + sub eax, [esi+CURSOR.hot_y] + mov [Y_UNDER_subtraction_CUR_hot_y], ax + add eax, [cur.h] + mov [Y_UNDER_sub_CUR_hot_y_add_curh], ax + + mov ax, [X_UNDER] + sub eax, [esi+CURSOR.hot_x] + mov [X_UNDER_subtraction_CUR_hot_x], ax + add eax, [cur.w] + mov [X_UNDER_sub_CUR_hot_x_add_curh], ax +;-------------------------------------- +align 4 +@@: + popfd +;-------------------------------------- +align 4 +nodmp: + popad + ret +;----------------------------------------------------------------------------- +align 4 +proc set_mouse_data stdcall, BtnState:dword, XMoving:dword, YMoving:dword, VScroll:dword, HScroll:dword + + mov eax, [BtnState] + mov [BTN_DOWN], eax + + mov eax, [XMoving] + call mouse_acceleration + add ax, [MOUSE_X];[XCoordinate] + cmp ax, 0 + jge @@M1 + mov eax, 0 + jmp @@M2 +;-------------------------------------- +align 4 +@@M1: + cmp ax, word [Screen_Max_X];ScreenLength + jl @@M2 + mov ax, word [Screen_Max_X];ScreenLength-1 +;-------------------------------------- +align 4 +@@M2: + mov [MOUSE_X], ax;[XCoordinate] + + mov eax, [YMoving] + neg eax + call mouse_acceleration + + add ax, [MOUSE_Y];[YCoordinate] + cmp ax, 0 + jge @@M3 + mov ax, 0 + jmp @@M4 +;-------------------------------------- +align 4 +@@M3: + cmp ax, word [Screen_Max_Y];ScreenHeigth + jl @@M4 + mov ax, word [Screen_Max_Y];ScreenHeigth-1 +;-------------------------------------- +align 4 +@@M4: + mov [MOUSE_Y], ax;[YCoordinate] + + mov eax, [VScroll] + add [MOUSE_SCROLL_V], ax + + mov eax, [HScroll] + add [MOUSE_SCROLL_H], ax + + mov [mouse_active], 1 + mov eax, [timer_ticks] + mov [mouse_timer_ticks], eax + call wakeup_osloop + ret +endp +;----------------------------------------------------------------------------- +align 4 +mouse_acceleration: + push eax + mov eax, [timer_ticks] + sub eax, [mouse_timer_ticks] + cmp eax, [mouse_delay] + pop eax + ja @f + ;push edx + imul eax, [mouse_speed_factor] + ;pop edx +;-------------------------------------- +align 4 +@@: + ret +;----------------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-process/hid/set_dtc.inc b/kernel/branches/kolibri-process/hid/set_dtc.inc new file mode 100644 index 000000000..1340459ee --- /dev/null +++ b/kernel/branches/kolibri-process/hid/set_dtc.inc @@ -0,0 +1,203 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +;setting date,time,clock and alarm-clock +;add sys_settime at servetable as for ex. 22 fcn: +; 22 - SETTING DATE TIME, CLOCK AND ALARM-CLOCK +; ebx =0 - set time ecx - 00SSMMHH +; ebx =1 - set date ecx=00DDMMYY +; ebx =2 - set day of week ecx- 1-7 +; ebx =3 - set alarm-clock ecx - 00SSMMHH +; out: 0 -Ok 1 -wrong format 2 -battery low +sys_settime: + + cli + mov al, 0x0d + out 0x70, al + in al, 0x71 + bt ax, 7 + jnc bat_low + cmp ebx, 2;day of week + jne nosetweek + test ecx, ecx ;test day of week + je wrongtime + cmp ecx, 7 + ja wrongtime + mov edx, 0x70 + call startstopclk + dec edx + mov al, 6 + out dx, al + inc edx + mov al, cl + out dx, al + jmp endsettime + nosetweek: ;set date + cmp ebx, 1 + jne nosetdate + cmp cl, 0x99;test year + ja wrongtime + shl ecx, 4 + cmp cl, 0x90 + ja wrongtime + cmp ch, 0x99;test month + ja wrongtime + shr ecx, 4 + test ch, ch + je wrongtime + cmp ch, 0x12 + ja wrongtime + shl ecx, 8 + bswap ecx ;ebx=00YYMMDD + test cl, cl ;test day + je wrongtime + shl ecx, 4 + cmp cl, 0x90 + ja wrongtime + shr ecx, 4 + cmp ch, 2 ;February + jne testday + cmp cl, 0x29 + ja wrongtime + jmp setdate + testday: + cmp ch, 8 + jb testday1;Aug-Dec + bt cx, 8 + jnc days31 + jmp days30 + testday1: + bt cx, 8 ;Jan-Jul ex.Feb + jnc days30 + days31: + cmp cl, 0x31 + ja wrongtime + jmp setdate + days30: + cmp cl, 0x30 + ja wrongtime + setdate: + mov edx, 0x70 + call startstopclk + dec edx + mov al, 7 ;set days + out dx, al + inc edx + mov al, cl + out dx, al + dec edx + mov al, 8 ;set months + out dx, al + inc edx + mov al, ch + out dx, al + dec edx + mov al, 9 ;set years + out dx, al + inc edx + shr ecx, 8 + mov al, ch + out dx, al + jmp endsettime + nosetdate: ;set time or alarm-clock + cmp ebx, 3 + ja wrongtime + cmp cl, 0x23 + ja wrongtime + cmp ch, 0x59 + ja wrongtime + shl ecx, 4 + cmp cl, 0x90 + ja wrongtime + cmp ch, 0x92 + ja wrongtime + shl ecx, 4 + bswap ecx ;00HHMMSS + cmp cl, 0x59 + ja wrongtime + shl ecx, 4 + cmp cl, 0x90 + ja wrongtime + shr ecx, 4 + + mov edx, 0x70 + call startstopclk + dec edx + cmp ebx, 3 + + je setalarm + xor eax, eax;al=0-set seconds + out dx, al + inc edx + mov al, cl + out dx, al + dec edx + mov al, 2 ;set minutes + out dx, al + inc edx + mov al, ch + out dx, al + dec edx + mov al, 4 ;set hours + out dx, al + inc edx + shr ecx, 8 + mov al, ch + out dx, al + jmp endsettime + setalarm: + mov al, 1;set seconds for al. + out dx, al + inc edx + mov al, cl + out dx, al + dec edx + mov al, 3;set minutes for al. + out dx, al + inc edx + mov al, ch + out dx, al + dec edx + mov al, 5;set hours for al. + out dx, al + inc edx + shr ecx, 8 + mov al, ch + out dx, al + dec edx + mov al, 0x0b;enable irq's + out dx, al + inc dx + in al, dx + bts ax, 5;set bit 5 + out dx, al + endsettime: + dec edx + call startstopclk + sti + and [esp+36-4], dword 0 + ret + bat_low: + sti + mov [esp+36-4], dword 2 + ret + wrongtime: + sti + mov [esp+36-4], dword 1 + ret + +startstopclk: + mov al, 0x0b + out dx, al + inc dx + in al, dx + btc ax, 7 + out dx, al + ret diff --git a/kernel/branches/kolibri-process/imports.inc b/kernel/branches/kolibri-process/imports.inc new file mode 100644 index 000000000..8211d87eb --- /dev/null +++ b/kernel/branches/kolibri-process/imports.inc @@ -0,0 +1,27 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;============================================================================ +; +; External kernel dependencies +; +;============================================================================ + +$Revision: 2455 $ + + +align 4 +@IMPORT: + +library \ + libini,'libini.obj' + +import libini, \ + ini.lib_init,'lib_init',\ + ini.get_str,'ini.get_str',\ + ini.enum_keys,'ini.enum_keys',\ + ini.get_int,'ini.get_int' diff --git a/kernel/branches/kolibri-process/init.inc b/kernel/branches/kolibri-process/init.inc new file mode 100644 index 000000000..f227c639b --- /dev/null +++ b/kernel/branches/kolibri-process/init.inc @@ -0,0 +1,610 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4390 $ + + +MEM_WB equ 6 ;write-back memory +MEM_WC equ 1 ;write combined memory +MEM_UC equ 0 ;uncached memory + +align 4 +proc mem_test +; if we have BIOS with fn E820, skip the test + cmp dword [BOOT_VARS-OS_BASE + 0x9100], 0 + jnz .ret + + mov eax, cr0 + and eax, not (CR0_CD+CR0_NW) + or eax, CR0_CD ;disable caching + mov cr0, eax + wbinvd ;invalidate cache + + xor edi, edi + mov ebx, 'TEST' +@@: + add edi, 0x100000 + xchg ebx, dword [edi] + cmp dword [edi], 'TEST' + xchg ebx, dword [edi] + je @b + + and eax, not (CR0_CD+CR0_NW) ;enable caching + mov cr0, eax + inc dword [BOOT_VARS-OS_BASE + 0x9100] + xor eax, eax + mov [BOOT_VARS-OS_BASE + 0x9104], eax + mov [BOOT_VARS-OS_BASE + 0x9108], eax + mov [BOOT_VARS-OS_BASE + 0x910C], edi + mov [BOOT_VARS-OS_BASE + 0x9110], eax + inc eax + mov [BOOT_VARS-OS_BASE + 0x9114], eax +.ret: + ret +endp + +align 4 +proc init_mem +; calculate maximum allocatable address and number of allocatable pages + mov edi, BOOT_VARS-OS_BASE + 0x9104 + mov ecx, [edi-4] + xor esi, esi; esi will hold total amount of memory + xor edx, edx; edx will hold maximum allocatable address +.calcmax: +; round all to pages + mov eax, [edi] + cmp [edi+16], byte 1 + jne .unusable + + test eax, 0xFFF + jz @f + neg eax + and eax, 0xFFF + add [edi], eax + adc dword [edi+4], 0 + sub [edi+8], eax + sbb dword [edi+12], 0 + jc .unusable +@@: + and dword [edi+8], not 0xFFF + jz .unusable +; ignore memory after 4 Gb + cmp dword [edi+4], 0 + jnz .unusable + mov eax, [edi] + cmp dword [edi+12], 0 + jnz .overflow + add eax, [edi+8] + jnc @f +.overflow: + mov eax, 0xFFFFF000 +@@: + cmp edx, eax + jae @f + mov edx, eax +@@: + sub eax, [edi] + mov [edi+8], eax + add esi, eax + jmp .usable +.unusable: +; and dword [edi+8], 0 +.usable: + add edi, 20 + loop .calcmax +.calculated: + mov [MEM_AMOUNT-OS_BASE], esi + mov [pg_data.mem_amount-OS_BASE], esi + shr esi, 12 + mov [pg_data.pages_count-OS_BASE], esi + + shr edx, 12 + add edx, 31 + and edx, not 31 + shr edx, 3 + mov [pg_data.pagemap_size-OS_BASE], edx + + add edx, (sys_pgmap-OS_BASE)+4095 + and edx, not 4095 + mov [tmp_page_tabs], edx + + mov edx, esi + and edx, -1024 + cmp edx, (OS_BASE/4096) + jbe @F + mov edx, (OS_BASE/4096) + jmp .set +@@: + cmp edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 + jae .set + mov edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 +.set: + mov [pg_data.kernel_pages-OS_BASE], edx + shr edx, 10 + mov [pg_data.kernel_tables-OS_BASE], edx + + xor eax, eax + mov edi, sys_proc-OS_BASE + mov ecx, 8192/4 + cld + rep stosd + + mov edx, (sys_proc-OS_BASE+PROC.pdt_0)+ 0x800; (OS_BASE shr 20) + bt [cpu_caps-OS_BASE], CAPS_PSE + jnc .no_PSE + + mov ebx, cr4 + or ebx, CR4_PSE + mov eax, PG_LARGE+PG_SW + mov cr4, ebx + dec [pg_data.kernel_tables-OS_BASE] + + mov [edx], eax + add edx, 4 + + mov edi, [tmp_page_tabs] + jmp .map_kernel_heap ; new kernel fits to the first 4Mb - nothing to do with ".map_low" +.no_PSE: + mov eax, PG_SW + mov ecx, [tmp_page_tabs] + shr ecx, 12 +.map_low: + mov edi, [tmp_page_tabs] +@@: ; + stosd + add eax, 0x1000 + dec ecx + jnz @B + +.map_kernel_heap: + mov ecx, [pg_data.kernel_tables-OS_BASE] + shl ecx, 10 + xor eax, eax + rep stosd + + mov ecx, [pg_data.kernel_tables-OS_BASE] + mov eax, [tmp_page_tabs] + or eax, PG_SW + mov edi, edx + +.map_kernel_tabs: + stosd + add eax, 0x1000 + dec ecx + jnz .map_kernel_tabs + + mov dword [sys_proc-OS_BASE+PROC.pdt_0+(page_tabs shr 20)], sys_proc+PROC.pdt_0+PG_SW-OS_BASE + + mov edi, (sys_proc+PROC.pdt_0-OS_BASE) + lea esi, [edi+(OS_BASE shr 20)] + movsd + movsd + ret +endp + +align 4 +proc init_page_map +; mark all memory as unavailable + mov edi, sys_pgmap-OS_BASE + mov ecx, [pg_data.pagemap_size-OS_BASE] + shr ecx, 2 + xor eax, eax + cld + rep stosd + +; scan through memory map and mark free areas as available + mov ebx, BOOT_VARS-OS_BASE + 0x9104 + mov edx, [ebx-4] +.scanmap: + cmp [ebx+16], byte 1 + jne .next + + mov ecx, [ebx+8] + shr ecx, 12; ecx = number of pages + jz .next + mov edi, [ebx] + shr edi, 12; edi = first page + mov eax, edi + shr edi, 5 + shl edi, 2 + add edi, sys_pgmap-OS_BASE + and eax, 31 + jz .startok + add ecx, eax + sub ecx, 32 + jbe .onedword + push ecx + mov ecx, eax + or eax, -1 + shl eax, cl + or [edi], eax + add edi, 4 + pop ecx +.startok: + push ecx + shr ecx, 5 + or eax, -1 + rep stosd + pop ecx + and ecx, 31 + neg eax + shl eax, cl + dec eax + or [edi], eax + jmp .next +.onedword: + add ecx, 32 + sub ecx, eax +@@: + bts [edi], eax + inc eax + loop @b +.next: + add ebx, 20 + dec edx + jnz .scanmap + +; mark kernel memory as allocated (unavailable) + mov ecx, [tmp_page_tabs] + mov edx, [pg_data.pages_count-OS_BASE] + shr ecx, 12 + add ecx, [pg_data.kernel_tables-OS_BASE] + sub edx, ecx + mov [pg_data.pages_free-OS_BASE], edx + + mov edi, sys_pgmap-OS_BASE + mov ebx, ecx + shr ecx, 5 + xor eax, eax + rep stosd + + not eax + mov ecx, ebx + and ecx, 31 + shl eax, cl + and [edi], eax + add edi, OS_BASE + mov [page_start-OS_BASE], edi; + + mov ebx, sys_pgmap + add ebx, [pg_data.pagemap_size-OS_BASE] + mov [page_end-OS_BASE], ebx + + ret +endp + +align 4 + +init_BIOS32: + mov edi, 0xE0000 +.pcibios_nxt: + cmp dword[edi], '_32_'; "magic" word + je .BIOS32_found +.pcibios_nxt2: + add edi, 0x10 + cmp edi, 0xFFFF0 + je .BIOS32_not_found + jmp .pcibios_nxt +.BIOS32_found: ; magic word found, check control summ + + movzx ecx, byte[edi + 9] + shl ecx, 4 + mov esi, edi + xor eax, eax + cld ; paranoia +@@: + lodsb + add ah, al + loop @b + jnz .pcibios_nxt2; control summ must be zero + ; BIOS32 service found ! + mov ebp, [edi + 4] + mov [bios32_entry], ebp + ; check PCI BIOS present + mov eax, '$PCI' + xor ebx, ebx + push cs ; special for 'ret far' from BIOS + call ebp + test al, al + jnz .PCI_BIOS32_not_found + + ; здесь создаются дискрипторы для PCI BIOS + + add ebx, OS_BASE + dec ecx + mov [(pci_code_32-OS_BASE)], cx ;limit 0-15 + mov [(pci_data_32-OS_BASE)], cx ;limit 0-15 + + mov [(pci_code_32-OS_BASE)+2], bx ;base 0-15 + mov [(pci_data_32-OS_BASE)+2], bx ;base 0-15 + + shr ebx, 16 + mov [(pci_code_32-OS_BASE)+4], bl ;base 16-23 + mov [(pci_data_32-OS_BASE)+4], bl ;base 16-23 + + shr ecx, 16 + and cl, 0x0F + mov ch, bh + add cx, D32 + mov [(pci_code_32-OS_BASE)+6], cx ;lim 16-19 & + mov [(pci_data_32-OS_BASE)+6], cx ;base 24-31 + + mov [(pci_bios_entry-OS_BASE)], edx + ; jmp .end +.PCI_BIOS32_not_found: + ; здесь должна заполнятся pci_emu_dat +.BIOS32_not_found: +.end: + ret + +align 4 +proc test_cpu + locals + cpu_type dd ? + cpu_id dd ? + cpu_Intel dd ? + cpu_AMD dd ? + endl + + xor eax, eax + mov [cpu_type], eax + mov [cpu_caps-OS_BASE], eax + mov [cpu_caps+4-OS_BASE], eax + + pushfd + pop eax + mov ecx, eax + xor eax, 0x40000 + push eax + popfd + pushfd + pop eax + xor eax, ecx + mov [cpu_type], CPU_386 + jz .end_cpuid + push ecx + popfd + + mov [cpu_type], CPU_486 + mov eax, ecx + xor eax, 0x200000 + push eax + popfd + pushfd + pop eax + xor eax, ecx + je .end_cpuid + mov [cpu_id], 1 + + xor eax, eax + cpuid + + mov [cpu_vendor-OS_BASE], ebx + mov [cpu_vendor+4-OS_BASE], edx + mov [cpu_vendor+8-OS_BASE], ecx + cmp ebx, dword [intel_str-OS_BASE] + jne .check_AMD + cmp edx, dword [intel_str+4-OS_BASE] + jne .check_AMD + cmp ecx, dword [intel_str+8-OS_BASE] + jne .check_AMD + mov [cpu_Intel], 1 + cmp eax, 1 + jl .end_cpuid + mov eax, 1 + cpuid + mov [cpu_sign-OS_BASE], eax + mov [cpu_info-OS_BASE], ebx + mov [cpu_caps-OS_BASE], edx + mov [cpu_caps+4-OS_BASE], ecx + + shr eax, 8 + and eax, 0x0f + ret +.end_cpuid: + mov eax, [cpu_type] + ret + +.check_AMD: + cmp ebx, dword [AMD_str-OS_BASE] + jne .unknown + cmp edx, dword [AMD_str+4-OS_BASE] + jne .unknown + cmp ecx, dword [AMD_str+8-OS_BASE] + jne .unknown + mov [cpu_AMD], 1 + cmp eax, 1 + jl .unknown + mov eax, 1 + cpuid + mov [cpu_sign-OS_BASE], eax + mov [cpu_info-OS_BASE], ebx + mov [cpu_caps-OS_BASE], edx + mov [cpu_caps+4-OS_BASE], ecx + shr eax, 8 + and eax, 0x0f + ret +.unknown: + mov eax, 1 + cpuid + mov [cpu_sign-OS_BASE], eax + mov [cpu_info-OS_BASE], ebx + mov [cpu_caps-OS_BASE], edx + mov [cpu_caps+4-OS_BASE], ecx + shr eax, 8 + and eax, 0x0f + ret +endp + +iglobal +align 4 +acpi_lapic_base dd 0xfee00000 ; default local apic base +endg + +uglobal +align 4 +acpi_rsdp rd 1 +acpi_rsdt rd 1 +acpi_madt rd 1 + +acpi_dev_data rd 1 +acpi_dev_size rd 1 + +acpi_rsdt_base rd 1 +acpi_fadt_base rd 1 +acpi_dsdt_base rd 1 +acpi_dsdt_size rd 1 +acpi_madt_base rd 1 +acpi_ioapic_base rd 1 + +cpu_count rd 1 +smpt rd 16 +endg + +ACPI_HI_RSDP_WINDOW_START equ 0x000E0000 +ACPI_HI_RSDP_WINDOW_END equ 0x00100000 +ACPI_RSDP_CHECKSUM_LENGTH equ 20 +ACPI_MADT_SIGN equ 0x43495041 +ACPI_FADT_SIGN equ 0x50434146 + + +acpi_locate: + push ebx + mov ebx, ACPI_HI_RSDP_WINDOW_START +.check: + cmp [ebx], dword 0x20445352 + jne .next + cmp [ebx+4], dword 0x20525450 + jne .next + + mov edx, ebx + mov ecx, ACPI_RSDP_CHECKSUM_LENGTH + xor eax, eax +.sum: + add al, [edx] + inc edx + loop .sum + + test al, al + jnz .next + + mov eax, ebx + pop ebx + ret +.next: + add ebx, 16 + cmp ebx, ACPI_HI_RSDP_WINDOW_END + jb .check + + pop ebx + xor eax, eax + ret + +align 4 +rsdt_find: ;ecx= rsdt edx= SIG + push ebx + push esi + + lea ebx, [ecx+36] + mov esi, [ecx+4] + add esi, ecx +align 4 +.next: + mov eax, [ebx] + cmp [eax], edx + je .done + + add ebx, 4 + cmp ebx, esi + jb .next + + xor eax, eax + pop esi + pop ebx + ret + +.done: + mov eax, [ebx] + pop esi + pop ebx + ret + +align 4 +check_acpi: + + call acpi_locate + test eax, eax + jz .done + + mov ecx, [eax+16] + mov edx, 0x50434146 + mov [acpi_rsdt_base-OS_BASE], ecx + call rsdt_find + mov [acpi_fadt_base-OS_BASE], eax + test eax, eax + jz @f + + mov eax, [eax+40] + mov [acpi_dsdt_base-OS_BASE], eax + mov eax, [eax+4] + mov [acpi_dsdt_size-OS_BASE], eax + +@@: + mov edx, ACPI_MADT_SIGN + mov ecx, [acpi_rsdt_base-OS_BASE] + call rsdt_find + test eax, eax + jz .done + + mov [acpi_madt_base-OS_BASE], eax + mov ecx, [eax+36] + mov [acpi_lapic_base-OS_BASE], ecx + + mov edi, smpt-OS_BASE + mov ebx, [ecx+0x20] + shr ebx, 24 ; read APIC ID + + mov [edi], ebx ; bootstrap always first + inc [cpu_count-OS_BASE] + add edi, 4 + + lea edx, [eax+44] + mov ecx, [eax+4] + add ecx, eax +.check: + mov eax, [edx] + cmp al, 0 + jne .io_apic + + shr eax, 24 ; get APIC ID + cmp eax, ebx ; skip self + je .next + + test [edx+4], byte 1 ; is enabled ? + jz .next + + cmp [cpu_count-OS_BASE], 16 + jae .next + + stosd ; store APIC ID + inc [cpu_count-OS_BASE] +.next: + mov eax, [edx] + movzx eax, ah + add edx, eax + cmp edx, ecx + jb .check +.done: + ret + +.io_apic: + cmp al, 1 + jne .next + + mov eax, [edx+4] + mov [acpi_ioapic_base-OS_BASE], eax + jmp .next diff --git a/kernel/branches/kolibri-process/kernel.asm b/kernel/branches/kolibri-process/kernel.asm new file mode 100644 index 000000000..21ecd6d67 --- /dev/null +++ b/kernel/branches/kolibri-process/kernel.asm @@ -0,0 +1,6013 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. +;; PROGRAMMING: +;; Ivan Poddubny +;; Marat Zakiyanov (Mario79) +;; VaStaNi +;; Trans +;; Mihail Semenyako (mike.dld) +;; Sergey Kuzmin (Wildwest) +;; Andrey Halyavin (halyavin) +;; Mihail Lisovin (Mihasik) +;; Andrey Ignatiev (andrew_programmer) +;; NoName +;; Evgeny Grechnikov (Diamond) +;; Iliya Mihailov (Ghost) +;; Sergey Semyonov (Serge) +;; Johnny_B +;; SPraid (simba) +;; Hidnplayr +;; Alexey Teplov () +;; Rus +;; Nable +;; shurf +;; Alver +;; Maxis +;; Galkov +;; CleverMouse +;; tsdima +;; turbanoff +;; Asper +;; art_zh +;; +;; Data in this file was originally part of MenuetOS project which is +;; distributed under the terms of GNU GPL. It is modified and redistributed as +;; part of KolibriOS project under the terms of GNU GPL. +;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa +;; PROGRAMMING: +;; +;; Ville Mikael Turjanmaa, villemt@itu.jyu.fi +;; - main os coding/design +;; Jan-Michael Brummer, BUZZ2@gmx.de +;; Felix Kaiser, info@felix-kaiser.de +;; Paolo Minazzi, paolo.minazzi@inwind.it +;; quickcode@mail.ru +;; Alexey, kgaz@crosswinds.net +;; Juan M. Caravaca, bitrider@wanadoo.es +;; kristol@nic.fi +;; Mike Hibbett, mikeh@oceanfree.net +;; Lasse Kuusijarvi, kuusijar@lut.fi +;; Jarek Pelczar, jarekp3@wp.pl +;; +;; KolibriOS is distributed in the hope that it will be useful, but WITHOUT ANY +;; WARRANTY. No author or distributor accepts responsibility to anyone for the +;; consequences of using it or for whether it serves any particular purpose or +;; works at all, unless he says so in writing. Refer to the GNU General Public +;; License (the "GPL") for full details. +; +;; Everyone is granted permission to copy, modify and redistribute KolibriOS, +;; but only under the conditions described in the GPL. A copy of this license +;; is supposed to have been given to you along with KolibriOS so you can know +;; your rights and responsibilities. It should be in a file named COPYING. +;; Among other things, the copyright notice and this notice must be preserved +;; on all copies. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +format binary as "mnt" + +include 'macros.inc' +include 'struct.inc' + +$Revision: 4381 $ + + +USE_COM_IRQ equ 1 ; make irq 3 and irq 4 available for PCI devices +VESA_1_2_VIDEO equ 0 ; enable vesa 1.2 bank switch functions + +; 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 + +include "proc32.inc" +include "kglobals.inc" +include "lang.inc" +include "encoding.inc" + +include "const.inc" + +iglobal +; The following variable, if equal to 1, duplicates debug output to the screen. +debug_direct_print db 0 +; Start the first app (LAUNCHER) after kernel is loaded? (1=yes, 2 or 0=no) +launcher_start db 1 +endg + +max_processes equ 255 +tss_step equ (128+8192) ; tss & i/o - 65535 ports, * 256=557056*4 + +os_stack equ (os_data_l-gdts) ; GDTs +os_code equ (os_code_l-gdts) +graph_data equ (3+graph_data_l-gdts) +tss0 equ (tss0_l-gdts) +app_code equ (3+app_code_l-gdts) +app_data equ (3+app_data_l-gdts) +app_tls equ (3+tls_data_l-gdts) +pci_code_sel equ (pci_code_32-gdts) +pci_data_sel equ (pci_data_32-gdts) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Included files: +;; +;; Kernel16.inc +;; - Booteng.inc English text for bootup +;; - Bootcode.inc Hardware setup +;; - Pci16.inc PCI functions +;; +;; Kernel32.inc +;; - Sys32.inc Process management +;; - Shutdown.inc Shutdown and restart +;; - Fat32.inc Read / write hd +;; - Vesa12.inc Vesa 1.2 driver +;; - Vesa20.inc Vesa 2.0 driver +;; - Vga.inc VGA driver +;; - Stack.inc Network interface +;; - Mouse.inc Mouse pointer +;; - Scincode.inc Window skinning +;; - Pci32.inc PCI functions +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 16 BIT ENTRY FROM BOOTSECTOR ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +use16 + org 0x0 + jmp start_of_code + +if lang eq sp +include "kernelsp.inc" ; spanish kernel messages +else if lang eq et +version db 'Kolibri OS versioon 0.7.7.0+ ',13,10,13,10,0 +else +version db 'Kolibri OS version 0.7.7.0+ ',13,10,13,10,0 +end if + +include "boot/bootstr.inc" ; language-independent boot messages +include "boot/preboot.inc" + +if lang eq ge +include "boot/bootge.inc" ; german system boot messages +else if lang eq sp +include "boot/bootsp.inc" ; spanish system boot messages +else if lang eq ru +include "boot/bootru.inc" ; russian system boot messages +include "boot/ru.inc" ; Russian font +else if lang eq et +include "boot/bootet.inc" ; estonian system boot messages +include "boot/et.inc" ; Estonian font +else +include "boot/booten.inc" ; english system boot messages +end if + +include "boot/bootcode.inc" ; 16 bit system boot code +include "bus/pci/pci16.inc" +include "detect/biosdisk.inc" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SWITCH TO 32 BIT PROTECTED MODE ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +; CR0 Flags - Protected mode and Paging + + mov ecx, CR0_PE + +; Enabling 32 bit protected mode + + sidt [cs:old_ints_h] + + cli ; disable all irqs + cld + mov al, 255 ; mask all irqs + out 0xa1, al + out 0x21, al + l.5: + in al, 0x64 ; Enable A20 + test al, 2 + jnz l.5 + mov al, 0xD1 + out 0x64, al + l.6: + in al, 0x64 + test al, 2 + jnz l.6 + mov al, 0xDF + out 0x60, al + l.7: + in al, 0x64 + test al, 2 + jnz l.7 + mov al, 0xFF + out 0x64, al + + lgdt [cs:tmp_gdt] ; Load GDT + mov eax, cr0 ; protected mode + or eax, ecx + and eax, 10011111b *65536*256 + 0xffffff ; caching enabled + mov cr0, eax + jmp pword os_code:B32 ; jmp to enable 32 bit mode + +align 8 +tmp_gdt: + + dw 23 + dd tmp_gdt+0x10000 + dw 0 + + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10011010b + db 0x00 + + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10010010b + db 0x00 + +include "data16.inc" + +if ~ lang eq sp +diff16 "end of bootcode",0,$+0x10000 +end if + +use32 +org $+0x10000 + +align 4 +B32: + mov ax, os_stack ; Selector for os + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, 0x006CC00 ; Set stack + +; CLEAR 0x280000 - HEAP_BASE + + xor eax, eax + mov edi, CLEAN_ZONE + mov ecx, (HEAP_BASE-OS_BASE-CLEAN_ZONE) / 4 + cld + rep stosd + +; CLEAR KERNEL UNDEFINED GLOBALS + mov edi, endofcode-OS_BASE + mov ecx, 0x90000 + sub ecx, edi + shr ecx, 2 + rep stosd + +; SAVE & CLEAR 0-0xffff + + mov edi, 0x1000 + mov ecx, 0x8000 / 4 + rep stosd + mov edi, 0xa000 + mov ecx, 0x6000 / 4 + rep stosd + + call test_cpu + bts [cpu_caps-OS_BASE], CAPS_TSC ;force use rdtsc + + call check_acpi + call init_BIOS32 +; MEMORY MODEL + call mem_test + call init_mem + call init_page_map + +; ENABLE PAGING + + mov eax, sys_proc-OS_BASE+PROC.pdt_0 + mov cr3, eax + + mov eax, cr0 + or eax, CR0_PG+CR0_WP + mov cr0, eax + + lgdt [gdts] + jmp pword os_code:high_code + +align 4 +bios32_entry dd ? +tmp_page_tabs dd ? + +use16 +org $-0x10000 +include "boot/shutdown.inc" ; shutdown or restart +org $+0x10000 +use32 + +__DEBUG__ fix 1 +__DEBUG_LEVEL__ fix 1 +include 'init.inc' + +org OS_BASE+$ + +include 'fdo.inc' + +align 4 +high_code: + mov ax, os_stack + mov bx, app_data + mov cx, app_tls + mov ss, ax + add esp, OS_BASE + + mov ds, bx + mov es, bx + mov fs, cx + mov gs, bx + + bt [cpu_caps], CAPS_PGE + jnc @F + + or dword [sys_proc+PROC.pdt_0+(OS_BASE shr 20)], PG_GLOBAL + + mov ebx, cr4 + or ebx, CR4_PGE + mov cr4, ebx +@@: + xor eax, eax + mov dword [sys_proc+PROC.pdt_0], eax + mov dword [sys_proc+PROC.pdt_0+4], eax + + mov eax, cr3 + mov cr3, eax ; flush TLB + + mov ecx, pg_data.mutex + call mutex_init + + mov ecx, disk_list_mutex + call mutex_init + + mov ecx, keyboard_list_mutex + call mutex_init + + mov ecx, unpack_mutex + call mutex_init + + mov ecx, application_table_mutex + call mutex_init + + mov ecx, ide_mutex + call mutex_init + mov ecx, ide_channel1_mutex + call mutex_init + mov ecx, ide_channel2_mutex + call mutex_init +;----------------------------------------------------------------------------- +; SAVE REAL MODE VARIABLES +;----------------------------------------------------------------------------- +save_variables_IDE_controller: + xor eax, eax + mov ax, [BOOT_VARS + BOOT_IDE_INTERR_16] + mov [IDE_Interrupt], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_PI_16] + mov [IDEContrProgrammingInterface], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_BASE_ADDR] + mov [IDEContrRegsBaseAddr], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_BAR0_16] + cmp ax, 0 + je @f + cmp ax, 1 + jne .no_PATA_BAR0 +@@: + mov ax, 0x1F0 + jmp @f +.no_PATA_BAR0: + and ax, 0xFFFC +@@: + mov [StandardATABases], ax + mov [hd_address_table], eax + mov [hd_address_table+8], eax + mov [IDE_BAR0_val], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_BAR1_16] + cmp ax, 0 + je @f + cmp ax, 1 + jne .no_PATA_BAR1 +@@: + mov ax, 0x3F4 + jmp @f +.no_PATA_BAR1: + and ax, 0xFFFC +@@: + mov [IDE_BAR1_val], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_BAR2_16] + cmp ax, 0 + je @f + cmp ax, 1 + jne .no_PATA_BAR2 +@@: + mov ax, 0x170 + jmp @f +.no_PATA_BAR2: + and ax, 0xFFFC +@@: + mov [StandardATABases+2], ax + mov [hd_address_table+16], eax + mov [hd_address_table+24], eax + mov [IDE_BAR2_val], ax +;-------------------------------------- + mov ax, [BOOT_VARS + BOOT_IDE_BAR3_16] + cmp ax, 0 + je @f + cmp ax, 1 + jne .no_PATA_BAR3 +@@: + mov ax, 0x374 + jmp @f +.no_PATA_BAR3: + and ax, 0xFFFC +@@: + mov [IDE_BAR3_val], ax + +; --------------- APM --------------------- + +; init selectors + mov ebx, [BOOT_VARS+BOOT_APM_ENTRY] ; offset of APM entry point + movzx eax, word [BOOT_VARS+BOOT_APM_CODE_32] ; real-mode segment base address of + ; protected-mode 32-bit code segment + movzx ecx, word [BOOT_VARS+BOOT_APM_CODE_16]; real-mode segment base address of + ; protected-mode 16-bit code segment + movzx edx, word [BOOT_VARS+BOOT_APM_DATA_16]; real-mode segment base address of + ; protected-mode 16-bit data segment + + shl eax, 4 + mov [dword apm_code_32 + 2], ax + shr eax, 16 + mov [dword apm_code_32 + 4], al + + shl ecx, 4 + mov [dword apm_code_16 + 2], cx + shr ecx, 16 + mov [dword apm_code_16 + 4], cl + + shl edx, 4 + mov [dword apm_data_16 + 2], dx + shr edx, 16 + mov [dword apm_data_16 + 4], dl + + mov dword[apm_entry], ebx + mov word [apm_entry + 4], apm_code_32 - gdts + + mov eax, [BOOT_VARS + BOOT_APM_VERSION] ; version & flags + mov [apm_vf], eax +; ----------------------------------------- + mov al, [BOOT_VARS+BOOT_DMA] ; DMA access + mov [allow_dma_access], al + movzx eax, byte [BOOT_VARS+BOOT_BPP] ; bpp + mov [_display.bpp], eax + mov [_display.vrefresh], 60 + mov al, [BOOT_VARS+BOOT_DEBUG_PRINT] ; If nonzero, duplicates debug output to the screen + mov [debug_direct_print], al + mov al, [BOOT_VARS+BOOT_LAUNCHER_START] ; Start the first app (LAUNCHER) after kernel is loaded? + mov [launcher_start], al + movzx eax, word [BOOT_VARS+BOOT_X_RES]; X max + mov [_display.width], eax + mov [display_width_standard], eax + dec eax + mov [Screen_Max_X], eax + mov [screen_workarea.right], eax + movzx eax, word [BOOT_VARS+BOOT_Y_RES]; Y max + mov [_display.height], eax + mov [display_height_standard], eax + dec eax + mov [Screen_Max_Y], eax + mov [screen_workarea.bottom], eax + movzx eax, word [BOOT_VARS+BOOT_VESA_MODE] ; screen mode + mov dword [SCR_MODE], eax +; mov eax, [BOOT_VAR+0x9014] ; Vesa 1.2 bnk sw add +; mov [BANK_SWITCH], eax + mov eax, 640 *4 ; Bytes PerScanLine + cmp [SCR_MODE], word 0x13 ; 320x200 + je @f + cmp [SCR_MODE], word 0x12 ; VGA 640x480 + je @f + movzx eax, word[BOOT_VARS+BOOT_PITCH] ; for other modes +@@: + mov [_display.pitch], eax + mov eax, [_display.width] + mul [_display.height] + mov [_WinMapSize], eax + + call calculate_fast_getting_offset_for_WinMapAddress +; for Qemu or non standart video cards +; Unfortunately [BytesPerScanLine] does not always +; equal to [_display.width] * [ScreenBPP] / 8 + call calculate_fast_getting_offset_for_LFB + + mov esi, BOOT_VARS+0x9080 + movzx ecx, byte [esi-1] + mov [NumBiosDisks], ecx + mov edi, BiosDisksData + rep movsd + +; GRAPHICS ADDRESSES + + mov eax, [BOOT_VARS+BOOT_LFB] + mov [LFBAddress], eax + + cmp [SCR_MODE], word 0100000000000000b + jge setvesa20 + cmp [SCR_MODE], word 0x13 ; EGA 320*200 256 colors + je v20ga32 + jmp v20ga24 + +setvesa20: + mov [PUTPIXEL], dword Vesa20_putpixel24 ; Vesa 2.0 + mov [GETPIXEL], dword Vesa20_getpixel24 + cmp byte [_display.bpp], 24 + jz v20ga24 +v20ga32: + mov [PUTPIXEL], dword Vesa20_putpixel32 + mov [GETPIXEL], dword Vesa20_getpixel32 + jmp no_mode_0x12 +v20ga24: + cmp [SCR_MODE], word 0x12 ; 16 C VGA 640x480 + jne no_mode_0x12 + mov [PUTPIXEL], dword VGA_putpixel + mov [GETPIXEL], dword Vesa20_getpixel32 +no_mode_0x12: + + mov [MOUSE_PICTURE], dword mousepointer + mov [_display.check_mouse], check_mouse_area_for_putpixel + mov [_display.check_m_pixel], check_mouse_area_for_getpixel + +; -------- Fast System Call init ---------- +; Intel SYSENTER/SYSEXIT (AMD CPU support it too) + bt [cpu_caps], CAPS_SEP + jnc .SEnP ; SysEnter not Present + xor edx, edx + mov ecx, MSR_SYSENTER_CS + mov eax, os_code + wrmsr + mov ecx, MSR_SYSENTER_ESP +; mov eax, sysenter_stack ; Check it + xor eax, eax + wrmsr + mov ecx, MSR_SYSENTER_EIP + mov eax, sysenter_entry + wrmsr +.SEnP: +; AMD SYSCALL/SYSRET + cmp byte[cpu_vendor], 'A' + jne .noSYSCALL + mov eax, 0x80000001 + cpuid + test edx, 0x800 ; bit_11 - SYSCALL/SYSRET support + jz .noSYSCALL + mov ecx, MSR_AMD_EFER + rdmsr + or eax, 1 ; bit_0 - System Call Extension (SCE) + wrmsr + + ; !!!! It`s dirty hack, fix it !!! + ; Bits of EDX : + ; Bit 31–16 During the SYSRET instruction, this field is copied into the CS register + ; and the contents of this field, plus 8, are copied into the SS register. + ; Bit 15–0 During the SYSCALL instruction, this field is copied into the CS register + ; and the contents of this field, plus 8, are copied into the SS register. + + ; mov edx, (os_code + 16) * 65536 + os_code + mov edx, 0x1B0008 + + mov eax, syscall_entry + mov ecx, MSR_AMD_STAR + wrmsr +.noSYSCALL: +; ----------------------------------------- + stdcall alloc_page + stdcall map_page, tss-0xF80, eax, PG_SW + stdcall alloc_page + stdcall map_page, tss+0x80, eax, PG_SW + stdcall alloc_page + stdcall map_page, tss+0x1080, eax, PG_SW + +; LOAD IDT + + call build_interrupt_table ;lidt is executed + ;lidt [idtreg] + + call init_kernel_heap + stdcall kernel_alloc, (RING0_STACK_SIZE+512) * 2 + mov [os_stack_seg], eax + + lea esp, [eax+RING0_STACK_SIZE] + + mov [tss._ss0], os_stack + mov [tss._esp0], esp + mov [tss._esp], esp + mov [tss._cs], os_code + mov [tss._ss], os_stack + mov [tss._ds], app_data + mov [tss._es], app_data + mov [tss._fs], app_data + mov [tss._gs], app_data + mov [tss._io], 128 +;Add IO access table - bit array of permitted ports + mov edi, tss._io_map_0 + xor eax, eax + not eax + mov ecx, 8192/4 + rep stosd ; access to 4096*8=65536 ports + + mov ax, tss0 + ltr ax + + mov [LFBSize], 0xC00000 + call init_LFB + call init_fpu + call init_malloc + + stdcall alloc_kernel_space, 0x50000 ; FIXME check size + mov [default_io_map], eax + + add eax, 0x2000 + mov [ipc_tmp], eax + mov ebx, 0x1000 + + add eax, 0x40000 + mov [proc_mem_map], eax + + add eax, 0x8000 + mov [proc_mem_pdir], eax + + add eax, ebx + mov [proc_mem_tab], eax + + add eax, ebx + mov [tmp_task_ptab], eax + + add eax, ebx + mov [ipc_pdir], eax + + add eax, ebx + mov [ipc_ptab], eax + + stdcall kernel_alloc, (unpack.LZMA_BASE_SIZE+(unpack.LZMA_LIT_SIZE shl \ + (unpack.lc+unpack.lp)))*4 + + mov [unpack.p], eax + + call init_events + mov eax, srv.fd-SRV.fd + mov [srv.fd], eax + mov [srv.bk], eax + +;Set base of graphic segment to linear address of LFB + mov eax, [LFBAddress] ; set for gs + mov [graph_data_l+2], ax + shr eax, 16 + mov [graph_data_l+4], al + mov [graph_data_l+7], ah + + stdcall kernel_alloc, [_WinMapSize] + mov [_WinMapAddress], eax + + xor eax, eax + inc eax + +; set background + + mov [BgrDrawMode], eax + mov [BgrDataWidth], eax + mov [BgrDataHeight], eax + mov [mem_BACKGROUND], 4 + mov [img_background], static_background_data + +; set clipboard + + xor eax, eax + mov [clipboard_slots], eax + mov [clipboard_write_lock], eax + stdcall kernel_alloc, 4096 + test eax, eax + jnz @f + + dec eax +@@: + mov [clipboard_main_list], eax + +; SET UP OS TASK + + mov esi, boot_setostask + call boot_log + + mov edi, sys_proc + list_init edi + lea ecx, [edi+PROC.thr_list] + list_init ecx + mov [edi+PROC.pdt_0_phys], sys_proc-OS_BASE+PROC.pdt_0 + + mov eax, -1 + mov edi, thr_slot_map+4 + mov [edi-4], dword 0xFFFFFFF8 + stosd + stosd + stosd + stosd + stosd + stosd + stosd + + mov edx, SLOT_BASE+256*1 + 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 + + 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 [CURRENT_TASK], 2 + mov dword [TASK_COUNT], 2 + mov dword [current_slot], SLOT_BASE + 256*2 + mov dword [TASK_BASE], CURRENT_TASK + 32*2 + + +; REDIRECT ALL IRQ'S TO INT'S 0x20-0x2f + mov esi, boot_initirq + call boot_log + call init_irqs + + mov esi, boot_picinit + call boot_log + call PIC_init + + mov esi, boot_v86machine + call boot_log +; Initialize system V86 machine + call init_sys_v86 + + mov esi, boot_inittimer + call boot_log +; Initialize system timer (IRQ0) + call PIT_init + +; Register ramdisk file system + mov esi, boot_initramdisk + call boot_log + call ramdisk_init + + mov esi, boot_initapic + call boot_log +; Try to Initialize APIC + call APIC_init + + mov esi, boot_enableirq + call boot_log +; Enable timer IRQ (IRQ0) and co-processor IRQ (IRQ13) +; they are used: when partitions are scanned, hd_read relies on timer + call unmask_timer + stdcall enable_irq, 2 ; @#$%! PIC + stdcall enable_irq, 13 ; co-processor + + cmp [IDEContrProgrammingInterface], 0 + je @f + + mov esi, boot_disabling_ide + call boot_log +;-------------------------------------- +; Disable IDE interrupts, because the search +; for IDE partitions is in the PIO mode. +;-------------------------------------- +.disable_IDE_interrupt: +; Disable interrupts in IDE controller for PIO + mov al, 2 + mov dx, [IDE_BAR1_val] ;0x3F4 + add dx, 2 ;0x3F6 + out dx, al + mov dx, [IDE_BAR3_val] ;0x374 + add dx, 2 ;0x376 + out dx, al +@@: +;----------------------------------------------------------------------------- +;!!!!!!!!!!!!!!!!!!!!!!!!!! +; mov esi, boot_detectdisks +; call boot_log +;include 'detect/disks.inc' + mov esi, boot_detectfloppy + call boot_log +include 'detect/dev_fd.inc' + mov esi, boot_detecthdcd + call boot_log +include 'detect/dev_hdcd.inc' + mov esi, boot_getcache + call boot_log +include 'detect/getcache.inc' + mov esi, boot_detectpart + call boot_log +include 'detect/sear_par.inc' +;!!!!!!!!!!!!!!!!!!!!!!!!!! + + mov esi, boot_init_sys + call boot_log + call Parser_params + +if ~ defined extended_primary_loader +; ramdisk image should be loaded by extended primary loader if it exists +; READ RAMDISK IMAGE FROM HD + +;!!!!!!!!!!!!!!!!!!!!!!! +include 'boot/rdload.inc' +;!!!!!!!!!!!!!!!!!!!!!!! +end if +; mov [dma_hdd],1 + +if 0 + mov ax, [OS_BASE+0x10000+bx_from_load] + cmp ax, 'r1'; if using not ram disk, then load librares and parameters {SPraid.simba} + je no_lib_load + + mov esi, boot_loadlibs + call boot_log +; LOADING LIBRARES + stdcall dll.Load, @IMPORT ; loading librares for kernel (.obj files) + call load_file_parse_table ; prepare file parse table + call set_kernel_conf ; configure devices and gui +no_lib_load: +end if + +; Display APIC status + mov esi, boot_APIC_found + cmp [irq_mode], IRQ_APIC + je @f + mov esi, boot_APIC_nfound +@@: + call boot_log + +; PRINT AMOUNT OF MEMORY + mov esi, boot_memdetect + call boot_log + + movzx ecx, word [boot_y] + if lang eq ru + or ecx, (10+30*6) shl 16 + else if lang eq sp + or ecx, (10+33*6) shl 16 + else + or ecx, (10+29*6) shl 16 + end if + sub ecx, 10 + mov edx, 0xFFFFFF + mov ebx, [MEM_AMOUNT] + shr ebx, 20 + xor edi, edi + mov eax, 0x00040000 + inc edi + call display_number_force + +; BUILD SCHEDULER + +; call build_scheduler; sys32.inc + +; mov esi, boot_devices +; call boot_log + + mov [pci_access_enabled], 1 + call pci_enum +;----------------------------------------------------------------------------- + mov dx, [IDEContrRegsBaseAddr] +; test whether it is our interrupt? + add dx, 2 + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +@@: + add dx, 8 +; test whether it is our interrupt? + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +@@: +; read status register and remove the interrupt request + mov dx, [IDE_BAR0_val] ;0x1F0 + add dx, 0x7 ;0x1F7 + in al, dx + mov dx, [IDE_BAR2_val] ;0x170 + add dx, 0x7 ;0x177 + in al, dx +;----------------------------------------------------------------------------- +include "detect/vortex86.inc" ; Vortex86 SoC detection code + + stdcall load_driver, szVidintel + + call usb_init + +; SET PRELIMINARY WINDOW STACK AND POSITIONS + + mov esi, boot_windefs + call boot_log + call set_window_defaults + +; SET BACKGROUND DEFAULTS + + mov esi, boot_bgr + call boot_log + call init_background + call calculatebackground + +; RESERVE SYSTEM IRQ'S JA PORT'S + + mov esi, boot_resirqports + call boot_log + call reserve_irqs_ports + + call init_display + mov eax, [def_cursor] + mov [SLOT_BASE+APPDATA.cursor+256], eax + mov [SLOT_BASE+APPDATA.cursor+256*2], eax + +; PRINT CPU FREQUENCY + + mov esi, boot_cpufreq + call boot_log + + cli ;FIXME check IF + rdtsc + mov ecx, eax + mov esi, 250 ; wait 1/4 a second + call delay_ms + 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 + mov ebx, 1000000 + div ebx + mov ebx, eax + + movzx ecx, word [boot_y] + if lang eq ru + add ecx, (10+19*6) shl 16 - 10 + else if lang eq sp + add ecx, (10+25*6) shl 16 - 10 + else + add ecx, (10+17*6) shl 16 - 10 + end if + + mov edx, 0xFFFFFF + xor edi, edi + mov eax, 0x00040000 + inc edi + call display_number_force + +; SET VARIABLES + + call set_variables + +; STACK AND FDC + +; call stack_init + call fdc_init + +; PALETTE FOR 320x200 and 640x480 16 col + + cmp [SCR_MODE], word 0x12 + jne no_pal_vga + mov esi, boot_pal_vga + call boot_log + call paletteVGA + no_pal_vga: + + cmp [SCR_MODE], word 0x13 + jne no_pal_ega + mov esi, boot_pal_ega + call boot_log + call palette320x200 + no_pal_ega: + +; LOAD DEFAULT SKIN + + call load_default_skin + +;protect io permission map + + mov esi, [default_io_map] + stdcall map_page, esi, [SLOT_BASE+256+APPDATA.io_map], PG_MAP + add esi, 0x1000 + stdcall map_page, esi, [SLOT_BASE+256+APPDATA.io_map+4], PG_MAP + + stdcall map_page, tss._io_map_0, \ + [SLOT_BASE+256+APPDATA.io_map], PG_MAP + stdcall map_page, tss._io_map_1, \ + [SLOT_BASE+256+APPDATA.io_map+4], PG_MAP + +; LOAD FIRST APPLICATION + cmp byte [launcher_start], 1 ; Check if starting LAUNCHER is selected on blue screen (1 = yes) + jnz first_app_found + + cli + mov ebp, firstapp + call fs_execute_from_sysdir + test eax, eax + jnz first_app_found + + mov esi, boot_failed + call boot_log + + mov eax, 0xDEADBEEF ; otherwise halt + hlt + +first_app_found: + + cli + +; SET KEYBOARD PARAMETERS + mov al, 0xf6 ; reset keyboard, scan enabled + call kb_write + test ah, ah + jnz .no_keyboard + + ; wait until 8042 is ready + xor ecx, ecx + @@: + in al, 64h + and al, 00000010b + loopnz @b + +iglobal +align 4 +ps2_keyboard_functions: + dd .end - $ + dd 0 ; no close + dd ps2_set_lights +.end: +endg + stdcall register_keyboard, ps2_keyboard_functions, 0 + ; mov al, 0xED ; Keyboard LEDs - only for testing! + ; call kb_write + ; call kb_read + ; mov al, 111b + ; call kb_write + ; call kb_read + + mov al, 0xF3 ; set repeat rate & delay + call kb_write +; call kb_read + mov al, 0; 30 250 ;00100010b ; 24 500 ;00100100b ; 20 500 + call kb_write +; call kb_read + ;// mike.dld [ + call set_lights + ;// mike.dld ] + stdcall attach_int_handler, 1, irq1, 0 + DEBUGF 1, "K : IRQ1 error code %x\n", eax +.no_keyboard: + +; SET MOUSE + + stdcall load_driver, szPS2MDriver +; stdcall load_driver, szCOM_MDriver + + mov esi, boot_setmouse + call boot_log + call setmouse + +; Setup serial output console (if enabled) + +if defined debug_com_base + + ; enable Divisor latch + + mov dx, debug_com_base+3 + mov al, 1 shl 7 + out dx, al + + ; Set speed to 115200 baud (max speed) + + mov dx, debug_com_base + mov al, 0x01 + out dx, al + + mov dx, debug_com_base+1 + mov al, 0x00 + out dx, al + + ; No parity, 8bits words, one stop bit, dlab bit back to 0 + + mov dx, debug_com_base+3 + mov al, 3 + out dx, al + + ; disable interrupts + + mov dx, debug_com_base+1 + mov al, 0 + out dx, al + + ; clear + enable fifo (64 bits) + + mov dx, debug_com_base+2 + mov al, 0x7 + 1 shl 5 + out dx, al + + +end if + mov eax, [version_inf.rev] + DEBUGF 1, "K : kernel SVN r%d\n", eax + + mov eax, [cpu_count] + test eax, eax + jnz @F + mov al, 1 ; at least one CPU +@@: + DEBUGF 1, "K : %d CPU detected\n", eax + + DEBUGF 1, "K : BAR0 %x \n", [IDE_BAR0_val]:4 + DEBUGF 1, "K : BAR1 %x \n", [IDE_BAR1_val]:4 + DEBUGF 1, "K : BAR2 %x \n", [IDE_BAR2_val]:4 + DEBUGF 1, "K : BAR3 %x \n", [IDE_BAR3_val]:4 + DEBUGF 1, "K : BAR4 %x \n", [IDEContrRegsBaseAddr]:4 + DEBUGF 1, "K : IDEContrProgrammingInterface %x \n", [IDEContrProgrammingInterface]:4 + DEBUGF 1, "K : IDE_Interrupt %x \n", [IDE_Interrupt]:4 + +; START MULTITASKING + +; A 'All set - press ESC to start' messages if need +if preboot_blogesc + mov esi, boot_tasking + call boot_log +.bll1: + in al, 0x60 ; wait for ESC key press + cmp al, 129 + jne .bll1 +end if + + push eax edx + mov dx, [IDEContrRegsBaseAddr] + xor eax, eax + add dx, 2 + in al, dx + DEBUGF 1, "K : Primary Bus Master IDE Status Register %x\n", eax + + add dx, 8 + in al, dx + DEBUGF 1, "K : Secondary Bus Master IDE Status Register %x\n", eax + pop edx eax + + cmp [IDEContrRegsBaseAddr], 0 + setnz [dma_hdd] + + cmp [IDEContrProgrammingInterface], 0 + je set_interrupts_for_IDE_controllers.continue +;----------------------------------------------------------------------------- +; set interrupts for IDE Controller +;----------------------------------------------------------------------------- + mov esi, boot_set_int_IDE + call boot_log +set_interrupts_for_IDE_controllers: + mov ax, [IDEContrProgrammingInterface] + cmp ax, 0x0180 + je .pata_ide + + cmp ax, 0x018a + jne .sata_ide +;-------------------------------------- +.pata_ide: + cmp [IDEContrRegsBaseAddr], 0 + je .end_set_interrupts + + stdcall attach_int_handler, 14, IDE_irq_14_handler, 0 + DEBUGF 1, "K : Set IDE IRQ14 return code %x\n", eax + stdcall attach_int_handler, 15, IDE_irq_15_handler, 0 + DEBUGF 1, "K : Set IDE IRQ15 return code %x\n", eax + jmp .enable_IDE_interrupt +;-------------------------------------- +.sata_ide: + cmp ax, 0x0185 + je .sata_ide_1 + + cmp ax, 0x018f + jne .end_set_interrupts +;-------------------------------------- +.sata_ide_1: + cmp [IDEContrRegsBaseAddr], 0 + je .end_set_interrupts + + mov ax, [IDE_Interrupt] + movzx eax, al + stdcall attach_int_handler, eax, IDE_common_irq_handler, 0 + DEBUGF 1, "K : Set IDE IRQ%d return code %x\n", [IDE_Interrupt]:1, eax +;-------------------------------------- +.enable_IDE_interrupt: + mov esi, boot_enabling_ide + call boot_log +; Enable interrupts in IDE controller for DMA + mov al, 0 + mov ah, [DRIVE_DATA+1] + test ah, 10100000b + jz @f + + DEBUGF 1, "K : IDE CH1 PIO, because ATAPI drive present\n" + jmp .ch2_check +@@: + mov dx, [IDE_BAR1_val] ;0x3F4 + add dx, 2 ;0x3F6 + out dx, al + DEBUGF 1, "K : IDE CH1 DMA enabled\n" +.ch2_check: + test ah, 1010b + jz @f + + DEBUGF 1, "K : IDE CH2 PIO, because ATAPI drive present\n" + jmp .end_set_interrupts +@@: + mov dx, [IDE_BAR3_val] ;0x374 + add dx, 2 ;0x376 + out dx, al + DEBUGF 1, "K : IDE CH2 DMA enabled\n" +;-------------------------------------- +.end_set_interrupts: +;----------------------------------------------------------------------------- + cmp [dma_hdd], 0 + je .print_pio +.print_dma: + DEBUGF 1, "K : IDE DMA mode\n" + jmp .continue + +.print_pio: + DEBUGF 1, "K : IDE PIO mode\n" +.continue: + + mov [timer_ticks_enable], 1 ; for cd driver + + sti +; call change_task + + jmp osloop + + + ; Fly :) + +include 'unpacker.inc' + +align 4 +boot_log: + pushad + + mov ebx, 10*65536 + mov bx, word [boot_y] + add [boot_y], dword 10 + mov ecx, 0x80ffffff; ASCIIZ string with white color + xor edi, edi + mov edx, esi + inc edi + call dtext + + mov [novesachecksum], 1000 + call checkVga_N13 + + popad + + 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.process], sys_proc + + 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 ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +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 check_fdd_motor_status + call check_ATAPI_device_event + call check_lights_state + call check_timers + jmp osloop +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; MAIN OS LOOP END ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +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 +align 4 +osloop_nonperiodic_work dd ? +endg + +align 4 +idle_thread: + sti +idle_loop: + hlt + jmp idle_loop + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; INCLUDED SYSTEM FILES ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +include "kernel32.inc" + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; KERNEL FUNCTIONS ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +reserve_irqs_ports: + + +; RESERVE PORTS + mov eax, RESERVED_PORTS + mov ecx, 1 + + mov [eax], dword 4 + + mov [eax+16], ecx + mov [eax+16+4], dword 0 + mov [eax+16+8], dword 0x2D + + mov [eax+32], ecx + mov [eax+32+4], dword 0x30 + mov [eax+32+8], dword 0x4D + + mov [eax+48], ecx + mov [eax+48+4], dword 0x50 + mov [eax+48+8], dword 0xDF + + mov [eax+64], ecx + mov [eax+64+4], dword 0xE5 + mov [eax+64+8], dword 0xFF + + ret + + +iglobal + process_number dd 0x2 +endg + +set_variables: + + mov ecx, 0x16 ; flush port 0x60 +.fl60: + in al, 0x60 + loop .fl60 + push eax + + mov ax, [BOOT_VARS+BOOT_Y_RES] + shr ax, 1 + shl eax, 16 + mov ax, [BOOT_VARS+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 [KEY_COUNT], al ; keyboard buffer + mov byte [BTN_COUNT], al ; button buffer +; mov [MOUSE_X],dword 100*65536+100 ; mouse x/y + + pop eax + ret + +align 4 +;input eax=43,bl-byte of output, ecx - number of port +sys_outport: + + mov edi, ecx ; separate flag for read / write + and ecx, 65535 + + mov eax, [RESERVED_PORTS] + test eax, eax + jnz .sopl8 + inc eax + mov [esp+32], eax + ret + + .sopl8: + mov edx, [TASK_BASE] + mov edx, [edx+0x4] + ;and ecx,65535 + ;cld - set on interrupt 0x40 + .sopl1: + + mov esi, eax + shl esi, 4 + add esi, RESERVED_PORTS + cmp edx, [esi+0] + jne .sopl2 + cmp ecx, [esi+4] + jb .sopl2 + cmp ecx, [esi+8] + jg .sopl2 +.sopl3: + + test edi, 0x80000000; read ? + jnz .sopl4 + + mov eax, ebx + mov dx, cx ; write + out dx, al + and [esp+32], dword 0 + ret + + .sopl2: + + dec eax + jnz .sopl1 + inc eax + mov [esp+32], eax + ret + + + .sopl4: + + mov dx, cx ; read + in al, dx + and eax, 0xff + and [esp+32], dword 0 + mov [esp+20], eax + ret + +display_number: +;It is not optimization + mov eax, ebx + mov ebx, ecx + mov ecx, edx + mov edx, esi + mov esi, edi +; eax = print type, al=0 -> ebx is number +; al=1 -> ebx is pointer +; ah=0 -> display decimal +; ah=1 -> display hexadecimal +; ah=2 -> display binary +; eax bits 16-21 = number of digits to display (0-32) +; eax bits 22-31 = reserved +; +; ebx = number or pointer +; ecx = x shl 16 + y +; edx = color + xor edi, edi +display_number_force: + push eax + and eax, 0x3fffffff + cmp eax, 0xffff ; length > 0 ? + pop eax + jge cont_displ + ret + cont_displ: + push eax + and eax, 0x3fffffff + cmp eax, 61*0x10000 ; length <= 60 ? + pop eax + jb cont_displ2 + ret + cont_displ2: + + pushad + + cmp al, 1 ; ecx is a pointer ? + jne displnl1 + mov ebp, ebx + add ebp, 4 + mov ebp, [ebp+std_application_base_address] + mov ebx, [ebx+std_application_base_address] + displnl1: + sub esp, 64 + + test ah, ah ; DECIMAL + jnz no_display_desnum + shr eax, 16 + and eax, 0xC03f +; and eax,0x3f + push eax + and eax, 0x3f + mov edi, esp + add edi, 4+64-1 + mov ecx, eax + mov eax, ebx + mov ebx, 10 + d_desnum: + xor edx, edx + call division_64_bits + div ebx + add dl, 48 + mov [edi], dl + dec edi + loop d_desnum + pop eax + call normalize_number + call draw_num_text + add esp, 64 + popad + ret + no_display_desnum: + + cmp ah, 0x01 ; HEXADECIMAL + jne no_display_hexnum + shr eax, 16 + and eax, 0xC03f +; and eax,0x3f + push eax + and eax, 0x3f + mov edi, esp + add edi, 4+64-1 + mov ecx, eax + mov eax, ebx + mov ebx, 16 + d_hexnum: + xor edx, edx + call division_64_bits + div ebx + hexletters = __fdo_hexdigits + add edx, hexletters + mov dl, [edx] + mov [edi], dl + dec edi + loop d_hexnum + pop eax + call normalize_number + call draw_num_text + add esp, 64 + popad + ret + no_display_hexnum: + + cmp ah, 0x02 ; BINARY + jne no_display_binnum + shr eax, 16 + and eax, 0xC03f +; and eax,0x3f + push eax + and eax, 0x3f + mov edi, esp + add edi, 4+64-1 + mov ecx, eax + mov eax, ebx + mov ebx, 2 + d_binnum: + xor edx, edx + call division_64_bits + div ebx + add dl, 48 + mov [edi], dl + dec edi + loop d_binnum + pop eax + call normalize_number + call draw_num_text + add esp, 64 + popad + ret + no_display_binnum: + + add esp, 64 + popad + ret + +normalize_number: + test ah, 0x80 + jz .continue + mov ecx, 48 + and eax, 0x3f +@@: + inc edi + cmp [edi], cl + jne .continue + dec eax + cmp eax, 1 + ja @r + mov al, 1 +.continue: + and eax, 0x3f + ret + +division_64_bits: + test [esp+1+4], byte 0x40 + jz .continue + push eax + mov eax, ebp + div ebx + mov ebp, eax + pop eax +.continue: + ret + +draw_num_text: + mov esi, eax + mov edx, 64+4 + sub edx, eax + add edx, esp + mov ebx, [esp+64+32-8+4] +; add window start x & y + mov ecx, [TASK_BASE] + + mov edi, [CURRENT_TASK] + shl edi, 8 + + mov eax, [ecx-twdw+WDATA.box.left] + add eax, [edi+SLOT_BASE+APPDATA.wnd_clientbox.left] + shl eax, 16 + add eax, [ecx-twdw+WDATA.box.top] + add eax, [edi+SLOT_BASE+APPDATA.wnd_clientbox.top] + add ebx, eax + mov ecx, [esp+64+32-12+4] + and ecx, not 0x80000000 ; force counted string + mov eax, [esp+64+8] ; background color (if given) + mov edi, [esp+64+4] + jmp dtext + +align 4 + +sys_setup: + +; 1=roland mpu midi base , base io address +; 2=keyboard 1, base kaybap 2, shift keymap, 9 country 1eng 2fi 3ger 4rus +; 3=cd base 1, pri.master 2, pri slave 3 sec master, 4 sec slave +; 5=system language, 1eng 2fi 3ger 4rus +; 7=hd base 1, pri.master 2, pri slave 3 sec master, 4 sec slave +; 8=fat32 partition in hd +; 9 +; 10 = sound dma channel +; 11 = enable lba read +; 12 = enable pci access + + + and [esp+32], dword 0 + dec ebx ; MIDI + jnz nsyse1 + cmp ecx, 0x100 + + jb nsyse1 + mov esi, 65535 + cmp esi, ecx + + jb nsyse1 + mov [midi_base], cx ;bx + mov word [mididp], cx;bx + inc cx ;bx + mov word [midisp], cx;bx + ret + +iglobal +midi_base dw 0 +endg + + nsyse1: + dec ebx ; KEYBOARD + jnz nsyse2 + mov edi, [TASK_BASE] + mov eax, [edi+TASKDATA.mem_start] + add eax, edx + + dec ecx + jnz kbnobase + mov ebx, keymap + mov ecx, 128 + call memmove + ret + kbnobase: + dec ecx + jnz kbnoshift + + mov ebx, keymap_shift + mov ecx, 128 + call memmove + ret + kbnoshift: + dec ecx + jnz kbnoalt + mov ebx, keymap_alt + mov ecx, 128 + call memmove + ret + kbnoalt: + sub ecx, 6 + jnz kbnocountry + mov word [keyboard], dx + ret + kbnocountry: + mov [esp+32], dword 1 + ret + nsyse2: + dec ebx ; CD + jnz nsyse4 + + test ecx, ecx + jz nosesl + + cmp ecx, 4 + ja nosesl + mov [cd_base], cl + + dec ecx + jnz noprma + mov eax, [hd_address_table] + mov [cdbase], eax ;0x1f0 + mov [cdid], 0xa0 + noprma: + + dec ecx + jnz noprsl + mov eax, [hd_address_table] + mov [cdbase], eax ;0x1f0 + mov [cdid], 0xb0 + noprsl: + dec ecx + jnz nosema + mov eax, [hd_address_table+16] + mov [cdbase], eax ;0x170 + mov [cdid], 0xa0 + nosema: + dec ecx + jnz nosesl + mov eax, [hd_address_table+16] + mov [cdbase], eax ;0x170 + mov [cdid], 0xb0 + nosesl: + ret + +iglobal +cd_base db 0 + +endg + nsyse4: + + sub ebx, 2 ; SYSTEM LANGUAGE + jnz nsyse5 + mov [syslang], ecx + ret + nsyse5: + + sub ebx, 2 ; HD BASE - obsolete + jnz nsyse7 + + nosethd: + ret + +nsyse7: + +; cmp eax,8 ; HD PARTITION - obsolete + dec ebx + jnz nsyse8 + ret + +nsyse8: +; cmp eax,11 ; ENABLE LBA READ + and ecx, 1 + sub ebx, 3 + jnz no_set_lba_read + mov [lba_read_enabled], ecx + ret + +no_set_lba_read: +; cmp eax,12 ; ENABLE PCI ACCESS + dec ebx + jnz sys_setup_err + mov [pci_access_enabled], ecx + ret + +sys_setup_err: + or [esp+32], dword -1 + ret + +align 4 + +sys_getsetup: + +; 1=roland mpu midi base , base io address +; 2=keyboard 1, base kaybap 2, shift keymap, 9 country 1eng 2fi 3ger 4rus +; 3=cd base 1, pri.master 2, pri slave 3 sec master, 4 sec slave +; 5=system language, 1eng 2fi 3ger 4rus +; 7=hd base 1, pri.master 2, pri slave 3 sec master, 4 sec slave +; 8=fat32 partition in hd +; 9=get hs timer tic + +; cmp eax,1 + dec ebx + jnz ngsyse1 + movzx eax, [midi_base] + mov [esp+32], eax + ret +ngsyse1: +; cmp eax,2 + dec ebx + jnz ngsyse2 + + mov edi, [TASK_BASE] + mov ebx, [edi+TASKDATA.mem_start] + add ebx, edx + +; cmp ebx,1 + dec ecx + jnz kbnobaseret + mov eax, keymap + mov ecx, 128 + call memmove + ret +kbnobaseret: +; cmp ebx,2 + dec ecx + jnz kbnoshiftret + + mov eax, keymap_shift + mov ecx, 128 + call memmove + ret +kbnoshiftret: +; cmp ebx,3 + dec ecx + jne kbnoaltret + + mov eax, keymap_alt + mov ecx, 128 + call memmove + ret +kbnoaltret: +; cmp ebx,9 + sub ecx, 6 + jnz ngsyse2 + movzx eax, word [keyboard] + mov [esp+32], eax + ret + + +ngsyse2: +; cmp eax,3 + dec ebx + jnz ngsyse3 + movzx eax, [cd_base] + mov [esp+32], eax + ret +ngsyse3: +; cmp eax,5 + sub ebx, 2 + jnz ngsyse5 + mov eax, [syslang] + mov [esp+32], eax + ret +ngsyse5: +; cmp eax,7 + sub ebx, 2 + jnz ngsyse7 + xor eax, eax + mov [esp+32], eax + ret +ngsyse7: +; cmp eax,8 + dec ebx + jnz ngsyse8 + mov eax, [fat32part] + mov [esp+32], eax + ret +ngsyse8: +; cmp eax,9 + dec ebx + jnz ngsyse9 + mov eax, [timer_ticks];[0xfdf0] + mov [esp+32], eax + ret +ngsyse9: +; cmp eax,11 + sub ebx, 2 + jnz ngsyse11 + mov eax, [lba_read_enabled] + mov [esp+32], eax + ret +ngsyse11: +; cmp eax,12 + dec ebx + jnz ngsyse12 + mov eax, [pci_access_enabled] + mov [esp+32], eax + ret +ngsyse12: + mov [esp+32], dword 1 + ret + + +get_timer_ticks: + mov eax, [timer_ticks] + ret + +iglobal +align 4 +mousefn dd msscreen, mswin, msbutton, msset + dd app_load_cursor + dd app_set_cursor + dd app_delete_cursor + dd msz +endg + +readmousepos: + +; eax=0 screen relative +; eax=1 window relative +; eax=2 buttons pressed +; eax=3 set mouse pos ; reserved +; eax=4 load cursor +; eax=5 set cursor +; eax=6 delete cursor ; reserved +; eax=7 get mouse_z + + cmp ebx, 7 + ja msset + jmp [mousefn+ebx*4] +msscreen: + mov eax, [MOUSE_X] + shl eax, 16 + mov ax, [MOUSE_Y] + mov [esp+36-4], eax + ret +mswin: + mov eax, [MOUSE_X] + shl eax, 16 + mov ax, [MOUSE_Y] + mov esi, [TASK_BASE] + mov bx, word [esi-twdw+WDATA.box.left] + shl ebx, 16 + mov bx, word [esi-twdw+WDATA.box.top] + sub eax, ebx + + mov edi, [CURRENT_TASK] + shl edi, 8 + sub ax, word[edi+SLOT_BASE+APPDATA.wnd_clientbox.top] + rol eax, 16 + sub ax, word[edi+SLOT_BASE+APPDATA.wnd_clientbox.left] + rol eax, 16 + mov [esp+36-4], eax + ret +msbutton: + movzx eax, byte [BTN_DOWN] + mov [esp+36-4], eax + ret +msz: + mov edi, [TASK_COUNT] + movzx edi, word [WIN_POS + edi*2] + cmp edi, [CURRENT_TASK] + jne @f + mov ax, [MOUSE_SCROLL_H] + shl eax, 16 + mov ax, [MOUSE_SCROLL_V] + mov [esp+36-4], eax + and [MOUSE_SCROLL_H], word 0 + and [MOUSE_SCROLL_V], word 0 + ret + @@: + and [esp+36-4], dword 0 +; ret +msset: + ret + +app_load_cursor: + cmp ecx, OS_BASE + jae msset + stdcall load_cursor, ecx, edx + mov [esp+36-4], eax + ret + +app_set_cursor: + stdcall set_cursor, ecx + mov [esp+36-4], eax + ret + +app_delete_cursor: + stdcall delete_cursor, ecx + mov [esp+36-4], eax + ret + +is_input: + + push edx + mov dx, word [midisp] + in al, dx + and al, 0x80 + pop edx + ret + +is_output: + + push edx + mov dx, word [midisp] + in al, dx + and al, 0x40 + pop edx + ret + + +get_mpu_in: + + push edx + mov dx, word [mididp] + in al, dx + pop edx + ret + + +put_mpu_out: + + push edx + mov dx, word [mididp] + out dx, al + pop edx + ret + + + +align 4 + +sys_midi: + cmp [mididp], 0 + jnz sm0 + mov [esp+36], dword 1 + ret +sm0: + and [esp+36], dword 0 + dec ebx + jnz smn1 + ; call setuart +su1: + call is_output + test al, al + jnz su1 + mov dx, word [midisp] + mov al, 0xff + out dx, al +su2: + mov dx, word [midisp] + mov al, 0xff + out dx, al + call is_input + test al, al + jnz su2 + call get_mpu_in + cmp al, 0xfe + jnz su2 +su3: + call is_output + test al, al + jnz su3 + mov dx, word [midisp] + mov al, 0x3f + out dx, al + ret +smn1: + dec ebx + jnz smn2 +sm10: + call get_mpu_in + call is_output + test al, al + jnz sm10 + mov al, bl + call put_mpu_out + smn2: + ret + +detect_devices: +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +;include 'detect/commouse.inc' +;include 'detect/ps2mouse.inc' +;include 'detect/dev_fd.inc' +;include 'detect/dev_hdcd.inc' +;include 'detect/sear_par.inc' +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ret + +sys_end: +;-------------------------------------- + cmp [_display.select_cursor], 0 + je @f +; restore default cursor before killing + pusha + mov ecx, [current_slot] + 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] + test eax, eax + jz @F + + stdcall user_free, eax +@@: + + mov eax, [TASK_BASE] + mov [eax+TASKDATA.state], 3; terminate this program + call wakeup_osloop + +.waitterm: ; wait here for termination + call change_task + jmp .waitterm +;------------------------------------------------------------------------------ +align 4 +restore_default_cursor_before_killing: + pushfd + cli + mov eax, [def_cursor] + mov [ecx+APPDATA.cursor], eax + + movzx eax, word [MOUSE_Y] + movzx ebx, word [MOUSE_X] +; mov ecx, [Screen_Max_X] +; inc ecx +; mul ecx + mov eax, [d_width_calc_area + eax*4] + + add eax, [_WinMapAddress] + movzx edx, byte [ebx+eax] + shl edx, 8 + mov esi, [edx+SLOT_BASE+APPDATA.cursor] + + cmp esi, [current_cursor] + je @f + + push esi + call [_display.select_cursor] + mov [current_cursor], esi +@@: + mov [redrawmouse_unconditional], 1 + call wakeup_osloop + popfd + ret +;------------------------------------------------------------------------------ +iglobal +align 4 +sys_system_table: + dd sysfn_deactivate ; 1 = deactivate window + dd sysfn_terminate ; 2 = terminate thread + dd sysfn_activate ; 3 = activate window + dd sysfn_getidletime ; 4 = get idle time + dd sysfn_getcpuclock ; 5 = get cpu clock + dd sysfn_saveramdisk ; 6 = save ramdisk + dd sysfn_getactive ; 7 = get active window + dd sysfn_sound_flag ; 8 = get/set sound_flag + dd sysfn_shutdown ; 9 = shutdown with parameter + dd sysfn_minimize ; 10 = minimize window + dd sysfn_getdiskinfo ; 11 = get disk subsystem info + dd sysfn_lastkey ; 12 = get last pressed key + dd sysfn_getversion ; 13 = get kernel version + dd sysfn_waitretrace ; 14 = wait retrace + dd sysfn_centermouse ; 15 = center mouse cursor + dd sysfn_getfreemem ; 16 = get free memory size + dd sysfn_getallmem ; 17 = get total memory size + dd sysfn_terminate2 ; 18 = terminate thread using PID + ; instead of slot + dd sysfn_mouse_acceleration; 19 = set/get mouse acceleration + dd sysfn_meminfo ; 20 = get extended memory info + dd sysfn_pid_to_slot ; 21 = get slot number for pid + dd sysfn_min_rest_window ; 22 = minimize and restore any window + dd sysfn_min_windows ; 23 = minimize all windows + dd sysfn_set_screen_sizes ; 24 = set screen sizes for Vesa +sysfn_num = ($ - sys_system_table)/4 +endg +;------------------------------------------------------------------------------ +sys_system: + dec ebx + cmp ebx, sysfn_num + jae @f + jmp dword [sys_system_table + ebx*4] +@@: + ret +;------------------------------------------------------------------------------ +sysfn_shutdown: ; 18.9 = system shutdown + cmp ecx, 1 + jl exit_for_anyone + cmp ecx, 4 + jg exit_for_anyone + mov [BOOT_VARS+0x9030], cl + + mov eax, [TASK_COUNT] + mov [SYS_SHUTDOWN], al + mov [shutdown_processes], eax + call wakeup_osloop + and dword [esp+32], 0 + exit_for_anyone: + ret + uglobal + shutdown_processes: + dd 0x0 + endg +;------------------------------------------------------------------------------ +sysfn_terminate: ; 18.2 = TERMINATE + push ecx + cmp ecx, 2 + jb noprocessterminate + mov edx, [TASK_COUNT] + cmp ecx, edx + ja noprocessterminate + mov eax, [TASK_COUNT] + shl ecx, 5 + mov edx, [ecx+CURRENT_TASK+TASKDATA.pid] + add ecx, CURRENT_TASK+TASKDATA.state + cmp byte [ecx], 9 + jz noprocessterminate + push ecx edx + lea edx, [(ecx-(CURRENT_TASK and 1FFFFFFFh)-TASKDATA.state)*8+SLOT_BASE] + call request_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 +; restore default cursor before killing + pusha + mov ecx, [esp+32] + shl ecx, 8 + add ecx, SLOT_BASE + mov eax, [def_cursor] + cmp [ecx+APPDATA.cursor], eax + je @f + call restore_default_cursor_before_killing +@@: + popa +.restore_end: +;-------------------------------------- + ;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_owner]; clear app table stat + jne noatsc + call unlock_application_table +noatsc: +noprocessterminate: + add esp, 4 + ret +;------------------------------------------------------------------------------ +sysfn_terminate2: +;lock application_table_status mutex +.table_status: + call lock_application_table + mov eax, ecx + call pid_to_slot + test eax, eax + jz .not_found + mov ecx, eax + cli + call sysfn_terminate + call unlock_application_table + sti + and dword [esp+32], 0 + ret +.not_found: + call unlock_application_table + or dword [esp+32], -1 + ret +;------------------------------------------------------------------------------ +sysfn_deactivate: ; 18.1 = DEACTIVATE WINDOW + cmp ecx, 2 + jb .nowindowdeactivate + cmp ecx, [TASK_COUNT] + ja .nowindowdeactivate + + movzx esi, word [WIN_STACK + ecx*2] + cmp esi, 1 + je .nowindowdeactivate ; already deactive + + mov edi, ecx + shl edi, 5 + add edi, window_data + movzx esi, word [WIN_STACK + ecx * 2] + lea esi, [WIN_POS + esi * 2] + call window._.window_deactivate + + call syscall_display_settings._.calculate_whole_screen + call syscall_display_settings._.redraw_whole_screen +.nowindowdeactivate: + ret +;------------------------------------------------------------------------------ +sysfn_activate: ; 18.3 = ACTIVATE WINDOW + cmp ecx, 2 + jb .nowindowactivate + cmp ecx, [TASK_COUNT] + ja .nowindowactivate +;------------------------------------- +@@: +; If the window is captured and moved by the user, +; then you can't change the position in window stack!!! + mov al, [mouse.active_sys_window.action] + and al, WINDOW_MOVE_AND_RESIZE_FLAGS + test al, al + jz @f + call change_task + jmp @b +@@: +;------------------------------------- + mov [window_minimize], 2; restore window if minimized + call wakeup_osloop + + movzx esi, word [WIN_STACK + ecx*2] + cmp esi, [TASK_COUNT] + je .nowindowactivate; already active + + mov edi, ecx + shl edi, 5 + add edi, window_data + movzx esi, word [WIN_STACK + ecx * 2] + lea esi, [WIN_POS + esi * 2] + call waredraw +.nowindowactivate: + ret +;------------------------------------------------------------------------------ +sysfn_getidletime: ; 18.4 = GET IDLETIME + mov eax, [CURRENT_TASK+32+TASKDATA.cpu_usage] + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_getcpuclock: ; 18.5 = GET TSC/SEC + mov eax, dword [cpu_freq] + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +get_cpu_freq: + mov eax, dword [cpu_freq] + mov edx, dword [cpu_freq+4] + ret +; SAVE ramdisk to /hd/1/menuet.img +;!!!!!!!!!!!!!!!!!!!!!!!! + include 'blkdev/rdsave.inc' +;!!!!!!!!!!!!!!!!!!!!!!!! +;------------------------------------------------------------------------------ +align 4 +sysfn_getactive: ; 18.7 = get active window + mov eax, [TASK_COUNT] + movzx eax, word [WIN_POS + eax*2] + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_sound_flag: ; 18.8 = get/set sound_flag +; cmp ecx,1 + dec ecx + jnz nogetsoundflag + movzx eax, byte [sound_flag]; get sound_flag + mov [esp+32], eax + ret + nogetsoundflag: +; cmp ecx,2 + dec ecx + jnz nosoundflag + xor byte [sound_flag], 1 + nosoundflag: + ret +;------------------------------------------------------------------------------ +sysfn_minimize: ; 18.10 = minimize window + mov [window_minimize], 1 + call wakeup_osloop + ret +;------------------------------------------------------------------------------ +align 4 +sysfn_getdiskinfo: ; 18.11 = get disk info table +; cmp ecx,1 + dec ecx + jnz full_table + small_table: + call for_all_tables + mov ecx, 10 + cld + rep movsb + ret + for_all_tables: + mov edi, edx + mov esi, DRIVE_DATA + ret + full_table: +; cmp ecx,2 + dec ecx + jnz exit_for_anyone + call for_all_tables + mov ecx, DRIVE_DATA_SIZE/4 + cld + rep movsd + ret +;------------------------------------------------------------------------------ +sysfn_lastkey: ; 18.12 = return 0 (backward compatibility) + and dword [esp+32], 0 + ret +;------------------------------------------------------------------------------ +sysfn_getversion: ; 18.13 = get kernel ID and version + mov edi, ecx + mov esi, version_inf + mov ecx, version_end-version_inf + rep movsb + ret +;------------------------------------------------------------------------------ +sysfn_waitretrace: ; 18.14 = sys wait retrace + ;wait retrace functions + sys_wait_retrace: + mov edx, 0x3da + WaitRetrace_loop: + in al, dx + test al, 1000b + jz WaitRetrace_loop + and [esp+32], dword 0 + ret +;------------------------------------------------------------------------------ +align 4 +sysfn_centermouse: ; 18.15 = mouse centered +; removed here by +; call mouse_centered +;* mouse centered - start code- Mario79 +;mouse_centered: +; push eax + mov eax, [Screen_Max_X] + shr eax, 1 + mov [MOUSE_X], ax + mov eax, [Screen_Max_Y] + shr eax, 1 + mov [MOUSE_Y], ax + call wakeup_osloop +; ret +;* mouse centered - end code- Mario79 + xor eax, eax + and [esp+32], eax +; pop eax + ret +;------------------------------------------------------------------------------ +align 4 +sysfn_mouse_acceleration: ; 18.19 = set/get mouse features + test ecx, ecx; get mouse speed factor + jnz .set_mouse_acceleration + xor eax, eax + mov ax, [mouse_speed_factor] + mov [esp+32], eax + ret + .set_mouse_acceleration: +; cmp ecx,1 ; set mouse speed factor + dec ecx + jnz .get_mouse_delay + mov [mouse_speed_factor], dx + ret + .get_mouse_delay: +; cmp ecx,2 ; get mouse delay + dec ecx + jnz .set_mouse_delay + mov eax, [mouse_delay] + mov [esp+32], eax + ret + .set_mouse_delay: +; cmp ecx,3 ; set mouse delay + dec ecx + jnz .set_pointer_position + mov [mouse_delay], edx + ret + .set_pointer_position: +; cmp ecx,4 ; set mouse pointer position + dec ecx + jnz .set_mouse_button + cmp dx, word[Screen_Max_Y] + ja .end + rol edx, 16 + cmp dx, word[Screen_Max_X] + ja .end + mov [MOUSE_X], edx + mov [mouse_active], 1 + call wakeup_osloop + ret + .set_mouse_button: +; cmp ecx,5 ; set mouse button features + dec ecx + jnz .end + mov [BTN_DOWN], dl + mov [mouse_active], 1 + call wakeup_osloop + .end: + ret +;------------------------------------------------------------------------------ +sysfn_getfreemem: + mov eax, [pg_data.pages_free] + shl eax, 2 + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_getallmem: + mov eax, [MEM_AMOUNT] + shr eax, 10 + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_pid_to_slot: + mov eax, ecx + call pid_to_slot + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_min_rest_window: + pushad + mov eax, edx ; ebx - operating + shr ecx, 1 + jnc @f + call pid_to_slot +@@: + or eax, eax ; eax - number of slot + jz .error + cmp eax, 255 ; varify maximal slot number + ja .error + movzx eax, word [WIN_STACK + eax*2] + shr ecx, 1 + jc .restore + ; .minimize: + call minimize_window + jmp .exit +.restore: + call restore_minimized_window +.exit: + popad + xor eax, eax + mov [esp+32], eax + ret +.error: + popad + xor eax, eax + dec eax + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +sysfn_min_windows: + call minimize_all_window + mov [esp+32], eax + call change_task + ret +;------------------------------------------------------------------------------ +sysfn_set_screen_sizes: + cmp [SCR_MODE], word 0x13 + jbe .exit + + cmp [_display.select_cursor], select_cursor + jne .exit + + cmp ecx, [display_width_standard] + ja .exit + + cmp edx, [display_height_standard] + ja .exit + + pushfd + cli + mov eax, ecx + mov ecx, [_display.pitch] + mov [_display.width], eax + dec eax + mov [_display.height], edx + dec edx +; eax - new Screen_Max_X +; edx - new Screen_Max_Y + mov [do_not_touch_winmap], 1 + call set_screen + mov [do_not_touch_winmap], 0 + popfd + call change_task +.exit: + ret +;------------------------------------------------------------------------------ +uglobal +screen_workarea RECT +display_width_standard dd 0 +display_height_standard dd 0 +do_not_touch_winmap db 0 +window_minimize db 0 +sound_flag db 0 + +endg + +UID_NONE=0 +UID_MENUETOS=1 ;official +UID_KOLIBRI=2 ;russian + +iglobal +version_inf: + db 0,7,7,0 ; version 0.7.7.0 + db 0 +.rev dd __REV__ +version_end: +endg +;------------------------------------------------------------------------------ +align 4 +sys_cachetodiskette: + cmp ebx, 1 + jb .no_floppy_save + cmp ebx, 2 + ja .no_floppy_save + call save_image + mov [esp + 32], eax + ret +.no_floppy_save: + mov [esp + 32], dword 1 + ret +;------------------------------------------------------------------------------ +uglobal +; bgrchanged dd 0x0 +align 4 +bgrlockpid dd 0 +bgrlock db 0 +endg +;------------------------------------------------------------------------------ +align 4 +sys_background: + cmp ebx, 1 ; BACKGROUND SIZE + jnz nosb1 + test ecx, ecx + jz sbgrr + + test edx, edx + jz sbgrr +;-------------------------------------- +align 4 +@@: +;;Maxis use atomic bts for mutexes 4.4.2009 + bts dword [bgrlock], 0 + jnc @f + call change_task + jmp @b +;-------------------------------------- +align 4 +@@: + mov [BgrDataWidth], ecx + mov [BgrDataHeight], edx +; mov [bgrchanged],1 + + pushad +; return memory for old background + mov eax, [img_background] + cmp eax, static_background_data + jz @f + stdcall kernel_free, eax +;-------------------------------------- +align 4 +@@: +; calculate RAW size + xor eax, eax + inc eax + cmp [BgrDataWidth], eax + jae @f + mov [BgrDataWidth], eax +;-------------------------------------- +align 4 +@@: + cmp [BgrDataHeight], eax + jae @f + mov [BgrDataHeight], eax +;-------------------------------------- +align 4 +@@: + mov eax, [BgrDataWidth] + imul eax, [BgrDataHeight] + lea eax, [eax*3] +; it is reserved with aligned to the boundary of 4 KB pages, +; otherwise there may be exceptions a page fault for vesa20_drawbackground_tiled +; because the 32 bit read is used for high performance: "mov eax,[esi]" + shr eax, 12 + inc eax + shl eax, 12 + mov [mem_BACKGROUND], eax +; get memory for new background + stdcall kernel_alloc, eax + test eax, eax + jz .memfailed + mov [img_background], eax + jmp .exit +;-------------------------------------- +align 4 +.memfailed: +; revert to static monotone data + mov [img_background], static_background_data + xor eax, eax + inc eax + mov [BgrDataWidth], eax + mov [BgrDataHeight], eax + mov [mem_BACKGROUND], 4 +;-------------------------------------- +align 4 +.exit: + popad + mov [bgrlock], 0 +;-------------------------------------- +align 4 +sbgrr: + ret +;------------------------------------------------------------------------------ +align 4 +nosb1: + cmp ebx, 2 ; SET PIXEL + jnz nosb2 + + mov eax, [img_background] + test ecx, ecx + jz @f + cmp eax, static_background_data + jz .ret +;-------------------------------------- +align 4 +@@: + mov ebx, [mem_BACKGROUND] + add ebx, 4095 + and ebx, -4096 + sub ebx, 4 + cmp ecx, ebx + ja .ret + + mov ebx, [eax+ecx] + and ebx, 0xFF000000;255*256*256*256 + and edx, 0x00FFFFFF;255*256*256+255*256+255 + add edx, ebx + mov [eax+ecx], edx +;-------------------------------------- +align 4 +.ret: + ret +;------------------------------------------------------------------------------ +align 4 +nosb2: + cmp ebx, 3 ; DRAW BACKGROUND + jnz nosb3 +;-------------------------------------- +align 4 +draw_background_temp: + mov [background_defined], 1 + call force_redraw_background +;-------------------------------------- +align 4 +nosb31: + ret +;------------------------------------------------------------------------------ +align 4 +nosb3: + cmp ebx, 4 ; TILED / STRETCHED + jnz nosb4 + cmp ecx, [BgrDrawMode] + je nosb41 + mov [BgrDrawMode], ecx +;-------------------------------------- +align 4 +nosb41: + ret +;------------------------------------------------------------------------------ +align 4 +nosb4: + cmp ebx, 5 ; BLOCK MOVE TO BGR + jnz nosb5 + cmp [img_background], static_background_data + jnz @f + test edx, edx + jnz .fin + cmp esi, 4 + ja .fin +;-------------------------------------- +align 4 +@@: + ; bughere + mov eax, ecx + mov ebx, edx + add ebx, [img_background];IMG_BACKGROUND + mov ecx, esi + call memmove +;-------------------------------------- +align 4 +.fin: + ret +;------------------------------------------------------------------------------ +align 4 +nosb5: + cmp ebx, 6 + jnz nosb6 +;-------------------------------------- +align 4 +;;Maxis use atomic bts for mutex 4.4.2009 +@@: + bts dword [bgrlock], 0 + jnc @f + call change_task + jmp @b +;-------------------------------------- +align 4 +@@: + mov eax, [CURRENT_TASK] + mov [bgrlockpid], eax + cmp [img_background], static_background_data + jz .nomem + stdcall user_alloc, [mem_BACKGROUND] + mov [esp+32], eax + test eax, eax + jz .nomem + mov ebx, eax + shr ebx, 12 + or dword [page_tabs+(ebx-1)*4], DONT_FREE_BLOCK + mov esi, [img_background] + shr esi, 12 + mov ecx, [mem_BACKGROUND] + add ecx, 0xFFF + shr ecx, 12 +;-------------------------------------- +align 4 +.z: + mov eax, [page_tabs+ebx*4] + test al, 1 + jz @f + call free_page +;-------------------------------------- +align 4 +@@: + mov eax, [page_tabs+esi*4] + or al, PG_UW + mov [page_tabs+ebx*4], eax + mov eax, ebx + shl eax, 12 + invlpg [eax] + inc ebx + inc esi + loop .z + ret +;-------------------------------------- +align 4 +.nomem: + and [bgrlockpid], 0 + mov [bgrlock], 0 +;------------------------------------------------------------------------------ +align 4 +nosb6: + cmp ebx, 7 + jnz nosb7 + cmp [bgrlock], 0 + jz .err + mov eax, [CURRENT_TASK] + cmp [bgrlockpid], eax + jnz .err + mov eax, ecx + mov ebx, ecx + shr eax, 12 + mov ecx, [page_tabs+(eax-1)*4] + test cl, USED_BLOCK+DONT_FREE_BLOCK + jz .err + jnp .err + push eax + shr ecx, 12 + dec ecx +;-------------------------------------- +align 4 +@@: + and dword [page_tabs+eax*4], 0 + mov edx, eax + shl edx, 12 + push eax + invlpg [edx] + pop eax + inc eax + loop @b + pop eax + and dword [page_tabs+(eax-1)*4], not DONT_FREE_BLOCK + stdcall user_free, ebx + mov [esp+32], eax + and [bgrlockpid], 0 + mov [bgrlock], 0 + ret +;-------------------------------------- +align 4 +.err: + and dword [esp+32], 0 + ret +;------------------------------------------------------------------------------ +align 4 +nosb7: + cmp ebx, 8 + jnz nosb8 + + mov ecx, [current_slot] + xor eax, eax + xchg eax, [ecx+APPDATA.draw_bgr_x] + mov [esp + 32], eax ; eax = [left]*65536 + [right] + xor eax, eax + xchg eax, [ecx+APPDATA.draw_bgr_y] + mov [esp + 20], eax ; ebx = [top]*65536 + [bottom] + ret +;------------------------------------------------------------------------------ +align 4 +nosb8: + cmp ebx, 9 + jnz nosb9 +; ecx = [left]*65536 + [right] +; edx = [top]*65536 + [bottom] + mov eax, [Screen_Max_X] + mov ebx, [Screen_Max_Y] +; check [right] + cmp cx, ax + ja .exit +; check [left] + ror ecx, 16 + cmp cx, ax + ja .exit +; check [bottom] + cmp dx, bx + ja .exit +; check [top] + ror edx, 16 + cmp dx, bx + ja .exit + + movzx eax, cx ; [left] + movzx ebx, dx ; [top] + + shr ecx, 16 ; [right] + shr edx, 16 ; [bottom] + + mov [background_defined], 1 + + mov [draw_data+32 + RECT.left], eax + mov [draw_data+32 + RECT.top], ebx + + mov [draw_data+32 + RECT.right], ecx + mov [draw_data+32 + RECT.bottom], edx + + inc byte[REDRAW_BACKGROUND] + call wakeup_osloop +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +nosb9: + ret +;------------------------------------------------------------------------------ +align 4 +uglobal + BG_Rect_X_left_right dd 0x0 + BG_Rect_Y_top_bottom dd 0x0 +endg +;------------------------------------------------------------------------------ +align 4 +force_redraw_background: + and [draw_data+32 + RECT.left], 0 + and [draw_data+32 + RECT.top], 0 + push eax ebx + mov eax, [Screen_Max_X] + mov ebx, [Screen_Max_Y] + mov [draw_data+32 + RECT.right], eax + mov [draw_data+32 + RECT.bottom], ebx + pop ebx eax + inc byte[REDRAW_BACKGROUND] + call wakeup_osloop + ret +;------------------------------------------------------------------------------ +align 4 +sys_getbackground: +; cmp eax,1 ; SIZE + dec ebx + jnz nogb1 + mov eax, [BgrDataWidth] + shl eax, 16 + mov ax, word [BgrDataHeight] + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +align 4 +nogb1: +; cmp eax,2 ; PIXEL + dec ebx + jnz nogb2 + + mov eax, [img_background] + test ecx, ecx + jz @f + cmp eax, static_background_data + jz .ret +;-------------------------------------- +align 4 +@@: + mov ebx, [mem_BACKGROUND] + add ebx, 4095 + and ebx, -4096 + sub ebx, 4 + cmp ecx, ebx + ja .ret + + mov eax, [ecx+eax] + + and eax, 0xFFFFFF + mov [esp+32], eax +;-------------------------------------- +align 4 +.ret: + ret +;------------------------------------------------------------------------------ +align 4 +nogb2: + +; cmp eax,4 ; TILED / STRETCHED + dec ebx + dec ebx + jnz nogb4 + mov eax, [BgrDrawMode] +;-------------------------------------- +align 4 +nogb4: + mov [esp+32], eax + ret +;------------------------------------------------------------------------------ +align 4 +sys_getkey: + mov [esp + 32], dword 1 + ; test main buffer + mov ebx, [CURRENT_TASK] ; TOP OF WINDOW STACK + movzx ecx, word [WIN_STACK + ebx * 2] + mov edx, [TASK_COUNT] + cmp ecx, edx + jne .finish + cmp [KEY_COUNT], byte 0 + je .finish + movzx eax, byte [KEY_BUFF] + shl eax, 8 + push eax + dec byte [KEY_COUNT] + and byte [KEY_COUNT], 127 + movzx ecx, byte [KEY_COUNT] + add ecx, 2 + mov eax, KEY_BUFF + 1 + mov ebx, KEY_BUFF + call memmove + pop eax +;-------------------------------------- +align 4 +.ret_eax: + mov [esp + 32], eax + ret +;-------------------------------------- +align 4 +.finish: +; test hotkeys buffer + mov ecx, hotkey_buffer +;-------------------------------------- +align 4 +@@: + cmp [ecx], ebx + jz .found + add ecx, 8 + cmp ecx, hotkey_buffer + 120 * 8 + jb @b + ret +;-------------------------------------- +align 4 +.found: + mov ax, [ecx + 6] + shl eax, 16 + mov ah, [ecx + 4] + mov al, 2 + and dword [ecx + 4], 0 + and dword [ecx], 0 + jmp .ret_eax +;------------------------------------------------------------------------------ +align 4 +sys_getbutton: + mov ebx, [CURRENT_TASK] ; TOP OF WINDOW STACK + mov [esp + 32], dword 1 + movzx ecx, word [WIN_STACK + ebx * 2] + mov edx, [TASK_COUNT] ; less than 256 processes + cmp ecx, edx + jne .exit + movzx eax, byte [BTN_COUNT] + test eax, eax + jz .exit + mov eax, [BTN_BUFF] + and al, 0xFE ; delete left button bit + mov [BTN_COUNT], byte 0 + mov [esp + 32], eax +;-------------------------------------- +align 4 +.exit: + ret +;------------------------------------------------------------------------------ +align 4 +sys_cpuusage: + +; RETURN: +; +; +00 dword process cpu usage +; +04 word position in windowing stack +; +06 word windowing stack value at current position (cpu nro) +; +10 12 bytes name +; +22 dword start in mem +; +26 dword used mem +; +30 dword PID , process idenfification number +; + + cmp ecx, -1 ; who am I ? + jne .no_who_am_i + mov ecx, [CURRENT_TASK] + .no_who_am_i: + cmp ecx, max_processes + ja .nofillbuf + +; +4: word: position of the window of thread in the window stack + mov ax, [WIN_STACK + ecx * 2] + mov [ebx+4], ax +; +6: word: number of the thread slot, which window has in the window stack +; position ecx (has no relation to the specific thread) + mov ax, [WIN_POS + ecx * 2] + mov [ebx+6], ax + + shl ecx, 5 + +; +0: dword: memory usage + mov eax, [ecx+CURRENT_TASK+TASKDATA.cpu_usage] + mov [ebx], eax +; +10: 11 bytes: name of the process + push ecx + lea eax, [ecx*8+SLOT_BASE+APPDATA.app_name] + add ebx, 10 + mov ecx, 11 + call memmove + pop ecx + +; +22: address of the process in memory +; +26: size of used memory - 1 + push edi + lea edi, [ebx+12] + xor eax, eax + mov edx, 0x100000*16 + cmp ecx, 1 shl 5 + je .os_mem + mov edx, [SLOT_BASE+ecx*8+APPDATA.mem_size] + mov eax, std_application_base_address +.os_mem: + stosd + lea eax, [edx-1] + stosd + +; +30: PID/TID + mov eax, [ecx+CURRENT_TASK+TASKDATA.pid] + stosd + + ; window position and size + push esi + lea esi, [ecx + window_data + WDATA.box] + movsd + movsd + movsd + movsd + + ; Process state (+50) + mov eax, dword [ecx+CURRENT_TASK+TASKDATA.state] + stosd + + ; Window client area box + lea esi, [ecx*8 + SLOT_BASE + APPDATA.wnd_clientbox] + movsd + movsd + movsd + movsd + + ; Window state + mov al, [ecx+window_data+WDATA.fl_wstate] + stosb + + ; Event mask (+71) + mov EAX, dword [ECX+CURRENT_TASK+TASKDATA.event_mask] + stosd + + ; Keyboard mode (+75) + mov al, byte [ecx*8 + SLOT_BASE + APPDATA.keyboard_mode] + stosb + + pop esi + pop edi + +.nofillbuf: + ; return number of processes + + mov eax, [TASK_COUNT] + mov [esp+32], eax + ret + +align 4 +sys_clock: + cli + ; Mikhail Lisovin xx Jan 2005 + @@: + mov al, 10 + out 0x70, al + in al, 0x71 + test al, al + jns @f + mov esi, 1 + call delay_ms + jmp @b + @@: + ; end Lisovin's fix + + xor al, al ; seconds + out 0x70, al + in al, 0x71 + movzx ecx, al + mov al, 02 ; minutes + shl ecx, 16 + out 0x70, al + in al, 0x71 + movzx edx, al + mov al, 04 ; hours + shl edx, 8 + out 0x70, al + in al, 0x71 + add ecx, edx + movzx edx, al + add ecx, edx + sti + mov [esp + 32], ecx + ret + + +align 4 + +sys_date: + + cli + @@: + mov al, 10 + out 0x70, al + in al, 0x71 + test al, al + jns @f + mov esi, 1 + call delay_ms + jmp @b + @@: + + mov ch, 0 + mov al, 7 ; date + out 0x70, al + in al, 0x71 + mov cl, al + mov al, 8 ; month + shl ecx, 16 + out 0x70, al + in al, 0x71 + mov ch, al + mov al, 9 ; year + out 0x70, al + in al, 0x71 + mov cl, al + sti + mov [esp+32], ecx + ret + + +; redraw status + +sys_redrawstat: + cmp ebx, 1 + jne no_widgets_away + ; buttons away + mov ecx, [CURRENT_TASK] + sys_newba2: + mov edi, [BTN_ADDR] + cmp [edi], dword 0 ; empty button list ? + je end_of_buttons_away + movzx ebx, word [edi] + inc ebx + mov eax, edi + sys_newba: + dec ebx + jz end_of_buttons_away + + add eax, 0x10 + cmp cx, [eax] + jnz sys_newba + + push eax ebx ecx + mov ecx, ebx + inc ecx + shl ecx, 4 + mov ebx, eax + add eax, 0x10 + call memmove + dec dword [edi] + pop ecx ebx eax + + jmp sys_newba2 + + end_of_buttons_away: + + ret + + no_widgets_away: + + cmp ebx, 2 + jnz srl1 + + mov edx, [TASK_BASE] ; return whole screen draw area for this app + add edx, draw_data - CURRENT_TASK + mov [edx + RECT.left], 0 + mov [edx + RECT.top], 0 + mov eax, [Screen_Max_X] + mov [edx + RECT.right], eax + mov eax, [Screen_Max_Y] + mov [edx + RECT.bottom], eax + + srl1: + ret + +;ok - 100% work +;nt - not tested +;--------------------------------------------------------------------------------------------- +;eax +;0 - task switch counter. Ret switch counter in eax. Block. ok. +;1 - change task. Ret nothing. Block. ok. +;2 - performance control +; ebx +; 0 - enable or disable (inversion) PCE flag on CR4 for rdmpc in user mode. +; returned new cr4 in eax. Ret cr4 in eax. Block. ok. +; 1 - is cache enabled. Ret cr0 in eax if enabled else zero in eax. Block. ok. +; 2 - enable cache. Ret 1 in eax. Ret nothing. Block. ok. +; 3 - disable cache. Ret 0 in eax. Ret nothing. Block. ok. +;eax +;3 - rdmsr. Counter in edx. (edx:eax) [esi:edi, edx] => [edx:esi, ecx]. Ret in ebx:eax. Block. ok. +;4 - wrmsr. Counter in edx. (edx:eax) [esi:edi, edx] => [edx:esi, ecx]. Ret in ebx:eax. Block. ok. +;--------------------------------------------------------------------------------------------- +iglobal +align 4 +sheduler: + dd sys_sheduler.00 + dd change_task + dd sys_sheduler.02 + dd sys_sheduler.03 + dd sys_sheduler.04 +endg +sys_sheduler: +;rewritten by 29.12.2009 + jmp dword [sheduler+ebx*4] +;.shed_counter: +.00: + mov eax, [context_counter] + mov [esp+32], eax + ret + +.02: +;.perf_control: + inc ebx ;before ebx=2, ebx=3 + cmp ebx, ecx ;if ecx=3, ebx=3 + jz cache_disable + + dec ebx ;ebx=2 + cmp ebx, ecx ; + jz cache_enable ;if ecx=2 and ebx=2 + + dec ebx ;ebx=1 + cmp ebx, ecx + jz is_cache_enabled ;if ecx=1 and ebx=1 + + dec ebx + test ebx, ecx ;ebx=0 and ecx=0 + jz modify_pce ;if ecx=0 + + ret + +.03: +;.rdmsr_instr: +;now counter in ecx +;(edx:eax) esi:edi => edx:esi + mov eax, esi + mov ecx, edx + rdmsr + mov [esp+32], eax + mov [esp+20], edx ;ret in ebx? + ret + +.04: +;.wrmsr_instr: +;now counter in ecx +;(edx:eax) esi:edi => edx:esi + ; Fast Call MSR can't be destroy + ; Но MSR_AMD_EFER можно изменять, т.к. в этом регистре лиш + ; включаются/выключаются расширенные возможности + cmp edx, MSR_SYSENTER_CS + je @f + cmp edx, MSR_SYSENTER_ESP + je @f + cmp edx, MSR_SYSENTER_EIP + je @f + cmp edx, MSR_AMD_STAR + je @f + + mov eax, esi + mov ecx, edx + wrmsr + ; mov [esp + 32], eax + ; mov [esp + 20], edx ;ret in ebx? +@@: + ret + +cache_disable: + mov eax, cr0 + or eax, 01100000000000000000000000000000b + mov cr0, eax + wbinvd ;set MESI + ret + +cache_enable: + mov eax, cr0 + and eax, 10011111111111111111111111111111b + mov cr0, eax + ret + +is_cache_enabled: + mov eax, cr0 + mov ebx, eax + and eax, 01100000000000000000000000000000b + jz cache_disabled + mov [esp+32], ebx +cache_disabled: + mov dword [esp+32], eax;0 + ret + +modify_pce: + mov eax, cr4 +; mov ebx,0 +; or bx,100000000b ;pce +; xor eax,ebx ;invert pce + bts eax, 8;pce=cr4[8] + mov cr4, eax + mov [esp+32], eax + ret +;--------------------------------------------------------------------------------------------- + + +iglobal + cpustring db 'CPU',0 +endg + +uglobal +background_defined db 0 ; diamond, 11.04.2006 +endg +;----------------------------------------------------------------------------- +align 4 +checkmisc: + cmp [ctrl_alt_del], 1 + jne nocpustart + + mov ebp, cpustring + call fs_execute_from_sysdir + + mov [ctrl_alt_del], 0 +;-------------------------------------- +align 4 +nocpustart: + cmp [mouse_active], 1 + jne mouse_not_active + mov [mouse_active], 0 + + xor edi, edi + mov ebx, CURRENT_TASK + + mov ecx, [TASK_COUNT] + movzx eax, word [WIN_POS + ecx*2] ; active window + shl eax, 8 + push eax + + movzx eax, word [MOUSE_X] + movzx edx, word [MOUSE_Y] +;-------------------------------------- +align 4 +.set_mouse_event: + add edi, 256 + add ebx, 32 + test [ebx+TASKDATA.event_mask], 0x80000000 + jz .pos_filter + + cmp edi, [esp] ; skip if filtration active + jne .skip +;-------------------------------------- +align 4 +.pos_filter: + test [ebx+TASKDATA.event_mask], 0x40000000 + jz .set + + mov esi, [ebx-twdw+WDATA.box.left] + cmp eax, esi + jb .skip + add esi, [ebx-twdw+WDATA.box.width] + cmp eax, esi + ja .skip + + mov esi, [ebx-twdw+WDATA.box.top] + cmp edx, esi + jb .skip + add esi, [ebx-twdw+WDATA.box.height] + cmp edx, esi + ja .skip +;-------------------------------------- +align 4 +.set: + or [edi+SLOT_BASE+APPDATA.event_mask], 100000b ; set event 6 +;-------------------------------------- +align 4 +.skip: + loop .set_mouse_event + + pop eax +;-------------------------------------- +align 4 +mouse_not_active: + cmp byte[REDRAW_BACKGROUND], 0 ; background update ? + jz nobackgr + + cmp [background_defined], 0 + jz nobackgr +;-------------------------------------- +align 4 +backgr: + mov eax, [draw_data+32 + RECT.left] + shl eax, 16 + add eax, [draw_data+32 + RECT.right] + mov [BG_Rect_X_left_right], eax ; [left]*65536 + [right] + + mov eax, [draw_data+32 + RECT.top] + shl eax, 16 + add eax, [draw_data+32 + RECT.bottom] + mov [BG_Rect_Y_top_bottom], eax ; [top]*65536 + [bottom] + + call drawbackground +; DEBUGF 1, "K : drawbackground\n" +; DEBUGF 1, "K : backg x %x\n",[BG_Rect_X_left_right] +; DEBUGF 1, "K : backg y %x\n",[BG_Rect_Y_top_bottom] +;--------- set event 5 start ---------- + push ecx edi + xor edi, edi + mov ecx, [TASK_COUNT] +;-------------------------------------- +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], ax + jae @f + mov word [edi+SLOT_BASE+APPDATA.draw_bgr_x], ax +@@: + shr eax, 16 + cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_x+2], ax + jbe @f + mov word [edi+SLOT_BASE+APPDATA.draw_bgr_x+2], ax +@@: + cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_y], dx + jae @f + mov word [edi+SLOT_BASE+APPDATA.draw_bgr_y], dx +@@: + shr edx, 16 + cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_y+2], dx + jbe @f + mov word [edi+SLOT_BASE+APPDATA.draw_bgr_y+2], 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 +;--------- set event 5 stop ----------- + dec byte[REDRAW_BACKGROUND] ; got new update request? + 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 +;-------------------------------------- +align 4 +nobackgr: +; system shutdown request + cmp [SYS_SHUTDOWN], byte 0 + je noshutdown + + mov edx, [shutdown_processes] + + cmp [SYS_SHUTDOWN], dl + jne noshutdown + + lea ecx, [edx-1] + mov edx, OS_BASE+0x3040 + jecxz no_mark_system_shutdown +;-------------------------------------- +align 4 +markz: + push ecx edx + cmp [edx+TASKDATA.state], 9 + jz .nokill + lea edx, [(edx-(CURRENT_TASK and 1FFFFFFFh))*8+SLOT_BASE] + cmp [edx+APPDATA.process], sys_proc + jz .nokill + call request_terminate + jmp .common +.nokill: + dec byte [SYS_SHUTDOWN] + xor eax, eax +.common: + pop edx ecx + test eax, eax + jz @f + mov [edx+TASKDATA.state], byte 3 +@@: + add edx, 0x20 + loop markz + call wakeup_osloop +;-------------------------------------- +align 4 +@@: +no_mark_system_shutdown: + dec byte [SYS_SHUTDOWN] + je system_shutdown +;-------------------------------------- +align 4 +noshutdown: + mov eax, [TASK_COUNT] ; termination + mov ebx, TASK_DATA+TASKDATA.state + mov esi, 1 +;-------------------------------------- +align 4 +newct: + mov cl, [ebx] + cmp cl, byte 3 + jz .terminate + + cmp cl, byte 4 + jnz .noterminate +.terminate: + pushad + mov ecx, eax + shl ecx, 8 + add ecx, SLOT_BASE + call restore_default_cursor_before_killing + popad + + pushad + call terminate + popad + cmp byte [SYS_SHUTDOWN], 0 + jz .noterminate + dec byte [SYS_SHUTDOWN] + je system_shutdown + +.noterminate: + add ebx, 0x20 + inc esi + dec eax + jnz newct + ret +;----------------------------------------------------------------------------- +align 4 +redrawscreen: +; eax , if process window_data base is eax, do not set flag/limits + + pushad + push eax + +;;; mov ebx,2 +;;; call delay_hs + + ;mov ecx,0 ; redraw flags for apps + xor ecx, ecx +;-------------------------------------- +align 4 +newdw2: + inc ecx + push ecx + + mov eax, ecx + shl eax, 5 + add eax, window_data + + cmp eax, [esp+4] + je not_this_task + ; check if window in redraw area + mov edi, eax + + cmp ecx, 1 ; limit for background + jz bgli + + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.top] + + mov ecx, [draw_limits.bottom] ; ecx = area y end ebx = window y start + cmp ecx, ebx + jb ricino + + mov ecx, [draw_limits.right] ; ecx = area x end eax = window x start + cmp ecx, eax + jb ricino + + mov eax, [edi + WDATA.box.left] + mov ebx, [edi + WDATA.box.top] + mov ecx, [edi + WDATA.box.width] + mov edx, [edi + WDATA.box.height] + add ecx, eax + add edx, ebx + + mov eax, [draw_limits.top] ; eax = area y start edx = window y end + cmp edx, eax + jb ricino + + mov eax, [draw_limits.left] ; eax = area x start ecx = window x end + cmp ecx, eax + jb ricino +;-------------------------------------- +align 4 +bgli: + cmp dword[esp], 1 + jnz .az + + cmp byte[REDRAW_BACKGROUND], 0 + jz .az + + mov dl, 0 + lea eax, [edi+draw_data-window_data] + mov ebx, [draw_limits.left] + cmp ebx, [eax+RECT.left] + jae @f + + mov [eax+RECT.left], ebx + mov dl, 1 +;-------------------------------------- +align 4 +@@: + mov ebx, [draw_limits.top] + cmp ebx, [eax+RECT.top] + jae @f + + mov [eax+RECT.top], ebx + mov dl, 1 +;-------------------------------------- +align 4 +@@: + mov ebx, [draw_limits.right] + cmp ebx, [eax+RECT.right] + jbe @f + + mov [eax+RECT.right], ebx + mov dl, 1 +;-------------------------------------- +align 4 +@@: + mov ebx, [draw_limits.bottom] + cmp ebx, [eax+RECT.bottom] + jbe @f + + mov [eax+RECT.bottom], ebx + mov dl, 1 +;-------------------------------------- +align 4 +@@: + add byte[REDRAW_BACKGROUND], dl + call wakeup_osloop + jmp newdw8 +;-------------------------------------- +align 4 +.az: + mov eax, edi + add eax, draw_data-window_data + + mov ebx, [draw_limits.left] ; set limits + mov [eax + RECT.left], ebx + mov ebx, [draw_limits.top] + mov [eax + RECT.top], ebx + mov ebx, [draw_limits.right] + mov [eax + RECT.right], ebx + mov ebx, [draw_limits.bottom] + mov [eax + RECT.bottom], ebx + + sub eax, draw_data-window_data + + cmp dword [esp], 1 + jne nobgrd + inc byte[REDRAW_BACKGROUND] + call wakeup_osloop +;-------------------------------------- +align 4 +newdw8: +nobgrd: +;-------------------------------------- + push eax edi ebp + mov edi, [esp+12] + cmp edi, 1 + je .found + + mov eax, [draw_limits.left] + mov ebx, [draw_limits.top] + mov ecx, [draw_limits.right] + sub ecx, eax + test ecx, ecx + jz .not_found + + mov edx, [draw_limits.bottom] + sub edx, ebx + test edx, edx + jz .not_found + +; eax - x, ebx - y +; ecx - size x, edx - size y + add ebx, edx +;-------------------------------------- +align 4 +.start_y: + push ecx +;-------------------------------------- +align 4 +.start_x: + add eax, ecx + mov ebp, [d_width_calc_area + ebx*4] + add ebp, [_WinMapAddress] + movzx ebp, byte[eax+ebp] ; get value for current point + cmp ebp, edi + jne @f + + pop ecx + jmp .found +;-------------------------------------- +align 4 +@@: + sub eax, ecx + + dec ecx + jnz .start_x + + pop ecx + dec ebx + dec edx + jnz .start_y +;-------------------------------------- +align 4 +.not_found: + pop ebp edi eax + jmp ricino +;-------------------------------------- +align 4 +.found: + pop ebp edi eax + + mov [eax + WDATA.fl_redraw], byte 1 ; mark as redraw +;-------------------------------------- +align 4 +ricino: +not_this_task: + pop ecx + + cmp ecx, [TASK_COUNT] + jle newdw2 + + pop eax + popad + ret +;----------------------------------------------------------------------------- +align 4 +calculatebackground: ; background + mov edi, [_WinMapAddress] ; set os to use all pixels + mov eax, 0x01010101 + mov ecx, [_WinMapSize] + shr ecx, 2 + rep stosd + + mov byte[REDRAW_BACKGROUND], 0 ; do not draw background! + ret +;----------------------------------------------------------------------------- +uglobal + imax dd 0x0 +endg +;----------------------------------------------------------------------------- +align 4 +delay_ms: ; delay in 1/1000 sec + push eax + push ecx + + mov ecx, esi + ; + imul ecx, 33941 + shr ecx, 9 + ; + + in al, 0x61 + and al, 0x10 + mov ah, al + cld +;-------------------------------------- +align 4 +cnt1: + in al, 0x61 + and al, 0x10 + cmp al, ah + jz cnt1 + + mov ah, al + loop cnt1 + + pop ecx + pop eax + ret +;----------------------------------------------------------------------------- +align 4 +set_app_param: + mov edi, [TASK_BASE] + mov eax, ebx + btr eax, 3 ; move MOUSE_FILTRATION + mov ebx, [current_slot] ; bit into event_filter + setc byte [ebx+APPDATA.event_filter] + xchg eax, [edi + TASKDATA.event_mask] ; set new event mask + mov [esp+32], eax ; return old mask value + ret +;----------------------------------------------------------------------------- + +; this is for syscall +proc delay_hs_unprotected + call unprotect_from_terminate + call delay_hs + call protect_from_terminate + ret +endp + +if 1 +align 4 +delay_hs: ; delay in 1/100 secs +; ebx = delay time + + pushad + push ebx + xor esi, esi + mov ecx, MANUAL_DESTROY + call create_event + test eax, eax + jz .done + + mov ebx, edx + mov ecx, [esp] + push edx + push eax + call wait_event_timeout + pop eax + pop ebx + call destroy_event +.done: + add esp, 4 + popad + ret + +else + +align 4 +delay_hs: ; delay in 1/100 secs +; ebx = delay time + push ecx + push edx + + mov edx, [timer_ticks] +;-------------------------------------- +align 4 +newtic: + mov ecx, [timer_ticks] + sub ecx, edx + cmp ecx, ebx + jae zerodelay + + call change_task + + jmp newtic +;-------------------------------------- +align 4 +zerodelay: + pop edx + pop ecx + ret +end if + +;----------------------------------------------------------------------------- +align 16 ;very often call this subrutine +memmove: ; memory move in bytes +; eax = from +; ebx = to +; ecx = no of bytes + test ecx, ecx + jle .ret + + push esi edi ecx + + mov edi, ebx + mov esi, eax + + test ecx, not 11b + jz @f + + push ecx + shr ecx, 2 + rep movsd + pop ecx + and ecx, 11b + jz .finish +;-------------------------------------- +align 4 +@@: + rep movsb +;-------------------------------------- +align 4 +.finish: + pop ecx edi esi +;-------------------------------------- +align 4 +.ret: + ret +;----------------------------------------------------------------------------- +; Sysfunction 34, read_floppy_file, is obsolete. Use 58 or 70 function instead. +;align 4 +; +;read_floppy_file: +; +;; as input +;; +;; eax pointer to file +;; ebx file lenght +;; ecx start 512 byte block number +;; edx number of blocks to read +;; esi pointer to return/work area (atleast 20 000 bytes) +;; +;; +;; on return +;; +;; eax = 0 command succesful +;; 1 no fd base and/or partition defined +;; 2 yet unsupported FS +;; 3 unknown FS +;; 4 partition not defined at hd +;; 5 file not found +;; ebx = size of file +; +; mov edi,[TASK_BASE] +; add edi,0x10 +; add esi,[edi] +; add eax,[edi] +; +; pushad +; mov edi,esi +; add edi,1024 +; mov esi,0x100000+19*512 +; sub ecx,1 +; shl ecx,9 +; add esi,ecx +; shl edx,9 +; mov ecx,edx +; cld +; rep movsb +; popad +; +; mov [esp+36],eax +; mov [esp+24],ebx +; ret + + + +align 4 +set_io_access_rights: + push edi eax + mov edi, tss._io_map_0 +; mov ecx,eax +; and ecx,7 ; offset in byte +; shr eax,3 ; number of byte +; add edi,eax +; mov ebx,1 +; shl ebx,cl + test ebp, ebp +; cmp ebp,0 ; enable access - ebp = 0 + jnz .siar1 +; not ebx +; and [edi],byte bl + btr [edi], eax + pop eax edi + ret +.siar1: + bts [edi], eax + ; or [edi],byte bl ; disable access - ebp = 1 + pop eax edi + ret +;reserve/free group of ports +; * eax = 46 - number function +; * ebx = 0 - reserve, 1 - free +; * ecx = number start arrea of ports +; * edx = number end arrea of ports (include last number of port) +;Return value: +; * eax = 0 - succesful +; * eax = 1 - error +; * The system has reserve this ports: +; 0..0x2d, 0x30..0x4d, 0x50..0xdf, 0xe5..0xff (include last number of port). +;destroys eax,ebx, ebp +r_f_port_area: + + test ebx, ebx + jnz free_port_area +; je r_port_area +; jmp free_port_area + +; r_port_area: + +; pushad + + cmp ecx, edx ; beginning > end ? + ja rpal1 + cmp edx, 65536 + jae rpal1 + mov eax, [RESERVED_PORTS] + test eax, eax ; no reserved areas ? + je rpal2 + cmp eax, 255 ; max reserved + jae rpal1 + rpal3: + mov ebx, eax + shl ebx, 4 + add ebx, RESERVED_PORTS + cmp ecx, [ebx+8] + ja rpal4 + cmp edx, [ebx+4] + jae rpal1 +; jb rpal4 +; jmp rpal1 + rpal4: + dec eax + jnz rpal3 + jmp rpal2 + rpal1: +; popad +; mov eax,1 + xor eax, eax + inc eax + ret + rpal2: +; popad + ; enable port access at port IO map + cli + pushad ; start enable io map + + cmp edx, 65536;16384 + jae no_unmask_io; jge + mov eax, ecx +; push ebp + xor ebp, ebp ; enable - eax = port +new_port_access: +; pushad + call set_io_access_rights +; popad + inc eax + cmp eax, edx + jbe new_port_access +; pop ebp +no_unmask_io: + popad ; end enable io map + sti + + mov eax, [RESERVED_PORTS] + add eax, 1 + mov [RESERVED_PORTS], eax + shl eax, 4 + add eax, RESERVED_PORTS + mov ebx, [TASK_BASE] + mov ebx, [ebx+TASKDATA.pid] + mov [eax], ebx + mov [eax+4], ecx + mov [eax+8], edx + + xor eax, eax + ret + +free_port_area: + +; pushad + mov eax, [RESERVED_PORTS]; no reserved areas ? + test eax, eax + jz frpal2 + mov ebx, [TASK_BASE] + mov ebx, [ebx+TASKDATA.pid] + frpal3: + mov edi, eax + shl edi, 4 + add edi, RESERVED_PORTS + cmp ebx, [edi] + jne frpal4 + cmp ecx, [edi+4] + jne frpal4 + cmp edx, [edi+8] + jne frpal4 + jmp frpal1 + frpal4: + dec eax + jnz frpal3 + frpal2: +; popad + inc eax + ret + frpal1: + push ecx + mov ecx, 256 + sub ecx, eax + shl ecx, 4 + mov esi, edi + add esi, 16 + cld + rep movsb + + dec dword [RESERVED_PORTS] +;popad +;disable port access at port IO map + +; pushad ; start disable io map + pop eax ;start port + cmp edx, 65536;16384 + jge no_mask_io + +; mov eax,ecx + xor ebp, ebp + inc ebp +new_port_access_disable: +; pushad +; mov ebp,1 ; disable - eax = port + call set_io_access_rights +; popad + inc eax + cmp eax, edx + jbe new_port_access_disable +no_mask_io: +; popad ; end disable io map + xor eax, eax + ret +;----------------------------------------------------------------------------- +align 4 +drawbackground: +dbrv20: + cmp [BgrDrawMode], dword 1 + jne bgrstr + call vesa20_drawbackground_tiled +; call [draw_pointer] + call __sys_draw_pointer + ret +;-------------------------------------- +align 4 +bgrstr: + call vesa20_drawbackground_stretch +; call [draw_pointer] + call __sys_draw_pointer + ret +;----------------------------------------------------------------------------- +align 4 +syscall_putimage: ; PutImage +sys_putimage: + test ecx, 0x80008000 + jnz .exit + test ecx, 0x0000FFFF + jz .exit + test ecx, 0xFFFF0000 + jnz @f +;-------------------------------------- +align 4 +.exit: + ret +;-------------------------------------- +align 4 +@@: + mov edi, [current_slot] + add dx, word[edi+APPDATA.wnd_clientbox.top] + rol edx, 16 + add dx, word[edi+APPDATA.wnd_clientbox.left] + rol edx, 16 +;-------------------------------------- +align 4 +.forced: + push ebp esi 0 + mov ebp, putimage_get24bpp + mov esi, putimage_init24bpp +;-------------------------------------- +align 4 +sys_putimage_bpp: + call vesa20_putimage + pop ebp esi ebp + ret +; jmp [draw_pointer] +;----------------------------------------------------------------------------- +align 4 +sys_putimage_palette: +; ebx = pointer to image +; ecx = [xsize]*65536 + [ysize] +; edx = [xstart]*65536 + [ystart] +; esi = number of bits per pixel, must be 8, 24 or 32 +; edi = pointer to palette +; ebp = row delta + mov eax, [CURRENT_TASK] + shl eax, 8 + add dx, word [eax+SLOT_BASE+APPDATA.wnd_clientbox.top] + rol edx, 16 + add dx, word [eax+SLOT_BASE+APPDATA.wnd_clientbox.left] + rol edx, 16 +;-------------------------------------- +align 4 +.forced: + cmp esi, 1 + jnz @f + push edi + mov eax, [edi+4] + sub eax, [edi] + push eax + push dword [edi] + push 0ffffff80h + mov edi, esp + call put_mono_image + add esp, 12 + pop edi + ret +;-------------------------------------- +align 4 +@@: + cmp esi, 2 + jnz @f + push edi + push 0ffffff80h + mov edi, esp + call put_2bit_image + pop eax + pop edi + ret +;-------------------------------------- +align 4 +@@: + cmp esi, 4 + jnz @f + push edi + push 0ffffff80h + mov edi, esp + call put_4bit_image + pop eax + pop edi + ret +;-------------------------------------- +align 4 +@@: + push ebp esi ebp + cmp esi, 8 + jnz @f + mov ebp, putimage_get8bpp + mov esi, putimage_init8bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + cmp esi, 9 + jnz @f + mov ebp, putimage_get9bpp + mov esi, putimage_init9bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + cmp esi, 15 + jnz @f + mov ebp, putimage_get15bpp + mov esi, putimage_init15bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + cmp esi, 16 + jnz @f + mov ebp, putimage_get16bpp + mov esi, putimage_init16bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + cmp esi, 24 + jnz @f + mov ebp, putimage_get24bpp + mov esi, putimage_init24bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + cmp esi, 32 + jnz @f + mov ebp, putimage_get32bpp + mov esi, putimage_init32bpp + jmp sys_putimage_bpp +;-------------------------------------- +align 4 +@@: + pop ebp esi ebp + ret +;----------------------------------------------------------------------------- +align 4 +put_mono_image: + push ebp esi ebp + mov ebp, putimage_get1bpp + mov esi, putimage_init1bpp + jmp sys_putimage_bpp +;----------------------------------------------------------------------------- +align 4 +put_2bit_image: + push ebp esi ebp + mov ebp, putimage_get2bpp + mov esi, putimage_init2bpp + jmp sys_putimage_bpp +;----------------------------------------------------------------------------- +align 4 +put_4bit_image: + push ebp esi ebp + mov ebp, putimage_get4bpp + mov esi, putimage_init4bpp + jmp sys_putimage_bpp +;----------------------------------------------------------------------------- +align 4 +putimage_init24bpp: + lea eax, [eax*3] +putimage_init8bpp: +putimage_init9bpp: + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get24bpp: + movzx eax, byte [esi+2] + shl eax, 16 + mov ax, [esi] + add esi, 3 + ret 4 +;----------------------------------------------------------------------------- +align 16 +putimage_get8bpp: + movzx eax, byte [esi] + push edx + mov edx, [esp+8] + mov eax, [edx+eax*4] + pop edx + inc esi + ret 4 +;----------------------------------------------------------------------------- +align 16 +putimage_get9bpp: + lodsb + mov ah, al + shl eax, 8 + mov al, ah + ret 4 +;----------------------------------------------------------------------------- +align 4 +putimage_init1bpp: + add eax, ecx + push ecx + add eax, 7 + add ecx, 7 + shr eax, 3 + shr ecx, 3 + sub eax, ecx + pop ecx + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get1bpp: + push edx + mov edx, [esp+8] + mov al, [edx] + add al, al + jnz @f + lodsb + adc al, al +@@: + mov [edx], al + sbb eax, eax + and eax, [edx+8] + add eax, [edx+4] + pop edx + ret 4 +;----------------------------------------------------------------------------- +align 4 +putimage_init2bpp: + add eax, ecx + push ecx + add ecx, 3 + add eax, 3 + shr ecx, 2 + shr eax, 2 + sub eax, ecx + pop ecx + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get2bpp: + push edx + mov edx, [esp+8] + mov al, [edx] + mov ah, al + shr al, 6 + shl ah, 2 + jnz .nonewbyte + lodsb + mov ah, al + shr al, 6 + shl ah, 2 + add ah, 1 +.nonewbyte: + mov [edx], ah + mov edx, [edx+4] + movzx eax, al + mov eax, [edx+eax*4] + pop edx + ret 4 +;----------------------------------------------------------------------------- +align 4 +putimage_init4bpp: + add eax, ecx + push ecx + add ecx, 1 + add eax, 1 + shr ecx, 1 + shr eax, 1 + sub eax, ecx + pop ecx + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get4bpp: + push edx + mov edx, [esp+8] + add byte [edx], 80h + jc @f + movzx eax, byte [edx+1] + mov edx, [edx+4] + and eax, 0x0F + mov eax, [edx+eax*4] + pop edx + ret 4 +@@: + movzx eax, byte [esi] + add esi, 1 + mov [edx+1], al + shr eax, 4 + mov edx, [edx+4] + mov eax, [edx+eax*4] + pop edx + ret 4 +;----------------------------------------------------------------------------- +align 4 +putimage_init32bpp: + shl eax, 2 + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get32bpp: + lodsd + ret 4 +;----------------------------------------------------------------------------- +align 4 +putimage_init15bpp: +putimage_init16bpp: + add eax, eax + ret +;----------------------------------------------------------------------------- +align 16 +putimage_get15bpp: +; 0RRRRRGGGGGBBBBB -> 00000000RRRRR000GGGGG000BBBBB000 + push ecx edx + movzx eax, word [esi] + add esi, 2 + mov ecx, eax + mov edx, eax + and eax, 0x1F + and ecx, 0x1F shl 5 + and edx, 0x1F shl 10 + shl eax, 3 + shl ecx, 6 + shl edx, 9 + or eax, ecx + or eax, edx + pop edx ecx + ret 4 +;----------------------------------------------------------------------------- +align 16 +putimage_get16bpp: +; RRRRRGGGGGGBBBBB -> 00000000RRRRR000GGGGGG00BBBBB000 + push ecx edx + movzx eax, word [esi] + add esi, 2 + mov ecx, eax + mov edx, eax + and eax, 0x1F + and ecx, 0x3F shl 5 + and edx, 0x1F shl 11 + shl eax, 3 + shl ecx, 5 + shl edx, 8 + or eax, ecx + or eax, edx + pop edx ecx + ret 4 +;----------------------------------------------------------------------------- +;align 4 +; eax x beginning +; ebx y beginning +; ecx x end + ; edx y end +; edi color +;__sys_drawbar: +; mov esi, [current_slot] +; add eax, [esi+APPDATA.wnd_clientbox.left] +; add ecx, [esi+APPDATA.wnd_clientbox.left] +; add ebx, [esi+APPDATA.wnd_clientbox.top] +; add edx, [esi+APPDATA.wnd_clientbox.top] +;-------------------------------------- +;align 4 +;.forced: +; call vesa20_drawbar +; call [draw_pointer] +; ret +;----------------------------------------------------------------------------- +align 4 +kb_read: + + push ecx edx + + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + kr_loop: + in al, 0x64 + test al, 1 + jnz kr_ready + loop kr_loop + mov ah, 1 + jmp kr_exit + kr_ready: + push ecx + mov ecx, 32 + kr_delay: + loop kr_delay + pop ecx + in al, 0x60 + xor ah, ah + kr_exit: + + pop edx ecx + + ret +;----------------------------------------------------------------------------- +align 4 +kb_write: + + push ecx edx + + mov dl, al +; mov ecx,0x1ffff ; last 0xffff, new value in view of fast CPU's +; kw_loop1: +; in al,0x64 +; test al,0x20 +; jz kw_ok1 +; loop kw_loop1 +; mov ah,1 +; jmp kw_exit +; kw_ok1: + in al, 0x60 + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + kw_loop: + in al, 0x64 + test al, 2 + jz kw_ok + loop kw_loop + mov ah, 1 + jmp kw_exit + kw_ok: + mov al, dl + out 0x60, al + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + kw_loop3: + in al, 0x64 + test al, 2 + jz kw_ok3 + loop kw_loop3 + mov ah, 1 + jmp kw_exit + kw_ok3: + mov ah, 8 + kw_loop4: + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + kw_loop5: + in al, 0x64 + test al, 1 + jnz kw_ok4 + loop kw_loop5 + dec ah + jnz kw_loop4 + kw_ok4: + xor ah, ah + kw_exit: + + pop edx ecx + + ret +;----------------------------------------------------------------------------- +align 4 +kb_cmd: + + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + c_wait: + in al, 0x64 + test al, 2 + jz c_send + loop c_wait + jmp c_error + c_send: + mov al, bl + out 0x64, al + mov ecx, 0x1ffff; last 0xffff, new value in view of fast CPU's + c_accept: + in al, 0x64 + test al, 2 + jz c_ok + loop c_accept + c_error: + mov ah, 1 + jmp c_exit + c_ok: + xor ah, ah + c_exit: + ret + + +setmouse: ; set mousepicture -pointer + ; ps2 mouse enable + +; mov [MOUSE_PICTURE], dword mousepointer + + cli + + ret + +if used _rdtsc +_rdtsc: + bt [cpu_caps], CAPS_TSC + jnc ret_rdtsc + rdtsc + ret + ret_rdtsc: + mov edx, 0xffffffff + mov eax, 0xffffffff + ret +end if + +sys_msg_board_str: + + pushad + @@: + cmp [esi], byte 0 + je @f + mov ebx, 1 + movzx ecx, byte [esi] + call sys_msg_board + inc esi + jmp @b + @@: + popad + ret + +sys_msg_board_byte: +; in: al = byte to display +; out: nothing +; destroys: nothing + pushad + mov ecx, 2 + shl eax, 24 + jmp @f + +sys_msg_board_word: +; in: ax = word to display +; out: nothing +; destroys: nothing + pushad + mov ecx, 4 + shl eax, 16 + jmp @f + +sys_msg_board_dword: +; in: eax = dword to display +; out: nothing +; destroys: nothing + pushad + mov ecx, 8 +@@: + push ecx + rol eax, 4 + push eax + and al, 0xF + cmp al, 10 + sbb al, 69h + das + mov cl, al + xor ebx, ebx + inc ebx + call sys_msg_board + pop eax + pop ecx + loop @b + popad + ret + +msg_board_data_size = 65536 ; Must be power of two + +uglobal + msg_board_data rb msg_board_data_size + msg_board_count dd 0x0 +endg + +sys_msg_board: + +; ebx=1 : write : bl byte to write +; ebx=2 : read : ebx=0 -> no data, ebx=1 -> data in al + + push eax ebx ; Save eax and ebx, since we're restoring their order required. + mov eax, ebx + mov ebx, ecx + + mov ecx, [msg_board_count] + cmp eax, 1 + jne .smbl1 + +if defined debug_com_base + + push dx ax + + @@: ; Wait for empty transmit register (yes, this slows down system..) + mov dx, debug_com_base+5 + in al, dx + test al, 1 shl 5 + jz @r + + mov dx, debug_com_base ; Output the byte + mov al, bl + out dx, al + + pop ax dx + +end if + + mov [msg_board_data+ecx], bl +; // if debug_direct_print == 1 + cmp byte [debug_direct_print], 1 + jnz @f + pusha +iglobal +msg_board_pos dd (42*6)*65536+10 ; for printing debug output on the screen +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], (42*6) + add word [msg_board_pos], 10 + mov ax, word [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 + + pop ebx eax + ret +.smbl1: + cmp eax, 2 + jne .smbl2 + test ecx, ecx + jz .smbl21 + + add esp, 8 ; Returning data in ebx and eax, so no need to restore them. + mov eax, msg_board_data+1 + mov ebx, msg_board_data + movzx edx, byte [ebx] + call memmove + dec [msg_board_count] + mov [esp + 32], edx ;eax + mov [esp + 20], dword 1 + ret +.smbl21: + mov [esp+32], ecx + mov [esp+20], ecx +.smbl2: + pop ebx eax + ret + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 66 sys function. ;; +;; in eax=66,ebx in [0..5],ecx,edx ;; +;; out eax ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +iglobal +align 4 +f66call: + dd sys_process_def.1 ; 1 = set keyboard mode + dd sys_process_def.2 ; 2 = get keyboard mode + dd sys_process_def.3 ; 3 = get keyboard ctrl, alt, shift + dd sys_process_def.4 ; 4 = set system-wide hotkey + dd sys_process_def.5 ; 5 = delete installed hotkey + dd sys_process_def.6 ; 6 = disable input, work only hotkeys + dd sys_process_def.7 ; 7 = enable input, opposition to f.66.6 +endg +;----------------------------------------------------------------------------- +align 4 +sys_process_def: + dec ebx + cmp ebx, 7 + jae .not_support ;if >=8 then or eax,-1 + + mov edi, [CURRENT_TASK] + jmp dword [f66call+ebx*4] + +.not_support: + or eax, -1 + ret +;----------------------------------------------------------------------------- +align 4 +.1: + shl edi, 8 + mov [edi+SLOT_BASE + APPDATA.keyboard_mode], cl + + ret +;----------------------------------------------------------------------------- +align 4 +.2: ; 2 = get keyboard mode + shl edi, 8 + movzx eax, byte [SLOT_BASE+edi + APPDATA.keyboard_mode] + mov [esp+32], eax + ret +;----------------------------------------------------------------------------- +align 4 +.3: ;3 = get keyboard ctrl, alt, shift + mov eax, [kb_state] + mov [esp+32], eax + ret +;----------------------------------------------------------------------------- +align 4 +.4: + mov eax, hotkey_list +@@: + cmp dword [eax+8], 0 + jz .found_free + add eax, 16 + cmp eax, hotkey_list+16*256 + jb @b + mov dword [esp+32], 1 + ret +.found_free: + mov [eax+8], edi + mov [eax+4], edx + movzx ecx, cl + lea ecx, [hotkey_scancodes+ecx*4] + mov edx, [ecx] + mov [eax], edx + mov [ecx], eax + mov [eax+12], ecx + test edx, edx + jz @f + mov [edx+12], eax +@@: + and dword [esp+32], 0 + ret +;----------------------------------------------------------------------------- +align 4 +.5: + movzx ebx, cl + lea ebx, [hotkey_scancodes+ebx*4] + mov eax, [ebx] +.scan: + test eax, eax + jz .notfound + cmp [eax+8], edi + jnz .next + cmp [eax+4], edx + jz .found +.next: + mov eax, [eax] + jmp .scan +.notfound: + mov dword [esp+32], 1 + ret +.found: + mov ecx, [eax] + jecxz @f + mov edx, [eax+12] + mov [ecx+12], edx +@@: + mov ecx, [eax+12] + mov edx, [eax] + mov [ecx], edx + xor edx, edx + mov [eax+4], edx + mov [eax+8], edx + mov [eax+12], edx + mov [eax], edx + mov [esp+32], edx + ret +;----------------------------------------------------------------------------- +align 4 +.6: + pushfd + cli + mov eax, [PID_lock_input] + test eax, eax + jnz @f +; get current PID + mov eax, [CURRENT_TASK] + shl eax, 5 + mov eax, [eax+CURRENT_TASK+TASKDATA.pid] +; set current PID for lock input + mov [PID_lock_input], eax +@@: + popfd + ret +;----------------------------------------------------------------------------- +align 4 +.7: + mov eax, [PID_lock_input] + test eax, eax + jz @f +; get current PID + mov ebx, [CURRENT_TASK] + shl ebx, 5 + mov ebx, [ebx+CURRENT_TASK+TASKDATA.pid] +; compare current lock input with current PID + cmp ebx, eax + jne @f + + xor eax, eax + mov [PID_lock_input], eax +@@: + ret +;----------------------------------------------------------------------------- +uglobal + PID_lock_input dd 0x0 +endg +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 61 sys function. ;; +;; in eax=61,ebx in [1..3] ;; +;; out eax ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +iglobal +align 4 +f61call: + dd sys_gs.1 ; resolution + dd sys_gs.2 ; bits per pixel + dd sys_gs.3 ; bytes per scanline +endg + + +align 4 + +sys_gs: ; direct screen access + dec ebx + cmp ebx, 2 + ja .not_support + jmp dword [f61call+ebx*4] +.not_support: + or [esp+32], dword -1 + ret + + +.1: ; resolution + mov eax, [Screen_Max_X] + shl eax, 16 + mov ax, word [Screen_Max_Y] + add eax, 0x00010001 + mov [esp+32], eax + ret +.2: ; bits per pixel + mov eax, [_display.bpp] + mov [esp+32], eax + ret +.3: ; bytes per scanline + mov eax, [_display.pitch] + mov [esp+32], eax + ret + +align 4 ; system functions + +syscall_setpixel: ; SetPixel + + mov eax, ebx + mov ebx, ecx + mov ecx, edx + mov edx, [TASK_BASE] + add eax, [edx-twdw+WDATA.box.left] + add ebx, [edx-twdw+WDATA.box.top] + mov edi, [current_slot] + add eax, [edi+APPDATA.wnd_clientbox.left] + add ebx, [edi+APPDATA.wnd_clientbox.top] + xor edi, edi ; no force + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; jmp [putpixel] + jmp __sys_putpixel + +align 4 + +syscall_writetext: ; WriteText + + mov eax, [TASK_BASE] + mov ebp, [eax-twdw+WDATA.box.left] + push esi + mov esi, [current_slot] + add ebp, [esi+APPDATA.wnd_clientbox.left] + shl ebp, 16 + add ebp, [eax-twdw+WDATA.box.top] + add bp, word[esi+APPDATA.wnd_clientbox.top] + pop esi + test ecx, 0x08000000 ; redirect the output to the user area + jnz @f + add ebx, ebp +align 4 +@@: + mov eax, edi + test ecx, 0x08000000 ; redirect the output to the user area + jnz dtext + xor edi, edi + jmp dtext + +align 4 + +syscall_drawrect: ; DrawRect + + mov edi, edx ; color + gradient + and edi, 0x80FFFFFF + test bx, bx ; x.size + je .drectr + test cx, cx ; y.size + je .drectr + + mov eax, ebx ; bad idea + mov ebx, ecx + + movzx ecx, ax ; ecx - x.size + shr eax, 16 ; eax - x.coord + movzx edx, bx ; edx - y.size + shr ebx, 16 ; ebx - y.coord + mov esi, [current_slot] + + add eax, [esi + APPDATA.wnd_clientbox.left] + add ebx, [esi + APPDATA.wnd_clientbox.top] + add ecx, eax + add edx, ebx +; jmp [drawbar] + jmp vesa20_drawbar +.drectr: + ret + +align 4 +syscall_getscreensize: ; GetScreenSize + mov ax, word [Screen_Max_X] + shl eax, 16 + mov ax, word [Screen_Max_Y] + mov [esp + 32], eax + ret + +align 4 + +syscall_cdaudio: ; CD + + cmp ebx, 4 + jb .audio + jz .eject + cmp ebx, 5 + jnz .ret +.load: + call .reserve + call LoadMedium + ;call .free + jmp .free +; ret +.eject: + call .reserve + call clear_CD_cache + call allow_medium_removal + call EjectMedium +; call .free + jmp .free +; ret +.audio: + call sys_cd_audio + mov [esp+36-4], eax +.ret: + ret + +.reserve: + call reserve_cd + mov eax, ecx + shr eax, 1 + and eax, 1 + inc eax + mov [ChannelNumber], ax + mov eax, ecx + and eax, 1 + mov [DiskNumber], al + call reserve_cd_channel + and ebx, 3 + inc ebx + mov [cdpos], ebx + add ebx, ebx + mov cl, 8 + sub cl, bl + mov al, [DRIVE_DATA+1] + shr al, cl + test al, 2 + jz .free;.err + ret +.free: + call free_cd_channel + and [cd_status], 0 + ret +.err: + call .free +; pop eax + ret +;----------------------------------------------------------------------------- +align 4 +syscall_getpixel_WinMap: ; GetPixel WinMap + cmp ebx, [Screen_Max_X] + jbe @f + cmp ecx, [Screen_Max_Y] + jbe @f + xor eax, eax + jmp .store +;-------------------------------------- +align 4 +@@: + mov eax, [d_width_calc_area + ecx*4] + add eax, [_WinMapAddress] + movzx eax, byte[eax+ebx] ; get value for current point +;-------------------------------------- +align 4 +.store: + mov [esp + 32], eax + ret +;----------------------------------------------------------------------------- +align 4 +syscall_getpixel: ; GetPixel + mov ecx, [Screen_Max_X] + inc ecx + xor edx, edx + mov eax, ebx + div ecx + mov ebx, edx + xchg eax, ebx + and ecx, 0xFBFFFFFF ;negate 0x04000000 use mouseunder area + call dword [GETPIXEL]; eax - x, ebx - y + mov [esp + 32], ecx + ret +;----------------------------------------------------------------------------- +align 4 +syscall_getarea: +;eax = 36 +;ebx = pointer to bufer for img BBGGRRBBGGRR... +;ecx = [size x]*65536 + [size y] +;edx = [start x]*65536 + [start y] + pushad + mov edi, ebx + mov eax, edx + shr eax, 16 + mov ebx, edx + and ebx, 0xffff + dec eax + dec ebx + ; eax - x, ebx - y + mov edx, ecx + + shr ecx, 16 + and edx, 0xffff + mov esi, ecx + ; ecx - size x, edx - size y + + mov ebp, edx + dec ebp + lea ebp, [ebp*3] + + imul ebp, esi + + mov esi, ecx + dec esi + lea esi, [esi*3] + + add ebp, esi + add ebp, edi + + add ebx, edx +;-------------------------------------- +align 4 +.start_y: + push ecx edx +;-------------------------------------- +align 4 +.start_x: + push eax ebx ecx + add eax, ecx + + and ecx, 0xFBFFFFFF ;negate 0x04000000 use mouseunder area + call dword [GETPIXEL]; eax - x, ebx - y + + mov [ebp], cx + shr ecx, 16 + mov [ebp+2], cl + + pop ecx ebx eax + sub ebp, 3 + dec ecx + jnz .start_x + pop edx ecx + dec ebx + dec edx + jnz .start_y + popad + ret +;----------------------------------------------------------------------------- +align 4 +syscall_putarea_backgr: +;eax = 25 +;ebx = pointer to bufer for img BBGGRRBBGGRR... +;ecx = [size x]*65536 + [size y] +;edx = [start x]*65536 + [start y] + pushad + mov edi, ebx + mov eax, edx + shr eax, 16 + mov ebx, edx + and ebx, 0xffff + dec eax + dec ebx +; eax - x, ebx - y + mov edx, ecx + shr ecx, 16 + and edx, 0xffff + mov esi, ecx +; ecx - size x, edx - size y + mov ebp, edx + dec ebp + shl ebp, 2 + + imul ebp, esi + + mov esi, ecx + dec esi + shl esi, 2 + + add ebp, esi + add ebp, edi + + add ebx, edx +;-------------------------------------- +align 4 +.start_y: + push ecx edx +;-------------------------------------- +align 4 +.start_x: + push eax ecx + add eax, ecx + + mov ecx, [ebp] + rol ecx, 8 + test cl, cl ; transparensy = 0 + jz .no_put + + xor cl, cl + ror ecx, 8 + + pushad + mov edx, [d_width_calc_area + ebx*4] + add edx, [_WinMapAddress] + movzx edx, byte [eax+edx] + cmp dl, byte 1 + jne @f + + call dword [PUTPIXEL]; eax - x, ebx - y +;-------------------------------------- +align 4 +@@: + popad +;-------------------------------------- +align 4 +.no_put: + pop ecx eax + + sub ebp, 4 + dec ecx + jnz .start_x + + pop edx ecx + dec ebx + dec edx + jnz .start_y + + popad + ret +;----------------------------------------------------------------------------- +align 4 +syscall_drawline: ; DrawLine + + mov edi, [TASK_BASE] + movzx eax, word[edi-twdw+WDATA.box.left] + mov ebp, eax + mov esi, [current_slot] + add ebp, [esi+APPDATA.wnd_clientbox.left] + add ax, word[esi+APPDATA.wnd_clientbox.left] + add ebp, ebx + shl eax, 16 + movzx ebx, word[edi-twdw+WDATA.box.top] + add eax, ebp + mov ebp, ebx + add ebp, [esi+APPDATA.wnd_clientbox.top] + add bx, word[esi+APPDATA.wnd_clientbox.top] + add ebp, ecx + shl ebx, 16 + xor edi, edi + add ebx, ebp + mov ecx, edx +; jmp [draw_line] + jmp __sys_draw_line + + +align 4 +syscall_reserveportarea: ; ReservePortArea and FreePortArea + + call r_f_port_area + mov [esp+32], eax + ret + +align 4 +syscall_threads: ; CreateThreads +; +; ecx=thread entry point +; edx=thread stack pointer +; +; on return : eax = pid + + xor ebx, ebx + call new_sys_threads + + mov [esp+32], eax + ret + +align 4 + +paleholder: + ret +;------------------------------------------------------------------------------ +align 4 +calculate_fast_getting_offset_for_WinMapAddress: +; calculate data area for fast getting offset to _WinMapAddress + xor eax, eax + mov ecx, [_display.height] + mov edi, d_width_calc_area + cld +@@: + stosd + add eax, [_display.width] + dec ecx + jnz @r + ret +;------------------------------------------------------------------------------ +align 4 +calculate_fast_getting_offset_for_LFB: +; calculate data area for fast getting offset to LFB + xor eax, eax + mov ecx, [_display.height] + mov edi, BPSLine_calc_area + cld +@@: + stosd + add eax, [_display.pitch] + dec ecx + jnz @r + ret +;------------------------------------------------------------------------------ +align 4 +set_screen: +; in: +; eax - new Screen_Max_X +; ecx - new BytesPerScanLine +; edx - new Screen_Max_Y + + pushfd + cli + + mov [Screen_Max_X], eax + mov [Screen_Max_Y], edx + mov [_display.pitch], ecx + + mov [screen_workarea.right], eax + mov [screen_workarea.bottom], edx + + push ebx + push esi + push edi + + pushad + + cmp [do_not_touch_winmap], 1 + je @f + + stdcall kernel_free, [_WinMapAddress] + + mov eax, [_display.width] + mul [_display.height] + mov [_WinMapSize], eax + + stdcall kernel_alloc, eax + mov [_WinMapAddress], eax + test eax, eax + jz .epic_fail +; store for f.18.24 + mov eax, [_display.width] + mov [display_width_standard], eax + + mov eax, [_display.height] + mov [display_height_standard], eax +@@: + call calculate_fast_getting_offset_for_WinMapAddress +; for Qemu or non standart video cards +; Unfortunately [BytesPerScanLine] does not always +; equal to [_display.width] * [ScreenBPP] / 8 + call calculate_fast_getting_offset_for_LFB + popad + + call repos_windows + xor eax, eax + xor ebx, ebx + mov ecx, [Screen_Max_X] + mov edx, [Screen_Max_Y] + call calculatescreen + pop edi + pop esi + pop ebx + + popfd + ret + +.epic_fail: + hlt ; Houston, we've had a problem + +; --------------- APM --------------------- +uglobal +apm_entry dp 0 +apm_vf dd 0 +endg + +align 4 +sys_apm: + xor eax, eax + cmp word [apm_vf], ax ; Check APM BIOS enable + jne @f + inc eax + or dword [esp + 44], eax ; error + add eax, 7 + mov dword [esp + 32], eax ; 32-bit protected-mode interface not supported + ret + +@@: +; xchg eax, ecx +; xchg ebx, ecx + + cmp dx, 3 + ja @f + and [esp + 44], byte 0xfe ; emulate func 0..3 as func 0 + mov eax, [apm_vf] + mov [esp + 32], eax + shr eax, 16 + mov [esp + 28], eax + ret + +@@: + + mov esi, [master_tab+(OS_BASE shr 20)] + xchg [master_tab], esi + push esi + mov edi, cr3 + mov cr3, edi ;flush TLB + + call pword [apm_entry] ;call APM BIOS + + xchg eax, [esp] + mov [master_tab], eax + mov eax, cr3 + mov cr3, eax + pop eax + + mov [esp + 4 ], edi + mov [esp + 8], esi + mov [esp + 20], ebx + mov [esp + 24], edx + mov [esp + 28], ecx + mov [esp + 32], eax + setc al + and [esp + 44], byte 0xfe + or [esp + 44], al + ret +; ----------------------------------------- + +align 4 + +undefined_syscall: ; Undefined system call + mov [esp + 32], dword -1 + ret + +align 4 +system_shutdown: ; shut down the system + + cmp byte [BOOT_VARS+0x9030], 1 + jne @F + ret +@@: + call stop_all_services + movi eax, 3 + call sys_cd_audio + +yes_shutdown_param: + cli + +if ~ defined extended_primary_loader +; load kernel.mnt to 0x7000:0 + mov ebx, kernel_file_load + pushad + call file_system_lfn + popad + + mov esi, restart_kernel_4000+OS_BASE+0x10000 ; move kernel re-starter to 0x4000:0 + mov edi, OS_BASE+0x40000 + mov ecx, 1000 + rep movsb +end if + +; mov esi, BOOT_VAR ; restore 0x0 - 0xffff +; mov edi, OS_BASE +; mov ecx, 0x10000/4 +; cld +; rep movsd + + call IRQ_mask_all + +if 0 + mov word [OS_BASE+0x467+0], pr_mode_exit + mov word [OS_BASE+0x467+2], 0x1000 + + mov al, 0x0F + out 0x70, al + mov al, 0x05 + out 0x71, al + + mov al, 0xFE + out 0x64, al + + hlt + jmp $-1 + +else + cmp byte [OS_BASE + 0x9030], 2 + jnz no_acpi_power_off + +; scan for RSDP +; 1) The first 1 Kb of the Extended BIOS Data Area (EBDA). + movzx eax, word [OS_BASE + 0x40E] + shl eax, 4 + jz @f + mov ecx, 1024/16 + call scan_rsdp + jnc .rsdp_found +@@: +; 2) The BIOS read-only memory space between 0E0000h and 0FFFFFh. + mov eax, 0xE0000 + mov ecx, 0x2000 + call scan_rsdp + jc no_acpi_power_off +.rsdp_found: + mov esi, [eax+16] ; esi contains physical address of the RSDT + mov ebp, [ipc_tmp] + stdcall map_page, ebp, esi, PG_MAP + lea eax, [esi+1000h] + lea edx, [ebp+1000h] + stdcall map_page, edx, eax, PG_MAP + and esi, 0xFFF + add esi, ebp + cmp dword [esi], 'RSDT' + jnz no_acpi_power_off + mov ecx, [esi+4] + sub ecx, 24h + jbe no_acpi_power_off + shr ecx, 2 + add esi, 24h +.scan_fadt: + lodsd + mov ebx, eax + lea eax, [ebp+2000h] + stdcall map_page, eax, ebx, PG_MAP + lea eax, [ebp+3000h] + add ebx, 0x1000 + stdcall map_page, eax, ebx, PG_MAP + and ebx, 0xFFF + lea ebx, [ebx+ebp+2000h] + cmp dword [ebx], 'FACP' + jz .fadt_found + loop .scan_fadt + jmp no_acpi_power_off +.fadt_found: +; ebx is linear address of FADT + mov edi, [ebx+40] ; physical address of the DSDT + lea eax, [ebp+4000h] + stdcall map_page, eax, edi, PG_MAP + lea eax, [ebp+5000h] + lea esi, [edi+0x1000] + stdcall map_page, eax, esi, PG_MAP + and esi, 0xFFF + sub edi, esi + cmp dword [esi+ebp+4000h], 'DSDT' + jnz no_acpi_power_off + mov eax, [esi+ebp+4004h] ; DSDT length + sub eax, 36+4 + jbe no_acpi_power_off + add esi, 36 +.scan_dsdt: + cmp dword [esi+ebp+4000h], '_S5_' + jnz .scan_dsdt_cont + cmp byte [esi+ebp+4000h+4], 12h ; DefPackage opcode + jnz .scan_dsdt_cont + mov dl, [esi+ebp+4000h+6] + cmp dl, 4 ; _S5_ package must contain 4 bytes + ; ...in theory; in practice, VirtualBox has 2 bytes + ja .scan_dsdt_cont + cmp dl, 1 + jb .scan_dsdt_cont + lea esi, [esi+ebp+4000h+7] + xor ecx, ecx + cmp byte [esi], 0 ; 0 means zero byte, 0Ah xx means byte xx + jz @f + cmp byte [esi], 0xA + jnz no_acpi_power_off + inc esi + mov cl, [esi] +@@: + inc esi + cmp dl, 2 + jb @f + cmp byte [esi], 0 + jz @f + cmp byte [esi], 0xA + jnz no_acpi_power_off + inc esi + mov ch, [esi] +@@: + jmp do_acpi_power_off +.scan_dsdt_cont: + inc esi + cmp esi, 0x1000 + jb @f + sub esi, 0x1000 + add edi, 0x1000 + push eax + lea eax, [ebp+4000h] + stdcall map_page, eax, edi, PG_MAP + push PG_MAP + lea eax, [edi+1000h] + push eax + lea eax, [ebp+5000h] + push eax + stdcall map_page + pop eax +@@: + dec eax + jnz .scan_dsdt + jmp no_acpi_power_off +do_acpi_power_off: + mov edx, [ebx+48] + test edx, edx + jz .nosmi + mov al, [ebx+52] + out dx, al + mov edx, [ebx+64] +@@: + in ax, dx + test al, 1 + jz @b +.nosmi: + and cx, 0x0707 + shl cx, 2 + or cx, 0x2020 + mov edx, [ebx+64] + in ax, dx + and ax, 203h + or ah, cl + out dx, ax + mov edx, [ebx+68] + test edx, edx + jz @f + in ax, dx + and ax, 203h + or ah, ch + out dx, ax +@@: + jmp $ + + +no_acpi_power_off: + mov word [OS_BASE+0x467+0], pr_mode_exit + mov word [OS_BASE+0x467+2], 0x1000 + + mov al, 0x0F + out 0x70, al + mov al, 0x05 + out 0x71, al + + mov al, 0xFE + out 0x64, al + + hlt + jmp $-1 + +scan_rsdp: + add eax, OS_BASE +.s: + cmp dword [eax], 'RSD ' + jnz .n + cmp dword [eax+4], 'PTR ' + jnz .n + xor edx, edx + xor esi, esi +@@: + add dl, [eax+esi] + inc esi + cmp esi, 20 + jnz @b + test dl, dl + jz .ok +.n: + add eax, 10h + loop .s + stc +.ok: + ret +end if + +if ~ lang eq sp +diff16 "end of .text segment",0,$ +end if + +include "data32.inc" + +__REV__ = __REV + +if ~ lang eq sp +diff16 "end of kernel code",0,$ +end if diff --git a/kernel/branches/kolibri-process/kernel.mnt b/kernel/branches/kolibri-process/kernel.mnt new file mode 100644 index 000000000..b2d9ce66a Binary files /dev/null and b/kernel/branches/kolibri-process/kernel.mnt differ diff --git a/kernel/branches/kolibri-process/kernel32.inc b/kernel/branches/kolibri-process/kernel32.inc new file mode 100644 index 000000000..95d29b903 --- /dev/null +++ b/kernel/branches/kolibri-process/kernel32.inc @@ -0,0 +1,285 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; KERNEL32.INC ;; +;; ;; +;; Included 32 bit kernel files for MenuetOS ;; +;; ;; +;; This file is kept separate as it will be easier to ;; +;; maintain and compile with an automated SETUP program ;; +;; in the future. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4420 $ + +struct POINT + x dd ? + y dd ? +ends + +struct RECT + left dd ? + top dd ? + right dd ? + bottom dd ? +ends + +struct BOX + left dd ? + top dd ? + width dd ? + height dd ? +ends + +struct DISPMODE + width dw ? + height dw ? + bpp dw ? + freq dw ? +ends + +; constants definition +WSTATE_NORMAL = 00000000b +WSTATE_MAXIMIZED = 00000001b +WSTATE_MINIMIZED = 00000010b +WSTATE_ROLLEDUP = 00000100b + +WSTATE_REDRAW = 00000001b +WSTATE_WNDDRAWN = 00000010b + +WSTYLE_HASCAPTION = 00010000b +WSTYLE_CLIENTRELATIVE = 00100000b + +struct TASKDATA + event_mask dd ? + pid dd ? + dw ? + state db ? + db ? + dw ? + wnd_number db ? + db ? + mem_start dd ? + counter_sum dd ? + counter_add dd ? + cpu_usage dd ? +ends + +TSTATE_RUNNING = 0 +TSTATE_RUN_SUSPENDED = 1 +TSTATE_WAIT_SUSPENDED = 2 +TSTATE_ZOMBIE = 3 +TSTATE_TERMINATING = 4 +TSTATE_WAITING = 5 +TSTATE_FREE = 9 + +; structures definition +struct WDATA + box BOX + cl_workarea dd ? + cl_titlebar dd ? + cl_frames dd ? + reserved db ? + fl_wstate db ? + fl_wdrawn db ? + fl_redraw db ? +ends + +label WDATA.fl_wstyle byte at WDATA.cl_workarea + 3 + +struct DBG_REGS + dr0 dd ? + dr1 dd ? + dr2 dd ? + dr3 dd ? + dr7 dd ? +ends + +struct PROC + list LHEAD ; + thr_list LHEAD ; + heap_lock MUTEX ; + heap_base rd 1 ; + heap_top rd 1 ; + mem_used rd 1 + pdt_0_phys rd 1 + pdt_1_phys rd 1 + io_map_0 rd 1 + io_map_1 rd 1 + + unused rb 4096-$ + pdt_0 rd 1024 +ends + +struct APPDATA + app_name rb 11 + rb 5 + + process dd ? ;+16 + fpu_state dd ? ;+20 + exc_handler dd ? ;+24 + except_mask dd ? ;+28 + pl0_stack dd ? ;+32 + heap_base dd ? ;+36 + heap_top dd ? ;+40 + cursor dd ? ;+44 + fd_ev dd ? ;+48 + bk_ev dd ? ;+52 + fd_obj dd ? ;+56 + bk_obj dd ? ;+60 + saved_esp dd ? ;+64 + io_map rd 2 ;+68 + dbg_state dd ? ;+76 + cur_dir dd ? ;+80 + wait_timeout dd ? ;+84 + saved_esp0 dd ? ;+88 + wait_begin dd ? ;+92 +++ + wait_test dd ? ;+96 +++ + wait_param dd ? ;+100 +++ + tls_base dd ? ;+104 + dlls_list_ptr dd ? ;+108 + event_filter dd ? ;+112 + 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 ;+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 + 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 + + +; Core functions +include "core/sync.inc" ; macros for synhronization objects +include "core/sys32.inc" ; process management +include "core/sched.inc" ; process scheduling +include "core/syscall.inc" ; system call +include "core/fpu.inc" ; all fpu/sse support +include "core/memory.inc" +include "core/heap.inc" ; kernel and app heap +include "core/malloc.inc" ; small kernel heap +include "core/taskman.inc" +include "core/dll.inc" +include "core/peload.inc" ; +include "core/exports.inc" +include "core/string.inc" +include "core/v86.inc" ; virtual-8086 manager +include "core/irq.inc" ; irq handling functions +include "core/apic.inc" ; Interrupt Controller functions +include "core/timers.inc" +include "core/clipboard.inc" ; custom clipboard + +; GUI stuff +include "gui/window.inc" +include "gui/event.inc" +include "gui/font.inc" +include "gui/button.inc" + +; shutdown + +; file system + +include "blkdev/disk.inc" ; support for plug-n-play disks +include "blkdev/disk_cache.inc" ; caching for plug-n-play disks +include "blkdev/rd.inc" ; ramdisk read /write +include "fs/fat.inc" ; read / write for fat filesystem +include "fs/ntfs.inc" ; read / write for ntfs filesystem +include "fs/fs_lfn.inc" ; syscall, version 2 +include "fs/iso9660.inc" ; read for iso9660 filesystem CD +include "fs/ext2/ext2.asm" ; read / write for ext2 filesystem +include "fs/xfs.asm" ; read / write for xfs filesystem + +; sound + +include "sound/playnote.inc" ; player Note for Speaker PC + +; display + +;include "video/vesa12.inc" ; Vesa 1.2 functions +include "video/vesa20.inc" ; Vesa 2.0 functions +include "video/blitter.inc" ; +include "video/vga.inc" ; VGA 16 color functions +include "video/cursors.inc" ; cursors functions + +; Network Interface & TCPIP Stack + +include "network/stack.inc" + +;include "drivers/uart.inc" + + +; Mouse pointer + +include "gui/mouse.inc" + +; Window skinning + +include "gui/skincode.inc" + +; Pci functions + +include "bus/pci/pci32.inc" + +; USB functions +include "bus/usb/init.inc" + +; Floppy drive controller + +include "blkdev/fdc.inc" +include "blkdev/flp_drv.inc" + +; IDE cache +include "blkdev/ide_cache.inc" + +; HD drive controller +include "blkdev/hd_drv.inc" +; Access through BIOS +include "blkdev/bd_drv.inc" + +; CD drive controller + +include "blkdev/cdrom.inc" +include "blkdev/cd_drv.inc" + +; Character devices + +include "hid/keyboard.inc" +include "hid/mousedrv.inc" + +; setting date,time,clock and alarm-clock + +include "hid/set_dtc.inc" + +;% -include + +;parser file names +include "fs/parse_fn.inc" + +; work with conf lib +include "core/conf_lib.inc" + +; load external lib +include "core/ext_lib.inc" + +; list of external functions +include "imports.inc" diff --git a/kernel/branches/kolibri-process/kernelsp.inc b/kernel/branches/kolibri-process/kernelsp.inc new file mode 100644 index 000000000..661889dbb --- /dev/null +++ b/kernel/branches/kolibri-process/kernelsp.inc @@ -0,0 +1,4 @@ +; Éste archivo debe ser editado con codificación CP866 + +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-process/kglobals.inc b/kernel/branches/kolibri-process/kglobals.inc new file mode 100644 index 000000000..9942a3f90 --- /dev/null +++ b/kernel/branches/kolibri-process/kglobals.inc @@ -0,0 +1,69 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +;------------------------------------------------------------------ +; use "iglobal" for inserting initialized global data definitions. +;------------------------------------------------------------------ +macro iglobal { + IGlobals equ IGlobals, + macro __IGlobalBlock { } + +macro iglobal_nested { + IGlobals equ IGlobals, + macro __IGlobalBlock \{ } + +;------------------------------------------------------------- +; use 'uglobal' for inserting uninitialized global definitions. +; even when you define some data values, these variables +; will be stored as uninitialized data. +;------------------------------------------------------------- +macro uglobal { + UGlobals equ UGlobals, + macro __UGlobalBlock { } + +macro uglobal_nested { + UGlobals equ UGlobals, + macro __UGlobalBlock \{ } + +endg fix } ; Use endg for ending iglobal and uglobal blocks. +endg_nested fix \} + +macro IncludeIGlobals{ + macro IGlobals dummy,[n] \{ __IGlobalBlock + purge __IGlobalBlock \} + match I, IGlobals \{ I \} } + + +macro IncludeUGlobals{ + macro UGlobals dummy,[n] \{ + \common + \local begin, size + begin = $ + virtual at $ + \forward + __UGlobalBlock + purge __UGlobalBlock + \common + size = $ - begin + end virtual + rb size + \} + match U, UGlobals \{ U \} } + +macro IncludeAllGlobals { + IncludeIGlobals + IncludeUGlobals +} + +iglobal +endg + +uglobal +endg diff --git a/kernel/branches/kolibri-process/lang.inc b/kernel/branches/kolibri-process/lang.inc new file mode 100644 index 000000000..6ae37e104 --- /dev/null +++ b/kernel/branches/kolibri-process/lang.inc @@ -0,0 +1 @@ +lang fix ru diff --git a/kernel/branches/kolibri-process/macros.inc b/kernel/branches/kolibri-process/macros.inc new file mode 100644 index 000000000..8a79935d9 --- /dev/null +++ b/kernel/branches/kolibri-process/macros.inc @@ -0,0 +1,138 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +__REV = 0 + +macro $Revision a { + match =: Num =$,a \{ + if __REV < Num + __REV = Num + end if + \} +} + +$Revision: 3598 $ + + +;// mike.dld, 2006-29-01 [ + +; macros definition +macro diff16 title,l1,l2 +{ + local s,d + s = l2-l1 + display title,': 0x' + repeat 16 + d = 48 + s shr ((16-%) shl 2) and $0F + if d > 57 + d = d + 65-57-1 + end if + display d + end repeat + display 13,10 +} +macro diff10 title,l1,l2 + { + local s,d,z,m + s = l2-l1 + z = 0 + m = 1000000000 + display title,': ' + repeat 10 + d = '0' + s / m + s = s - (s/m)*m + m = m / 10 + if d <> '0' + z = 1 + end if + if z <> 0 + display d + end if + end repeat + display 13,10 + } + +include 'kglobals.inc' + +; \begin{diamond}[29.09.2006] +; may be useful for kernel debugging +; example 1: +; dbgstr 'Hello, World!' +; example 2: +; dbgstr 'Hello, World!', save_flags +macro dbgstr string*, f +{ +local a +iglobal_nested +a db 'K : ',string,13,10,0 +endg_nested +if ~ f eq + pushfd +end if + push esi + mov esi, a + call sys_msg_board_str + pop esi +if ~ f eq + popfd +end if +} +; \end{diamond}[29.09.2006] + +macro Mov op1,op2,op3 ; op1 = op2 = op3 + { + mov op2, op3 + mov op1, op2 + } + +macro list_init head +{ + mov [head+LHEAD.next], head + mov [head+LHEAD.prev], head +} + +macro __list_add new, prev, next +{ + mov [next+LHEAD.prev], new + mov [new+LHEAD.next], next + mov [new+LHEAD.prev], prev + mov [prev+LHEAD.next], new +} + +macro list_add new, head +{ + mov eax, [head+LHEAD.next] + __list_add new, head, eax +} + +macro list_add_tail new, head +{ + mov eax, [head+LHEAD.prev] + __list_add new, eax, head +} + +macro list_del entry +{ + mov edx, [entry+list_fd] + mov ecx, [entry+list_bk] + mov [edx+list_bk], ecx + mov [ecx+list_fd], edx +} + +; MOV Immediate. +; Useful for things like movi eax,10: +; shorter than regular mov, but slightly slower, +; do not use it in performance-critical places. +macro movi dst, imm +{ +if imm >= -0x80 & imm <= 0x7F + push imm + pop dst +else + mov dst, imm +end if +} diff --git a/kernel/branches/kolibri-process/makefile b/kernel/branches/kolibri-process/makefile new file mode 100644 index 000000000..88cccdb0d --- /dev/null +++ b/kernel/branches/kolibri-process/makefile @@ -0,0 +1,47 @@ +FASM=fasm +FLAGS=-m 65536 +languages=en|ru|ge|et|sp +drivers_src=com_mouse emu10k1x fm801 infinity sis sound vt823x + +.PHONY: all kernel drivers bootloader clean + +all: kernel drivers bootloader + +kernel: check_lang + @echo "*** building kernel with language '$(lang)' ..." + @mkdir -p bin + @echo "lang fix $(lang)" > lang.inc + @echo "--- building 'bin/kernel.mnt' ..." + @$(FASM) $(FLAGS) kernel.asm bin/kernel.mnt + @rm -f lang.inc + +drivers: + @echo "*** building drivers ..." + @mkdir -p bin/drivers + @cd drivers; for f in $(drivers_src); do \ + echo "--- building 'bin/drivers/$${f}.obj' ..."; \ + $(FASM) $(FLAGS) "$${f}.asm" "../bin/drivers/$${f}.obj" || exit $?; \ + done + +bootloader: check_lang + @echo "*** building bootloader with language '$(lang)' ..." + @mkdir -p bin + @echo "lang fix $(lang)" > lang.inc + @echo "--- building 'bin/boot_fat12.bin' ..." + @$(FASM) $(FLAGS) bootloader/boot_fat12.asm bin/boot_fat12.bin + @rm -f lang.inc + + +check_lang: + @case "$(lang)" in \ + $(languages)) \ + ;; \ + *) \ + echo "*** error: language is incorrect or not specified"; \ + exit 1; \ + ;; \ + esac + +clean: + rm -rf bin + rm -f lang.inc diff --git a/kernel/branches/kolibri-process/memmap.inc b/kernel/branches/kolibri-process/memmap.inc new file mode 100644 index 000000000..921c78dc8 --- /dev/null +++ b/kernel/branches/kolibri-process/memmap.inc @@ -0,0 +1,279 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; MEMORY MAP +; +; Boot: +; +; 0:9000 byte bits per pixel +; 0:9001 word scanline length +; 0:9008 word vesa video mode +; 0:900A word X res +; 0:900C word Y res +; 0:9010 byte mouse port - not used +; 0:9014 dword Vesa 1.2 pm bank switch +; 0:9018 dword Vesa 2.0 LFB address +; 0:901C byte 0 or 1 : enable MTRR graphics acceleration +; 0:901D byte not used anymore (0 or 1 : enable system log display) +; 0:901E byte 0 or 1 : enable direct lfb write, paging disabled +; 0:901F byte DMA write : 1=yes, 2=no +; 0:9020 8bytes pci data +; 0:9030 byte VRR start enabled 1, 2-no +; 0:9031 word IDEContrRegsBaseAddr +; 0x9040 - dword - entry point of APM BIOS +; 0x9044 - word - version (BCD) +; 0x9046 - word - flags +; 0:907F byte number of BIOS hard disks +; 0:9080 Nbytes BIOS hard disks +; 0:9100 word available physical memory map: number of blocks +; 0:9104 available physical memory map: blocks +; +; Runtime: +; +; 0x00000000 -> 0x7FFFFFFF application 2Gb + +; 0x80000000 -> 0FFF physical page zero - do not write +; (used by int 13h in some configurations) +; +; 0x80001000 -> 2FFF window_data - 256 entries +; +; 0000 dword x start +; 0004 dword y start +; 0008 dword x size +; 000C dword y size +; 0010 dword color of work area +; 0014 dword color of grab bar +; 0018 dword color of frames +; 001C dword window flags, +30 = window drawn, +31 redraw flag +; +; 3000 -> 4FFF task list - 256 entries +; +; 00 dword process count +; 04 dword no of processes +; 10 dword base of running process at 0x3000+ +; +; 20 dword application event mask +; 24 dword PID - process identification number +; 2a byte slot state: 0=running, 1,2=suspended +; 3=zombie, 4=terminate, +; 5=waiting for event, 9 = not used +; 2e byte window number on screen +; 30 dword exact position in memory +; 34 dword counter sum +; 38 dword time stamp counter add +; 3c dword cpu usage in cpu timer tics +; +; +; 5000 -> 68FF free (6k6) +; 6900 -> 6EFF saved picture under mouse pointer (1k5) +; +; 6F00 -> 6FFF free (256) +; +; 7000 -> 7FFF used CD driver +; +; 8000 -> A3FF used FLOPPY driver +; +; A400 -> B0FF free (3k3), unused ACTIVE_PROC_STACK + +; B100 -> B307 IDT for int_0x00..int_0x40 + +; B308 -> BFFF free (3k3) + +; C000 -> C3FF window stack C000 no of windows - all in words +; C402 -> C7FF window position in stack +; D000 -> D1FF FDC controller +; D200 -> D3FF FDC controller for Fat12 +; D400 -> DFFF free (3k) +; E000 byte multitasking started +; E020 dword putpixel address +; E024 dword getpixel address +; E030 dword Vesa 1.2 pm bank switch address +; E034 -> F1FF free (4k5) +; F200 dword mousepicture -pointer +; F204 dword mouse appearance counter +; F208 -> F2FF free (248) +; F300 dword x & y temp for windowmove +; F304 -> F3FF free (252) +; F400 byte no of keys in buffer +; F401 byte 'buffer' +; F402 -> F4FF reserved for keys +; F500 byte no of buttons in buffer +; F501 dword 'buffer' +; F502 -> F5FF reserved for buttons +; F600 dword tsc / second +; F604 byte (unused?) mouse port: 1 ps2, 2 com1, 3 com2 +; F605 -> FAFF free (1k2) +; FB00 -> FB0F mouse memory 00 chunk count, that includes: +; FB08 word -- mouse H-scroll +; FB0A word -- mouse x +; FB0C word -- mouse y +; FB0E word -- mouse V-scroll +; FB10 -> FB17 mouse color mem +; FB21 x move +; FB22 y move +; FB28 high bits temp +; FB30 color temp +; FB40 byte buttons down +; FB44 byte 0 mouse down -> do not draw +; FB4A -> FB4D FB4A-B x-under - FB4C-D y-under +; FBF1 byte bits per pixel +; FC00 -> FCFE com1/ps2 buffer +; FCFF com1/ps2 buffer count starting from FC00 +; FD00 -> FDFF free (256) +; FE00 dword screen x size +; FE04 dword screen y size +; FE08 dword screen y multiplier +; FE0C dword screen mode +; FE10 -> FE7F free (112) +; FE80 dword address of LFB in physical +; FE84 dword address of applications memory start in physical ? +; FE88 dword address of button list +; FE8C dword memory to use +; FE90 -> FEFF free (112) +; FF00 byte 1 = system shutdown request +; FF01 byte task activation request? +; FFF0 byte >0 if redraw background request from app +; FFF1 byte free +; FFF2 write and read bank in screen +; FFF4 byte 0 if first mouse draw & do not return picture under +; FFF5 byte 1 do not draw pointer +; FFFF byte do not change task for 1/100 sec. +; +; 0x80010000 -> 6CBFF kernel, 32-bit run-time code (up to 371 Kb) + +; 0x8006CC00 -> 6DBFF stack at boot time (4Kb) +; +; 0x8006DC00 -> 6E5FF free (2560) +; 0x8006E600 -> 6Efff free (2560) +; 0x8006F000 -> 6FFFF main page directory + +; 0x80070000 -> 7FFFF data of retrieved disks and partitions (Mario79) +; 0x80080000 -> 8FFFF additional app info, in 256 byte steps - 256 entries +; +; 00 11db name of app running +; 0x10 dword pointer to fpu save area +; 0x14 dword event count +; 0x18 dword user fpu exceptoins handler +; 0x1c dword user sse exceptions handler +; 20 dword PL0 stack base +; 24 dword user heap base +; 28 dword user heap top +; 2c dword window cursor handle +; 30 dword first event in list +; 34 dword last event in list +; 38 dword first kernel object in list +; 3c dword last kernel object in list +; 40 dword thread esp +; 44 dword io permission map page 0 +; 48 dword io permission map page 1 +; 4c dword debug state: 1= load debug registers +; 50 dword current directory ptr +; 54 dword wait timeout +; 58 dword thread TSS._esp0 (= pl0 stack base + size except for V86) +; 5C-7F unused +; +; 80 dword address of random shaped window area +; 84 byte shape area scale +; 88 dword free +; 8C dword application memory size +; 90 dword window X position save +; 94 dword window Y position save +; 98 dword window X size save +; 9C dword window Y size save +; A0 dword IPC memory start +; A4 dword IPC memory size +; A8 dword event bits: mouse, stack,.. +; AC dword 0 or debugger slot +; B0 dword free +; B4 byte keyboard mode: 0 = keymap, 1 = scancodes +; B8 dword physical address of directory table +; BC dword address of debug event memory +; C0 5 dd thread debug registers: DR0,DR1,DR2,DR3,DR7 +; +; 0x80090000 -> 9FFFF tmp (64k) - unused? +; 0x800A0000 -> AFFFF screen access area +; 0x800B0000 -> FFFFF bios rest in peace -area (320k) ? +; 0x80100000 -> 27FFFF diskette image (1m5) +; 0x80280000 -> 283FFF free (16k) +; +; 0x80284000 -> 28BFFF HDD DMA AREA (32k) +; 0x8028C000 -> 297FFF free (48k) +; +; 0x80298000 -> 29FFFF auxiliary table for background smoothing code (32k) +; +; 0x802A0000 -> 2B00FF wav device buffer (64k) +; 0x802A0000 -> 2B00FF wav device status (256) +; +; 0x802B0100 -> 2B3FFD free (15k7) +; +; 0x802B3FEE -> 2B3FEF button info (64K+ 16 + 2 byte) +; 2B3FEE 0000 word number of buttons +; 2B3FF0 first button entry +; +; button entry at 0x10 +; +0000 word process number +; +0002 word button id number : bits 00-15 +; +0004 word x start +; +0006 word x size +; +0008 word y start +; +000A word y size +; +000C word button id number : bits 16-31 +; +; 0x802C4000 -> 2C9FFF area for fast getting offset to LFB (24k) +; BPSLine_calc_area +; 0x802CA000 -> 2CFFFF area for fast getting offset to _WinMapAddress (24k) +; d_width_calc_area +; +; 0x802D0000 -> 2DFFFF reserved port area (64k) +; +; 0000 dword no of port areas reserved +; 0010 dword process id +; dword start port +; dword end port +; dword 0 +; +; 0x802E0000 -> 2EFFFF irq data area (64k) ;BOOT_VAR +; +; 0x802F0000 -> 2F3FFF tcp memory stack_data_start eth_data_start (16k) +; +; 0x802F4000 -> 30ffff stack_data | stack_data_end (112k) +; +; 0x80310000 -> 317fff resendQ (32k) +; +; 0x80318000 -> 31ffff skin_data (32k) +; +; 0x80320000 -> 323FF3 draw data - 256 entries (4k) +; 00 dword draw limit - x start +; 04 dword draw limit - y start +; 08 dword draw limit - x end +; 0C dword draw limit - y end +; +; 0x8032BFF4 -> 32BFFF background info +; 0x80323FF4 BgrDrawMode +; 0x80323FF8 BgrDataWidth +; 0x80323FFC BgrDataHeight +; +; 0x80324000 page map (length b = memsize shr 15) +; 0x80324000 + b start of static pagetables + +; 0x803FFFFF <- no direct address translation beyond this point +; ============================================================= + +; 0x805FF000 -> 5FFF80 TSS +; 0x80600000 -> 601FFF i/o maps + +; 0x80800000 -> kernel heap +; 0x80FFFFFF heap min limit +; 0xFDBFFFFF heap max limit + +; 0xF0000000 -> 0xF1FFFFFF PCI-express extended config space +; 0xFDC00000 -> 0xFDFFFFFF page tables 4Mb +; 0xFE000000 -> 0xFFFFFFFF LFB 32Mb +; 0xFE000000 -> 0xFE7FFFFF application available LFB 8Mb +; 0xFE800000 -> 0xFFFFFFFF kernel LFB part 24 Mb + + diff --git a/kernel/branches/kolibri-process/network/ARP.inc b/kernel/branches/kolibri-process/network/ARP.inc new file mode 100644 index 000000000..2fb07730e --- /dev/null +++ b/kernel/branches/kolibri-process/network/ARP.inc @@ -0,0 +1,676 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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 + +uglobal +align 4 + + ARP_table rb NET_DEVICES_MAX*(ARP_TABLE_SIZE * sizeof.ARP_entry) + + ARP_entries_num rd NET_DEVICES_MAX + ARP_PACKETS_TX rd NET_DEVICES_MAX + ARP_PACKETS_RX rd NET_DEVICES_MAX + ARP_CONFLICTS rd NET_DEVICES_MAX + + +endg + + + +;----------------------------------------------------------------- +; +; ARP_init +; +; This function resets all ARP variables +; +;----------------------------------------------------------------- +macro ARP_init { + + xor eax, eax + mov edi, ARP_entries_num + mov ecx, 4*NET_DEVICES_MAX + 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 + + xor edi, edi + .loop_outer: + mov ecx, [ARP_entries_num + 4*edi] + test ecx, ecx + jz .exit + + mov esi, (ARP_TABLE_SIZE * sizeof.ARP_entry) + imul esi, edi + add 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 edi ecx + call ARP_del_entry + pop ecx edi esi + + jmp .next + + .response_timeout: + mov [esi + ARP_entry.Status], ARP_RESPONSE_TIMEOUT + mov [esi + ARP_entry.TTL], 10 + + jmp .next + + .exit: + inc edi + cmp edi, NET_DEVICES_MAX + jb .loop_outer + +} + + +;----------------------------------------------------------------- +; +; 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_num4 + cmp edi, -1 + jz .exit + + inc [ARP_PACKETS_RX + edi] ; update stats + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: got packet from %u.%u.%u.%u (device*4=%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 + edi] + je .collision + +;--------------------- +; Handle reply packets + + cmp [edx + ARP_header.Opcode], ARP_REP_OPCODE + jne .maybe_request + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: It's a reply\n" + + mov ecx, [ARP_entries_num + edi] + test ecx, ecx + jz .exit + + mov esi, edi + imul esi, (ARP_TABLE_SIZE * sizeof.ARP_entry)/4 + add esi, ARP_table + .loop: + cmp [esi + ARP_entry.IP], eax + je .gotit + add esi, sizeof.ARP_entry + dec ecx + jnz .loop + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: no matching entry found\n" + jmp .exit + + .gotit: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "ARP_input: its a request\n" + + mov eax, [IP_LIST + 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 + 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 DEBUG_NETWORK_VERBOSE, "ARP_input: Sending reply\n" + + call [ebx + NET_DEVICE.transmit] + ret + + .collision: + inc [ARP_CONFLICTS + edi] + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: IP address conflict detected!\n" + + .exit: + call NET_packet_free + add esp, 4 ; pop (balance stack) + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: exiting\n" + ret + + +;--------------------------------------------------------------------------- +; +; ARP_output_request +; +; IN: ebx = device ptr +; eax = IP +; OUT: / +; scratched: probably everything +; +;--------------------------------------------------------------------------- +align 4 +ARP_output_request: + + push eax + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\ + [esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx + + lea eax, [ebx + ETH_DEVICE.mac] ; local device mac + mov edx, ETH_BROADCAST ; broadcast mac + mov ecx, sizeof.ARP_header + mov di, ETHER_PROTO_ARP + call ETH_output + jz .exit + + 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 ; + + push edi + call NET_ptr_to_num4 + inc [ARP_PACKETS_TX + edi] ; assume we will succeed + lea esi, [IP_LIST + edi] ; SenderIP + pop edi + movsd + + mov esi, ETH_BROADCAST ; DestMac + movsw ; + movsd ; + popd [edi] ; DestIP + + push edx eax + call [ebx + NET_DEVICE.transmit] + ret + + .exit: + add esp, 4 + DEBUGF DEBUG_NETWORK_ERROR, "ARP_output_request: send failed\n" + ret + + +;----------------------------------------------------------------- +; +; ARP_add_entry (or update) +; +; IN: esi = ptr to entry (can easily be made on the stack) +; edi = device num*4 +; OUT: eax = entry #, -1 on error +; esi = ptr to newly created entry +; +;----------------------------------------------------------------- ; TODO: use a mutex +align 4 +ARP_add_entry: + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_add_entry: device=%u\n", edi + + mov ecx, [ARP_entries_num + edi] + cmp ecx, ARP_TABLE_SIZE ; list full ? + jae .full + +; From this point on, we can only fail if IP has a static entry, or if table is corrupt. + + inc [ARP_entries_num + edi] ; assume we will succeed + + push edi + xor ecx, ecx + imul edi, ARP_TABLE_SIZE*sizeof.ARP_entry/4 + add edi, ARP_table + mov eax, [esi + ARP_entry.IP] + .loop: + cmp [edi + ARP_entry.Status], ARP_NO_ENTRY ; is this slot empty? + je .add + + cmp [edi + ARP_entry.IP], eax ; 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 + + DEBUGF DEBUG_NETWORK_ERROR, "ARP_add_entry: failed, IP already has a static entry\n" + jmp .error + + .maybe_next: ; try the next slot + add edi, sizeof.ARP_entry + inc ecx + cmp ecx, ARP_TABLE_SIZE + jb .loop + + .add: + push ecx + mov ecx, sizeof.ARP_entry/2 + rep movsw + pop ecx + lea esi, [edi - sizeof.ARP_entry] + pop edi + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_add_entry: entry=%u\n", ecx + + ret + + .error: + pop edi + dec [ARP_entries_num + edi] + DEBUGF DEBUG_NETWORK_ERROR, "ARP_add_entry_failed\n" + .full: + mov eax, -1 + ret + + +;----------------------------------------------------------------- +; +; ARP_del_entry +; +; IN: esi = ptr to arp entry +; edi = device number +; OUT: / +; +;----------------------------------------------------------------- +align 4 +ARP_del_entry: + + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_del_entry: entry=%x entrys=%u\n", esi, [ARP_entries_num + 4*edi] + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + + push edi + imul edi, (ARP_TABLE_SIZE) * sizeof.ARP_entry + lea ecx, [ARP_table + (ARP_TABLE_SIZE - 1) * sizeof.ARP_entry + edi] + sub ecx, esi + shr ecx, 1 + +; move all trailing entries, sizeof.ARP_entry bytes to left. + mov edi, esi + add esi, sizeof.ARP_entry + rep movsw + +; now add an empty entry to the end (erasing previous one) + xor eax, eax + mov ecx, sizeof.ARP_entry/2 + rep stosw + + pop edi + dec [ARP_entries_num + 4*edi] + DEBUGF DEBUG_NETWORK_VERBOSE, "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 * 4 +; 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 DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: %u.%u", al, ah + rol eax, 16 + DEBUGF DEBUG_NETWORK_VERBOSE, ".%u.%u device*4: %u\n", al, ah, edi + rol eax, 16 + + cmp eax, 0xffffffff + je .broadcast + +;-------------------------------- +; Try to find the IP in ARP_table + + mov ecx, [ARP_entries_num + edi] + test ecx, ecx + jz .not_in_list + mov esi, edi + imul esi, (sizeof.ARP_entry * ARP_TABLE_SIZE)/4 + add esi, ARP_table + ARP_entry.IP + .scan_loop: + cmp [esi], eax + je .found_it + add esi, sizeof.ARP_entry + dec ecx + jnz .scan_loop + + .not_in_list: + DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: preparing for ARP request\n" + + push eax edi ; save IP for ARP_output_request +; Now craft the ARP entry on the stack + pushw ARP_REQUEST_TTL ; TTL + pushw ARP_AWAITING_RESPONSE ; status + pushd 0 ; mac + pushw 0 + pushd eax ; ip + mov esi, esp + +; Add it to the list + call ARP_add_entry + +; Delete the temporary entry + add esp, sizeof.ARP_entry ; clear the entry from stack + +; If we could not add it to the list, give up + cmp eax, -1 ; did ARP_add_entry fail? + je .full + +;----------------------------------------------- +; At this point, we got an ARP entry in the list + +; Now send a request packet on the network + pop edi eax ; IP in eax, device number in ebx, for ARP_output_request + + push esi edi + mov ebx, [NET_DRV_LIST + edi] + call ARP_output_request + pop edi esi + .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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: table is full!\n" + add esp, 8 + .give_up: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 76 +; +; 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, [ARP_entries_num + eax] + ret + + .read: + cmp ecx, [ARP_entries_num + eax] + jae .error + shr eax, 2 + imul eax, sizeof.ARP_entry*ARP_TABLE_SIZE + add eax, ARP_table + ; edi = pointer to buffer + ; ecx = # entry + imul ecx, sizeof.ARP_entry + lea esi, [eax + ecx] + mov ecx, sizeof.ARP_entry/2 + rep movsw + + xor eax, eax + ret + + .write: + ; esi = pointer to buffer + mov edi, eax + call ARP_add_entry ; out: eax = entry number, -1 on error + ret + + .remove: + ; ecx = # entry + cmp ecx, [ARP_entries_num + eax] + jae .error + imul ecx, sizeof.ARP_entry + lea esi, [ARP_table + ecx] + mov edi, eax + shr edi, 2 + call ARP_del_entry + ret + + .send_announce: + mov ebx, [NET_DRV_LIST + eax] + mov eax, [IP_LIST + eax] + call ARP_output_request ; now send a gratuitous ARP + ret + diff --git a/kernel/branches/kolibri-process/network/IPv4.inc b/kernel/branches/kolibri-process/network/IPv4.inc new file mode 100644 index 000000000..b3c582486 --- /dev/null +++ b/kernel/branches/kolibri-process/network/IPv4.inc @@ -0,0 +1,1084 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. 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 $ + +IPv4_MAX_FRAGMENTS = 64 +IPv4_MAX_ROUTES = 64 + +IPv4_ROUTE_FLAG_UP = 1 shl 0 +IPv4_ROUTE_FLAG_GATEWAY = 1 shl 1 +IPv4_ROUTE_FLAG_HOST = 1 shl 2 +IPv4_ROUTE_FLAG_D = 1 shl 3 ; Route was created by a redirect +IPv4_ROUTE_FLAG_M = 1 shl 4 ; Route was modified by a redirect + +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 IPv4_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 IPv4_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 + +struct IPv4_ROUTE + + Destination dd ? + Gateway dd ? + Flags dd ? + Use dd ? + Interface dd ? + +ends + + +uglobal +align 4 + + IP_LIST rd NET_DEVICES_MAX + SUBNET_LIST rd NET_DEVICES_MAX + DNS_LIST rd NET_DEVICES_MAX + GATEWAY_LIST rd NET_DEVICES_MAX + BROADCAST_LIST rd NET_DEVICES_MAX + + IPv4_packets_tx rd NET_DEVICES_MAX + IPv4_packets_rx rd NET_DEVICES_MAX + IPv4_packets_dumped rd NET_DEVICES_MAX + + IPv4_FRAGMENT_LIST rb IPv4_MAX_FRAGMENTS * sizeof.IPv4_FRAGMENT_slot + + IPv4_ROUTES rd IPv4_MAX_ROUTES * sizeof.IPv4_ROUTE + +endg + + +;----------------------------------------------------------------- +; +; IPv4_init +; +; This function resets all IP variables +; +;----------------------------------------------------------------- +macro IPv4_init { + + xor eax, eax + mov edi, IP_LIST + mov ecx, 7*NET_DEVICES_MAX + (sizeof.IPv4_FRAGMENT_slot*IPv4_MAX_FRAGMENTS)/4 + rep stosd + +} + + +;----------------------------------------------------------------- +; +; Decrease TimeToLive of all fragment slots +; +;----------------------------------------------------------------- +macro IPv4_decrease_fragment_ttls { + +local .loop, .next + + mov esi, IPv4_FRAGMENT_LIST + mov ecx, IPv4_MAX_FRAGMENTS + .loop: + cmp [esi + IPv4_FRAGMENT_slot.ttl], 0 + je .next + dec [esi + IPv4_FRAGMENT_slot.ttl] + jz .died + .next: + add esi, sizeof.IPv4_FRAGMENT_slot + dec ecx + jnz .loop + jmp .done + + .died: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "IPv4_input: Checksum ok\n" + +;----------------------------------- +; Check if destination IP is correct + + call NET_ptr_to_num4 + + ; 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 DEBUG_NETWORK_VERBOSE, "IPv4_input: Destination address does not match!\n" + jmp .dump + +;------------------------ +; Now we can update stats + + .ip_ok: + inc [IPv4_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 DEBUG_NETWORK_VERBOSE, "IPv4_input: unknown protocol %u\n", al + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: dumping\n" + inc [IPv4_packets_dumped] ; FIXME: use correct interface + call NET_packet_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 DEBUG_NETWORK_VERBOSE, "IPv4_input: fragmented packet offset=%u id=%x ptr=0x%x\n", ax, [edx + IPv4_header.Identification]:4, edx + + 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 DEBUG_NETWORK_VERBOSE, "IPv4_input: Middle fragment packet received!\n" + + call IPv4_find_fragment_slot + cmp esi, -1 + je .dump + + mov [esi + IPv4_FRAGMENT_slot.ttl], 15 ; Reset the ttl + mov esi, [esi + IPv4_FRAGMENT_slot.ptr] + or edi, -1 + .find_last_entry: ; The following routine will try to find the last entry + cmp edi, [esi + IPv4_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 + IPv4_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 + IPv4_FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry + mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1 + mov [eax + IPv4_FRAGMENT_entry.PrevPtr], edi + mov [eax + IPv4_FRAGMENT_entry.Owner], ebx + + add esp, 4 + ret + + +;------------------------------------ +; We have received the first fragment + + .is_first_fragment: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: First fragment packet received!\n" + ; try to locate a free slot.. + mov ecx, IPv4_MAX_FRAGMENTS + mov esi, IPv4_FRAGMENT_LIST + .find_free_slot: + cmp word [esi + IPv4_FRAGMENT_slot.ttl], 0 + je .found_free_slot + add esi, sizeof.IPv4_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 + IPv4_FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl + mov ax, [edx + IPv4_header.Identification] + mov [esi + IPv4_FRAGMENT_slot.id], ax + mov eax, [edx + IPv4_header.SourceAddress] + mov [esi + IPv4_FRAGMENT_slot.SrcIP], eax + mov eax, [edx + IPv4_header.DestinationAddress] + mov [esi + IPv4_FRAGMENT_slot.DstIP], eax + pop eax + mov [esi + IPv4_FRAGMENT_slot.ptr], eax + ; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure + mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1 + mov [eax + IPv4_FRAGMENT_entry.PrevPtr], -1 + mov [eax + IPv4_FRAGMENT_entry.Owner], ebx + + add esp, 4 ; balance stack and exit + ret + + +;----------------------------------- +; We have received the last fragment + + .is_last_fragment: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Last fragment packet received!\n" + + call IPv4_find_fragment_slot + cmp esi, -1 + je .dump + + mov esi, [esi + IPv4_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 + IPv4_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.IPv4_FRAGMENT_entry + IPv4_header.TotalLength] ; Add total length + xchg cl, ch + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx + add ax, cx + movzx cx, [esi + sizeof.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Sub Header length + and cx, 0x000F + shl cx, 2 + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Header size=%u\n", cx + sub ax, cx + mov edi, esi + mov esi, [esi + IPv4_FRAGMENT_entry.NextPtr] + cmp esi, -1 + jne .count_bytes + + mov esi, [esp+4] + mov [edi + IPv4_FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code + mov [esi + IPv4_FRAGMENT_entry.NextPtr], -1 + mov [esi + IPv4_FRAGMENT_entry.PrevPtr], edi + mov [esi + IPv4_FRAGMENT_entry.Owner], ebx + + mov cx, [edx + IPv4_header.TotalLength] ; Note: This time we dont substract Header length + xchg cl, ch + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx + add ax, cx + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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.IPv4_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 DEBUG_NETWORK_VERBOSE, "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.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Find header size (in ebx) of fragment + and bx, 0x000F ; + shl bx, 2 ; + + lea esi, [edx + sizeof.IPv4_FRAGMENT_entry] ; Set esi to the correct begin of fragment + movzx ecx, [edx + sizeof.IPv4_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: + + + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Copying %u bytes from 0x%x to 0x%x\n", ecx, esi, edi + 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 + IPv4_FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet + push [edx + IPv4_FRAGMENT_entry.NextPtr] ; Set edx to the next pointer + push edx ; Push pointer to fragment onto stack + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Next Fragment: 0x%x\n", edx + call NET_packet_free ; free the previous fragment buffer (this uses the value from stack) + pop edx ebx 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, 12 + xchg cl, ch + push ecx edx ; size and pointer + 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 DEBUG_NETWORK_VERBOSE, "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, IPv4_MAX_FRAGMENTS + mov esi, IPv4_FRAGMENT_LIST + mov ebx, [edx + IPv4_header.SourceAddress] + mov edx, [edx + IPv4_header.DestinationAddress] + .find_slot: + cmp [esi + IPv4_FRAGMENT_slot.id], ax + jne .try_next + cmp [esi + IPv4_FRAGMENT_slot.SrcIP], ebx + jne .try_next + cmp [esi + IPv4_FRAGMENT_slot.DstIP], edx + je .found_slot + .try_next: + add esi, sizeof.IPv4_FRAGMENT_slot + loop .find_slot + + or esi, -1 + .found_slot: + pop edx ecx ebx eax + ret + + +;------------------------------------------------------------------ +; +; IPv4_output +; +; IN: eax = Destination IP +; 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 DEBUG_NETWORK_VERBOSE, "IPv4_output: size=%u ip=0x%x\n", ecx, eax + + cmp ecx, 65500 ; Max IPv4 packet size + ja .too_large + + push ecx di eax + call IPv4_route ; outputs device number in edi, dest ip in eax, source IP in edx + push edx + + test edi, edi + jz .loopback + + call ARP_IP_to_MAC + test eax, 0xffff0000 ; error bits + jnz .arp_error + push ebx ; push the mac onto the stack + push ax + + inc [IPv4_packets_tx + edi] ; update stats + + mov ebx, [NET_DRV_LIST + edi] + lea eax, [ebx + ETH_DEVICE.mac] + mov edx, esp + mov ecx, [esp + 6 + 8 + 2] + add ecx, sizeof.IPv4_header + mov di, ETHER_PROTO_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 + + mov [edi + IPv4_header.HeaderChecksum], 0 + popd [edi + IPv4_header.SourceAddress] + popd [edi + IPv4_header.DestinationAddress] + + pop word[edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol +; [edi + IPv4_header.Protocol] + + pop ecx + + IPv4_checksum edi + add edi, sizeof.IPv4_header + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output: success!\n" + ret + + .eth_error: + DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ethernet error\n" + add esp, 3*4+2+6 + xor edi, edi + ret + + .arp_error: + DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ARP error=%x\n", eax + add esp, 3*4+2 + xor edi, edi + ret + + .too_large: + DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: Packet too large!\n" + xor edi, edi + ret + + .loopback: + mov dword [esp + 2], eax ; change source IP to dest IP + mov ecx, [esp + 10] + add ecx, sizeof.IPv4_header + mov edi, AF_INET4 + 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 [IPv4_packets_tx + 4*edi] + mov ebx, [NET_DRV_LIST + 4*edi] + lea eax, [ebx + ETH_DEVICE.mac] + mov edx, esp + mov ecx, [esp + 6 + 4] + add ecx, sizeof.IPv4_header + mov di, ETHER_PROTO_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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_ERROR, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "Ipv4_fragment: new fragment" + + + mov eax, [esp + 3*4] + lea ebx, [esp + 4*4] + mov di , ETHER_PROTO_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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_ERROR, "Ipv4_fragment: failed\n" + .done: + add esp, 12 + 4 + 6 + .err2: + DEBUGF DEBUG_NETWORK_VERBOSE, "Ipv4_fragment: dumping\n" + call NET_packet_free + add esp, 4 + + ret + + + +;--------------------------------------------------------------------------- +; +; IPv4_route +; +; IN: eax = Destination IP +; edx = Source IP +; OUT: eax = Destination IP (or gateway IP) +; edx = Source IP +; edi = device number*4 +; DESTROYED: +; ecx +; +;--------------------------------------------------------------------------- +align 4 +IPv4_route: ; TODO: return error if no valid route found + + cmp eax, 0xffffffff + je .broadcast + + xor edi, edi + .loop: + mov ebx, [IP_LIST + edi] + and ebx, [SUBNET_LIST + edi] + jz .next + mov ecx, eax + and ecx, [SUBNET_LIST + edi] + cmp ebx, ecx + je .got_it + .next: + add edi, 4 + cmp edi, 4*NET_DEVICES_MAX + jb .loop + + mov eax, [GATEWAY_LIST + 4] ; TODO: let user (or a user space daemon) configure default route + .broadcast: + mov edi, 4 ; TODO: same as above + .got_it: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi + test edx, edx + jnz @f + mov edx, [IP_LIST + 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_connect +; +; IN: eax = socket pointer +; OUT: eax = 0 ok / -1 error +; ebx = error code +; +;------------------------- +align 4 +IPv4_connect: + + push eax edx + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + pop edx eax + +; Fill in local IP + cmp [eax + IP_SOCKET.LocalIP], 0 + jne @f + push [IP_LIST + 4] ; FIXME: use correct local IP + pop [eax + IP_SOCKET.LocalIP] + +; Fill in remote IP + pushd [edx + 4] + pop [eax + IP_SOCKET.RemoteIP] + +; Set up data receiving queue + push eax + init_queue (eax + SOCKET_QUEUE_LOCATION) + pop eax + + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + + xor eax, eax + 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, [IPv4_packets_tx + eax] + ret + + .packets_rx: + mov eax, [IPv4_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 ebx, [NET_DRV_LIST + eax] + mov eax, [IP_LIST + eax] + 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-process/network/IPv6.inc b/kernel/branches/kolibri-process/network/IPv6.inc new file mode 100644 index 000000000..f058ec639 --- /dev/null +++ b/kernel/branches/kolibri-process/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 + + +uglobal +align 4 + + IPv6: + .addresses rd 4*NET_DEVICES_MAX + .subnet rd 4*NET_DEVICES_MAX + .dns rd 4*NET_DEVICES_MAX + .gateway rd 4*NET_DEVICES_MAX + + .packets_tx rd NET_DEVICES_MAX + .packets_rx rd NET_DEVICES_MAX + +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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "IPv6_input - unknown protocol: %u\n", al + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "IPv6_input - pad %u\n", eax + inc esi + inc esi + add esi, eax + sub ecx, eax + jmp .hop_by_hop + + .pad_1: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - pad 1\n" + inc esi + dec ecx + jmp .hop_by_hop + + + + .dest_opts: + DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - dest opts\n" + jmp .nextheader + + .routing: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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-process/network/PPPoE.inc b/kernel/branches/kolibri-process/network/PPPoE.inc new file mode 100644 index 000000000..c3f9e533e --- /dev/null +++ b/kernel/branches/kolibri-process/network/PPPoE.inc @@ -0,0 +1,358 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2012-2013. 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 +align 4 + + 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 DEBUG_NETWORK_VERBOSE, "PPPoE_discovery_input\n" + +; First, find open PPPoE socket + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + +; Now, send it to the this socket + + mov ecx, [esp + 4] + mov esi, [esp] + + jmp SOCKET_input + + .dump: + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, 'PPPoE_discovery_input: dumping\n' + call NET_packet_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 DEBUG_NETWORK_VERBOSE, "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, NET_DEVICES_MAX + ja .bad + + mov ebx, [NET_DRV_LIST + 4*ebx] + test ebx, ebx + jz .bad + + cmp [ebx + NET_DEVICE.device_type], NET_DEVICE_ETH + jne .bad + + DEBUGF DEBUG_NETWORK_VERBOSE, "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_PROTO_PPP_SESSION ; Allow only PPP_discovery, or LCP + je @f + mov ax, ETHER_PROTO_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 DEBUG_NETWORK_VERBOSE, "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_PROTO_IPv4 + je IPv4_input + +; cmp ax, PPP_PROTO_IPv6 +; je IPv6_input + + jmp PPPoE_discovery_input ; Send LCP,CHAP,CBCP,... packets to the PPP dialer + DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_input: Unknown protocol=%x\n", ax + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_input: dumping\n" + call NET_packet_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 DEBUG_NETWORK_VERBOSE, "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_PROTO_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 DEBUG_NETWORK_VERBOSE, "PPPoE_output: success!\n" + ret + + + .eth_error: + add esp, 4 + xor edi, edi + + ret + + +PPPoE_start_connection: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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-process/network/ethernet.inc b/kernel/branches/kolibri-process/network/ethernet.inc new file mode 100644 index 000000000..05fa7e648 --- /dev/null +++ b/kernel/branches/kolibri-process/network/ethernet.inc @@ -0,0 +1,284 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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 +ETH_QUEUE_SIZE = 255 + +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 + +struct ETH_queue_entry + + device dd ? + packet dd ? + size dd ? + +ends + +iglobal +align 4 + + ETH_BROADCAST dp 0xffffffffffff +endg + +uglobal +align 4 + ETH_input_event dd ? + ETH_queue rd (ETH_QUEUE_SIZE*sizeof.ETH_queue_entry + sizeof.queue)/4 +endg + +macro ETH_init { + + init_queue ETH_queue + + movi ebx, 1 + mov ecx, ETH_process_input + call new_sys_threads + test eax, eax + jns @f + DEBUGF DEBUG_NETWORK_ERROR,'K : cannot create kernel thread for ethernet, error %d\n', eax + @@: + +} + +;----------------------------------------------------------------- +; +; 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: + + push ebx + mov esi, esp + + pushf + cli + add_to_queue ETH_queue, ETH_QUEUE_SIZE, sizeof.ETH_queue_entry, .fail + popf + + add esp, sizeof.ETH_queue_entry + + xor edx, edx + mov eax, [ETH_input_event] + mov ebx, [eax + EVENT.id] + xor esi, esi + call raise_event + + ret + + .fail: + popf + DEBUGF DEBUG_NETWORK_VERBOSE, "ETH incoming queue is full, discarding packet!\n" + + add esp, sizeof.ETH_queue_entry - 8 + call NET_packet_free + add esp, 4 + + ret + + + + +align 4 +ETH_process_input: + + xor esi, esi + mov ecx, MANUAL_DESTROY + call create_event + mov [ETH_input_event], eax + + .wait: + mov eax, [ETH_input_event] + mov ebx, [eax + EVENT.id] + call wait_event + + .loop: + get_from_queue ETH_queue, ETH_QUEUE_SIZE, sizeof.ETH_queue_entry, .wait + + mov eax, [esi + ETH_queue_entry.packet] + mov ecx, [esi + ETH_queue_entry.size] + mov ebx, [esi + ETH_queue_entry.device] + + pushd .loop ; return address + push ecx eax + + DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: size=%u\n", ecx + sub ecx, sizeof.ETH_header + jb .dump + + lea edx, [eax + sizeof.ETH_header] + mov ax, [eax + ETH_header.Type] + + cmp ax, ETHER_PROTO_IPv4 + je IPv4_input + + cmp ax, ETHER_PROTO_ARP + je ARP_input + + cmp ax, ETHER_PROTO_IPv6 + je IPv6_input + + cmp ax, ETHER_PROTO_PPP_DISCOVERY + je PPPoE_discovery_input + + cmp ax, ETHER_PROTO_PPP_SESSION + je PPPoE_session_input + + DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: Unknown packet type=%x\n", ax + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: dumping\n" + call NET_packet_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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_ERROR, "ETH_output: Out of ram!\n" + add esp, 4+4+2+4 + sub edi, edi + ret + + .exit: + DEBUGF DEBUG_NETWORK_ERROR, "ETH_output: Packet too large!\n" + sub edi, edi + ret + + + +;----------------------------------------------------------------- +; +; ETH_API +; +; 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 +ETH_api: + + cmp bh, NET_DEVICES_MAX + ja .error + movzx eax, bh + mov eax, dword [NET_DRV_LIST + 4*eax] + cmp [eax + NET_DEVICE.device_type], NET_DEVICE_ETH + jne .error + + and ebx, 0xff + cmp ebx, .number + ja .error + jmp dword [.table + 4*ebx] + + .table: + dd .read_mac ; 0 + .number = ($ - .table) / 4 - 1 + + .error: + or eax, -1 + 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 + + diff --git a/kernel/branches/kolibri-process/network/icmp.inc b/kernel/branches/kolibri-process/network/icmp.inc new file mode 100644 index 000000000..ff7f3756b --- /dev/null +++ b/kernel/branches/kolibri-process/network/icmp.inc @@ -0,0 +1,469 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. 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: 2924 $ + +; 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 + + +uglobal +align 4 + + ICMP_PACKETS_TX rd NET_DEVICES_MAX + ICMP_PACKETS_RX rd NET_DEVICES_MAX + +endg + + + +;----------------------------------------------------------------- +; +; ICMP_init +; +;----------------------------------------------------------------- + +macro ICMP_init { + + xor eax, eax + mov edi, ICMP_PACKETS_TX + mov ecx, 2*NET_DEVICES_MAX + rep stosd + +} + + +;----------------------------------------------------------------- +; +; ICMP_input: +; +; This procedure will send reply's to ICMP echo's +; and insert packets into sockets when needed +; +; IN: Pointer to buffer in [esp] +; size of buffer in [esp+4] +; ebx = pointer to device struct +; ecx = ICMP Packet size +; esi = ptr to ICMP Packet data +; edi = ptr to ipv4 source and dest address +; +; OUT: / +; +;----------------------------------------------------------------- +align 4 +ICMP_input: + + DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input:\n" + +; First, check the checksum (altough some implementations ignore it) + + 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 + +; Check packet type + + cmp [edx + ICMP_header.Type], ICMP_ECHO ; Is this an echo request? + jne .check_sockets + +; Update stats (and validate device ptr) + call NET_ptr_to_num4 + cmp edi, -1 + je .dump + inc [ICMP_PACKETS_RX + edi] + +; We well re-use the packet so we can create the response as fast as possible +; Notice: this only works on pure ethernet + + DEBUGF DEBUG_NETWORK_VERBOSE, "got echo request\n" + mov [edx + ICMP_header.Type], ICMP_ECHOREPLY ; Change Packet type to reply + + mov esi, [esp] ; Start of buffer + cmp ebx, LOOPBACK_DEVICE + je .loopback + +; FIXME: dont assume device is an ethernet device! + +; 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-4 + + .loopback: + add esi, 4 + push [esi + IPv4_header.SourceAddress] + push [esi + IPv4_header.DestinationAddress] + pop [esi + IPv4_header.SourceAddress] + pop [esi + IPv4_header.DestinationAddress] + +; Recalculate ip header checksum + movzx ecx, [esi + IPv4_header.VersionAndIHL] ; Calculate IP Header length by using IHL field + and ecx, 0x0f + shl cx, 2 + mov edi, ecx ; IP header length + mov eax, edx ; ICMP packet start addr + + push esi ; Calculate the IP checksum + xor edx, edx ; + call checksum_1 ; + call checksum_2 ; + pop esi ; + mov [esi + IPv4_header.HeaderChecksum], dx ; + +; Recalculate ICMP CheckSum + movzx ecx, [esi + IPv4_header.TotalLength] ; Find length of IP Packet + xchg ch, cl ; + sub ecx, edi ; IP packet length - IP header length = ICMP packet length + + mov esi, eax ; Calculate ICMP checksum + xor edx, edx ; + call checksum_1 ; + call checksum_2 ; + mov [eax + ICMP_header.Checksum], dx ; + +; Transmit the packet (notice that packet ptr and packet size have been on stack since start of the procedure!) + call [ebx + NET_DEVICE.transmit] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [ICMP_PACKETS_TX + edi] + @@: + ret + + + + + .check_sockets: + ; Look for an open ICMP socket + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 + +; Update stats (and validate device ptr) + call NET_ptr_to_num4 + cmp edi, -1 + je .dump_ + inc [ICMP_PACKETS_RX + edi] + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "socket=%x\n", eax + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + popa + + mov esi, edx + jmp SOCKET_input + + .dump_: + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input: no socket found\n" + jmp .dump + + + .checksum_mismatch: + DEBUGF DEBUG_NETWORK_VERBOSE, "checksum mismatch\n" + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input: dumping\n" + + call NET_packet_free + add esp, 4 ; pop (balance stack) + + ret + + +if 0 +;----------------------------------------------------------------- +; +; ICMP_output +; +; IN: eax = dest ip +; bh = type +; bl = code +; ecx = data length +; edx = source ip +; esi = data offset +; edi = identifier shl 16 + sequence number +; +;----------------------------------------------------------------- +align 4 +ICMP_output: + + DEBUGF DEBUG_NETWORK_VERBOSE, "Creating ICMP Packet\n" + + push esi edi bx + add ecx, sizeof.ICMP_header + mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL + call IPv4_output + jz .exit + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "Sending ICMP Packet\n" + call [ebx + NET_DEVICE.transmit] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [ICMP_PACKETS_TX + edi] + @@: + ret + .exit: + DEBUGF DEBUG_NETWORK_ERROR, "Creating ICMP Packet failed\n" + add esp, 2*4 + 2 + ret +end if + + + + +;----------------------------------------------------------------- +; +; ICMP_output_raw +; +; IN: eax = socket ptr +; ecx = data length +; esi = data offset +; +;----------------------------------------------------------------- +align 4 +ICMP_output_raw: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "Sending ICMP Packet\n" + call [ebx + NET_DEVICE.transmit] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [ICMP_PACKETS_TX + edi] + @@: + ret + .exit: + DEBUGF DEBUG_NETWORK_ERROR, "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 diff --git a/kernel/branches/kolibri-process/network/loopback.inc b/kernel/branches/kolibri-process/network/loopback.inc new file mode 100644 index 000000000..72eeb76db --- /dev/null +++ b/kernel/branches/kolibri-process/network/loopback.inc @@ -0,0 +1,155 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. 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 +align 4 + +LOOPBACK_DEVICE: + + .device_type dd NET_DEVICE_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 + + .link_state dd -1 + .hwacc dd NET_HWACC_TCP_IPv4_IN + NET_HWACC_TCP_IPv4_OUT + + .namestr db 'loopback', 0 + + .dummy_fn: + ret + +endg + + +macro LOOP_init { +local .fail + + mov ebx, LOOPBACK_DEVICE + call NET_add_device + + cmp eax, -1 + je .fail + + mov [IP_LIST], 127 + 1 shl 24 + mov [SUBNET_LIST], 255 + mov [BROADCAST_LIST], 0xffffff00 + 127 + + .fail: +} + +;----------------------------------------------------------------- +; +; 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 + + inc [LOOPBACK_DEVICE.packets_rx] + add dword[LOOPBACK_DEVICE.bytes_rx], ecx + adc dword[LOOPBACK_DEVICE.bytes_rx + 4], 0 + + DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: size=%u\n", ecx + lea edx, [eax + 4] + mov eax, dword[eax] + mov ebx, LOOPBACK_DEVICE + + cmp eax, AF_INET4 + je IPv4_input + + DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: Unknown packet type=%x\n", ax + + .dump: + DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: dumping\n" + call NET_packet_free + add esp, 4 + ret + +;----------------------------------------------------------------- +; +; LOOP_output +; +; IN: +; ecx = packet size +; edi = address family +; +; 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 DEBUG_NETWORK_VERBOSE, "LOOP_output\n" + + push ecx + push edi + + add ecx, 4 + cmp ecx, [LOOPBACK_DEVICE.mtu] + ja .out_of_ram + stdcall kernel_alloc, ecx + test eax, eax + jz .out_of_ram + mov edi, eax + pop eax + stosd + + lea eax, [edi - 4] ; Set eax to buffer start + pop ecx + lea edx, [ecx + 4] ; Set edx to complete buffer size + mov ebx, LOOPBACK_DEVICE + + inc [LOOPBACK_DEVICE.packets_tx] + add dword[LOOPBACK_DEVICE.bytes_tx], ecx + adc dword[LOOPBACK_DEVICE.bytes_tx + 4], 0 + + DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_output: ptr=%x size=%u\n", eax, edx + ret + + .out_of_ram: + DEBUGF DEBUG_NETWORK_ERROR, "LOOP_output: out of memory\n" + add esp, 4+4 + xor edi, edi + ret + + diff --git a/kernel/branches/kolibri-process/network/queue.inc b/kernel/branches/kolibri-process/network/queue.inc new file mode 100644 index 000000000..9d7a557b5 --- /dev/null +++ b/kernel/branches/kolibri-process/network/queue.inc @@ -0,0 +1,141 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; queue.inc ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2305 $ + +; The Queues implemented by these macros form a ring-buffer. +; The data to these queue's always looks like this: +; +; 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) + + +struct queue + + size dd ? ; number of queued packets in this queue + w_ptr dd ? ; current writing pointer in queue + r_ptr dd ? ; current reading pointer + mutex MUTEX + +ends + +; The following macros share these inputs: + +; 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 + +; 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 { + +local .ok, .no_wrap + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_lock + popa + + cmp [ptr + queue.size], size ; Check if queue isnt full + jb .ok + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_unlock + popa + jmp failaddr + + .ok: + inc [ptr + queue.size] ; if not full, queue one more + + mov edi, [ptr + queue.w_ptr] ; Current write pointer (FIFO!) + mov ecx, entry_size/4 ; Write the queue entry + rep movsd ; + + lea ecx, [size*entry_size+ptr+sizeof.queue] + cmp edi, ecx ; entry size + jb .no_wrap + + sub edi, size*entry_size + .no_wrap: + mov [ptr + queue.w_ptr], edi + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_unlock + popa + +} + + + +macro get_from_queue ptr, size, entry_size, failaddr { + +local .ok, .no_wrap + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_lock + popa + + cmp [ptr + queue.size], 0 ; any packets queued? + ja .ok + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_unlock + popa + jmp failaddr + + .ok: + dec [ptr + queue.size] ; if so, dequeue one + + mov esi, [ptr + queue.r_ptr] + push esi + + add esi, entry_size + + lea ecx, [size*entry_size+ptr+sizeof.queue] + cmp esi, ecx ; entry size + jb .no_wrap + + sub esi, size*entry_size + + .no_wrap: + mov dword [ptr + queue.r_ptr], esi + + pop esi + + pusha + lea ecx, [ptr + queue.mutex] + call mutex_unlock + popa + +} + +macro init_queue ptr { + + mov [ptr + queue.size] , 0 + lea edi, [ptr + sizeof.queue] + mov [ptr + queue.w_ptr], edi + mov [ptr + queue.r_ptr], edi + + lea ecx, [ptr + queue.mutex] + call mutex_init +} \ No newline at end of file diff --git a/kernel/branches/kolibri-process/network/socket.inc b/kernel/branches/kolibri-process/network/socket.inc new file mode 100644 index 000000000..2aaa541fc --- /dev/null +++ b/kernel/branches/kolibri-process/network/socket.inc @@ -0,0 +1,2406 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: 3514 $ + +struct SOCKET + + 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/DGRAM + 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 ? + connect_proc dd ? + +ends + +struct IP_SOCKET SOCKET + + LocalIP rd 4 ; network byte order + RemoteIP rd 4 ; network byte order + +ends + +struct TCP_SOCKET IP_SOCKET + + LocalPort dw ? ; network byte order + RemotePort dw ? ; network byte order + + t_state dd ? ; TCB state + t_rxtshift db ? + rb 3 ; align + t_rxtcur dd ? + t_dupacks dd ? + t_maxseg dd ? + t_force dd ? + t_flags dd ? + +;--------------- +; RFC783 page 21 + +; send sequence + SND_UNA dd ? ; sequence number of unack'ed sent Packets + SND_NXT dd ? ; next send sequence number to use + SND_UP dd ? ; urgent pointer + SND_WL1 dd ? ; window minus one + SND_WL2 dd ? ; + ISS dd ? ; initial send sequence number + SND_WND dd ? ; send window + +; receive sequence + RCV_WND dd ? ; receive window + RCV_NXT dd ? ; next receive sequence number to use + RCV_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 ? ; congestion window + SND_SSTHRESH dd ? ; slow start threshold + +;---------------------- +; 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_flags dd ? + timer_retransmission dd ? ; rexmt + timer_persist dd ? + timer_keepalive dd ? ; keepalive/syn timeout + timer_timed_wait dd ? ; also used as 2msl timer + timer_connect dd ? + +; extra + + ts_ecr dd ? ; timestamp echo reply + ts_val dd ? + + seg_next dd ? ; re-assembly queue + +ends + +struct UDP_SOCKET IP_SOCKET + + LocalPort dw ? ; network byte order + RemotePort dw ? ; network byte order + +ends + + +struct ICMP_SOCKET IP_SOCKET + + Identifier dw ? + +ends + + +struct RING_BUFFER + + mutex MUTEX + start_ptr dd ? ; Pointer to start of buffer + end_ptr dd ? ; pointer to end of buffer + read_ptr dd ? ; Read pointer + write_ptr dd ? ; Write pointer + size dd ? ; Number of bytes buffered + +ends + +struct STREAM_SOCKET TCP_SOCKET + + rcv RING_BUFFER + snd RING_BUFFER + +ends + +struct socket_queue_entry + + data_ptr dd ? + buf_ptr dd ? + data_size dd ? + +ends + + +SOCKETBUFFSIZE = 4096 ; in bytes + +SOCKET_QUEUE_SIZE = 10 ; maximum number of incoming packets queued for 1 socket +; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start +SOCKET_QUEUE_LOCATION = (SOCKETBUFFSIZE - SOCKET_QUEUE_SIZE*sizeof.socket_queue_entry - sizeof.queue) + +uglobal +align 4 + + 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 ? ; + socket_mutex MUTEX + +endg + + +;----------------------------------------------------------------- +; +; SOCKET_init +; +;----------------------------------------------------------------- +macro SOCKET_init { + + xor eax, eax + mov edi, net_sockets + mov ecx, 5 + rep stosd + + @@: + pseudo_random eax + cmp ax, EPHEMERAL_PORT_MIN + jb @r + cmp ax, EPHEMERAL_PORT_MAX + ja @r + xchg al, ah + mov [last_UDP_port], ax + + @@: + pseudo_random eax + cmp ax, EPHEMERAL_PORT_MIN + jb @r + cmp ax, EPHEMERAL_PORT_MAX + ja @r + xchg al, ah + mov [last_TCP_port], ax + + mov ecx, socket_mutex + call mutex_init + +} + +;----------------------------------------------------------------- +; +; Socket API (function 74) +; +;----------------------------------------------------------------- +align 4 +sys_socket: + + mov dword[esp+20], 0 ; Set error code to 0 + + cmp ebx, 255 + jz SOCKET_debug + + cmp ebx, .number + ja .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 + + .error: + mov dword[esp+32], -1 + mov dword[esp+20], EINVAL + + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_open: domain=%u type=%u protocol=%x ", ecx, edx, esi + + push ecx edx esi + call SOCKET_alloc + pop esi edx ecx + jz .nobuffs + + mov [esp+32], edi ; return socketnumber + DEBUGF DEBUG_NETWORK_VERBOSE, "socknum=%u\n", edi + + test edx, SO_NONBLOCK + jz @f + or [eax + SOCKET.options], SO_NONBLOCK + and edx, not SO_NONBLOCK + @@: + + mov [eax + SOCKET.Domain], ecx + mov [eax + SOCKET.Type], edx + mov [eax + SOCKET.Protocol], esi + mov [eax + SOCKET.connect_proc], connect_notsupp + + 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: + .unsupported: + push eax + call SOCKET_free + pop eax + mov dword[esp+20], EOPNOTSUPP + mov dword[esp+32], -1 + ret + + .nobuffs: + mov dword[esp+20], ENOBUFS + mov dword[esp+32], -1 + ret + + .raw: + test esi, esi ; IP_PROTO_IP + jz .raw_ip + + cmp esi, IP_PROTO_ICMP + je .raw_icmp + + jmp .unsupported + +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 + mov [eax + SOCKET.connect_proc], UDP_connect + 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 + mov [eax + SOCKET.connect_proc], TCP_connect + + TCP_init_socket eax + ret + + +align 4 + .raw_ip: + mov [eax + SOCKET.snd_proc], SOCKET_send_ip + mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram + mov [eax + SOCKET.connect_proc], IPv4_connect + ret + + +align 4 + .raw_icmp: + mov [eax + SOCKET.snd_proc], SOCKET_send_icmp + mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram + mov [eax + SOCKET.connect_proc], IPv4_connect + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_bind: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi + + call SOCKET_num_to_ptr + jz .invalid + + cmp esi, 2 + jb .invalid + + cmp [eax + UDP_SOCKET.LocalPort], 0 ; Socket can only be bound once + jnz .invalid + + cmp word [edx], AF_INET4 + je .af_inet4 + + cmp word [edx], AF_LOCAL + je .af_local + + .notsupp: + mov dword[esp+20], EOPNOTSUPP + mov dword[esp+32], -1 + ret + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + ret + + .af_local: + ; TODO: write code here + mov dword[esp+32], 0 + ret + + .af_inet4: + cmp esi, 6 + jb .invalid + + cmp [eax + SOCKET.Protocol], IP_PROTO_UDP + je .udp + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + je .tcp + + jmp .notsupp + + .tcp: + .udp: + + pushd [edx + 4] ; First, fill in the IP + popd [eax + IP_SOCKET.LocalIP] + + mov bx, [edx + 2] ; Did caller specify a local port? + test bx, bx + jnz .just_check + call SOCKET_find_port ; Nope, find an ephemeral one + jmp .done + + .just_check: + call SOCKET_check_port ; Yes, check if it's still available + jz .addrinuse ; ZF is set by socket_check_port on error + + .done: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + + .addrinuse: + mov dword[esp+32], -1 + mov dword[esp+20], EADDRINUSE + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_connect: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi + + call SOCKET_num_to_ptr + jz .invalid + + cmp esi, 8 + jb .invalid + + cmp [eax + SOCKET.state], SS_ISCONNECTING + je .already + + test [eax + SOCKET.options], SO_ACCEPTCON + jnz .notsupp + + call [eax + SOCKET.connect_proc] + + mov dword[esp+20], ebx + mov dword[esp+32], eax + ret + + + .notsupp: + mov dword[esp+20], EOPNOTSUPP + mov dword[esp+32], -1 + ret + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + ret + + .already: + mov dword[esp+20], EALREADY + mov dword[esp+32], -1 + ret + + +connect_notsupp: + xor eax, eax + dec eax + mov ebx, EOPNOTSUPP + ret + + +;----------------------------------------------------------------- +; +; SOCKET_listen +; +; IN: socket number in ecx +; backlog in edx +; OUT: eax is socket num, -1 on error +; +;----------------------------------------------------------------- +align 4 +SOCKET_listen: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_listen: socknum=%u backlog=%u\n", ecx, edx + + call SOCKET_num_to_ptr + jz .invalid + + cmp [eax + SOCKET.Domain], AF_INET4 + jne .notsupp + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + jne .invalid + + cmp [eax + TCP_SOCKET.LocalPort], 0 + je .already + + cmp [eax + IP_SOCKET.LocalIP], 0 + jne @f + push [IP_LIST + 4] ;;; fixme!!!! + 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 + + .notsupp: + mov dword[esp+20], EOPNOTSUPP + mov dword[esp+32], -1 + ret + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + ret + + .already: + mov dword[esp+20], EALREADY + mov dword[esp+32], -1 + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_accept: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi + + call SOCKET_num_to_ptr + jz .invalid + + test [eax + SOCKET.options], SO_ACCEPTCON + jz .invalid + + cmp [eax + SOCKET.Domain], AF_INET4 + jne .notsupp + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + jne .invalid + + .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 .invalid ; FIXME ? + +; and return it to caller + mov [esp+32], eax + ret + + .block: + test [eax + SOCKET.options], SO_NONBLOCK + jnz .wouldblock + + call SOCKET_block + jmp .loop + + .wouldblock: + mov dword[esp+20], EWOULDBLOCK + mov dword[esp+32], -1 + ret + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + ret + + .notsupp: + mov dword[esp+20], EOPNOTSUPP + mov dword[esp+32], -1 + ret + +;----------------------------------------------------------------- +; +; SOCKET_close +; +; IN: socket number in ecx +; OUT: eax is socket num, -1 on error +; +;----------------------------------------------------------------- +align 4 +SOCKET_close: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_close: socknum=%u\n", ecx + + call SOCKET_num_to_ptr + jz .invalid + + mov dword[esp+32], 0 ; The socket exists, so we will succeed in closing it. + + 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 + call SOCKET_notify.unblock ; Unblock it. + @@: + + cmp [eax + SOCKET.Domain], AF_INET4 + jne .free + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + je .tcp + + .free: + call SOCKET_free + ret + + .tcp: + + call TCP_usrclosed + + test eax, eax + jz @f + call TCP_output ; If connection is not closed yet, send the FIN + @@: + + ret + + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_receive: socknum=%u bufaddr=%x buflength=%u flags=%x\n", ecx, edx, esi, edi + + call SOCKET_num_to_ptr + jz .invalid + + .loop: + push edi + call [eax + SOCKET.rcv_proc] + pop edi + + test [eax + SOCKET.state], SS_CANTRCVMORE + jnz .return + + cmp ebx, EWOULDBLOCK + jne .return + + test edi, MSG_DONTWAIT + jnz .return_err + +; test [eax + SOCKET.options], SO_NONBLOCK +; jnz .return_err + + call SOCKET_block + jmp .loop + + + .invalid: + push EINVAL + pop ebx + .return_err: + mov ecx, -1 + .return: + mov [esp+20], ebx + mov [esp+32], ecx + ret + + + + + +align 4 +SOCKET_receive_dgram: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: DGRAM\n" + + mov ebx, esi ; bufferlength + + get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, .wouldblock ; sets esi only on success. + mov ecx, [esi + socket_queue_entry.data_size] + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: %u bytes data\n", ecx + + cmp ecx, ebx ; If data segment does not fit in applications buffer, abort + ja .too_small + + push eax ecx + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_receive: Source buffer=%x real addr=%x\n", [esp], esi + +; copy the data from kernel buffer to application buffer + mov edi, edx ; bufferaddr + shr ecx, 1 + jnc .nb + movsb + .nb: + shr ecx, 1 + jnc .nw + movsw + .nw: + test ecx, ecx + jz .nd + rep movsd + .nd: + + call NET_packet_free + pop ecx eax ; return number of bytes copied to application + xor ebx, ebx + ret + + .too_small: + mov ecx, -1 + push EMSGSIZE + pop ebx + ret + + .wouldblock: + push EWOULDBLOCK + pop ebx + ret + + + +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 :( + @@: + + mov [eax + SOCKET.rcv_proc], SOCKET_receive_stream + +; ... continue to SOCKET_receive_stream + +align 4 +SOCKET_receive_stream: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: STREAM\n" + + cmp [eax + STREAM_SOCKET.rcv + RING_BUFFER.size], 0 + je .wouldblock + + test edi, MSG_PEEK + jnz .peek + + mov ecx, esi + mov edi, edx + xor edx, edx + + push eax + add eax, STREAM_SOCKET.rcv + call SOCKET_ring_read ; copy data from kernel buffer to application buffer + call SOCKET_ring_free ; free read memory + pop eax + + xor ebx, ebx ; errorcode = 0 (no error) + ret + + .wouldblock: + push EWOULDBLOCK + pop ebx + xor ecx, ecx + ret + + .peek: + mov ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size] + xor ebx, ebx + ret + + +;----------------------------------------------------------------- +; +; 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 DEBUG_NETWORK_VERBOSE, "SOCKET_send: socknum=%u data ptr=%x length=%u flags=%x\n", ecx, edx, esi, edi + + call SOCKET_num_to_ptr + jz .invalid + + mov ecx, esi + mov esi, edx + + jmp [eax + SOCKET.snd_proc] + + .invalid: + mov dword[esp+20], EINVAL + mov dword[esp+32], -1 + ret + + +align 4 +SOCKET_send_udp: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: UDP\n" + + mov [esp+32], ecx + call UDP_output + cmp eax, -1 + je .error + ret + + .error: + mov dword[esp+32], -1 + mov dword[esp+20], EMSGSIZE ; FIXME: UDP_output should return error codes! + ret + + +align 4 +SOCKET_send_tcp: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP\n" + + push eax + add eax, STREAM_SOCKET.snd + call SOCKET_ring_write + pop eax + + mov [esp+32], ecx + mov [eax + SOCKET.errorcode], 0 + push eax + call TCP_output ; FIXME: this doesnt look pretty, does it? + pop eax + mov eax, [eax + SOCKET.errorcode] + mov [esp+20], eax + ret + + +align 4 +SOCKET_send_ip: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: IPv4\n" + + mov [esp+32], ecx + call IPv4_output_raw ; FIXME: IPv4_output_raw should return error codes! + cmp eax, -1 + je .error + ret + + .error: + mov dword[esp+32], -1 + mov dword[esp+20], EMSGSIZE + ret + + +align 4 +SOCKET_send_icmp: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: ICMP\n" + + mov [esp+32], ecx + call ICMP_output_raw ; FIXME: errorcodes + cmp eax, -1 + je .error + ret + + .error: + mov dword[esp+32], -1 + mov dword[esp+20], EMSGSIZE + ret + + +align 4 +SOCKET_send_pppoe: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: PPPoE\n" + + mov [esp+32], ecx + mov ebx, [eax + SOCKET.device] + + call PPPoE_discovery_output ; FIXME: errorcodes + cmp eax, -1 + je .error + ret + + .error: + mov dword[esp+32], -1 + mov dword[esp+20], EMSGSIZE + 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 DEBUG_NETWORK_VERBOSE, "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 .invalid + + ; 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 + + .invalid: + mov dword[esp+32], -1 + mov dword[esp+20], EINVAL + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_get_opt\n" + + call SOCKET_num_to_ptr + jz .invalid + + cmp dword [edx], IP_PROTO_TCP + jne .invalid + cmp dword [edx+4], -2 + je @f + cmp dword [edx+4], -3 + jne .invalid +@@: +; 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 + + .invalid: + mov dword[esp+32], -1 + mov dword[esp+20], EINVAL + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_set_opt\n" + + call SOCKET_num_to_ptr + jz .invalid + + cmp dword [edx], SOL_SOCKET + jne .invalid + + cmp dword [edx+4], SO_BINDTODEVICE + je .bind + + .invalid: + mov dword[esp+32], -1 + mov dword[esp+20], EINVAL + ret + + .bind: + cmp dword[edx+8], 0 + je .unbind + + movzx edx, byte[edx + 9] + cmp edx, NET_DEVICES_MAX + ja .invalid + + mov edx, [NET_DRV_LIST + 4*edx] + test edx, edx + jz .already + mov [eax + SOCKET.device], edx + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + + .already: + mov dword[esp+20], EALREADY + mov dword[esp+32], -1 + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_pair\n" + + call SOCKET_alloc + jz .nomem1 + 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 .nomem2 + mov [esp+20], 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 + + ret + + .nomem2: + mov eax, ebx + call SOCKET_free + .nomem1: + mov dword[esp+32], -1 + mov dword[esp+28], ENOMEM + ret + + + +;----------------------------------------------------------------- +; +; 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 DEBUG_NETWORK_VERBOSE, "SOCKET_debug\n" + + mov edi, edx + + test ecx, ecx + jz .returnall + + call SOCKET_num_to_ptr + jz .invalid + + mov esi, eax + mov ecx, SOCKETBUFFSIZE/4 + rep movsd + + mov dword[esp+32], 0 + ret + + .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], eax + ret + + .invalid: + mov dword[esp+32], -1 + mov dword[esp+28], EINVAL + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "SOCKET_check_port: " + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "local port %x already in use\n", bx ; FIXME: find a way to print big endian values with debugf + ret + + .port_ok: + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "SOCKET_input: socket %x is full!\n", eax + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + popa + + call NET_packet_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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 + pusha + lea ecx, [eax + RING_BUFFER.mutex] + call mutex_unlock ; TODO: check what registers this function actually destroys + popa + + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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: eax = unchanged +; +;----------------------------------------------------------------- +align 4 +SOCKET_block: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_block: %x\n", eax + + pushf + push eax + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_block: suspending thread: %u\n", edx + mov [eax + SOCKET.TID], edx + pop edx + + call change_task + pop eax + popf + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_block: continueing\n" + + ret + + +;----------------------------------------------------------------- +; +; SOCKET_notify +; +; notify's the owner of a socket that something happened +; +; IN: eax = socket ptr +; OUT: eax = unchanged +; +;----------------------------------------------------------------- +align 4 +SOCKET_notify: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "SOCKET_notify: poking thread %u!\n", eax + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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], .not_yet + mov [eax + SOCKET.rcv_proc], .not_yet + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + +; 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 DEBUG_NETWORK_VERBOSE, "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 + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + .exit: + pop ebx + + ret + + .not_yet: + mov dword[esp+20], ENOTCONN + mov dword[esp+32], -1 + ret + + +;---------------------------------------------------- +; +; SOCKET_free +; +; Free socket data memory and remove socket from the list +; Caller should lock and unlock socket_mutex +; +; IN: eax = socket ptr +; OUT: / +; +;---------------------------------------------------- +align 4 +SOCKET_free: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_free: freeing socket %x\n", eax + push eax ; this will be passed to kernel_free + mov ebx, [eax + SOCKET.NextPtr] + mov eax, [eax + SOCKET.PrevPtr] + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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, 2 pointers and mutex +; TID will be filled in later + 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 + +; Notify owner of parent socket + push eax + mov eax, ebx + call SOCKET_notify + pop eax + + ret + + .fail2: + add esp, 4+4+4 + .fail: + DEBUGF DEBUG_NETWORK_ERROR, "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 DEBUG_NETWORK_VERBOSE, "SOCKET_num_to_ptr: num=%u ", ecx + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "ptr=%x\n", eax + ret + + .error: + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_num_to_ptr: 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 DEBUG_NETWORK_VERBOSE, "SOCKET_ptr_to_num: ptr=%x ", eax + + call SOCKET_check + jz .error + + mov eax, [eax + SOCKET.Number] + + DEBUGF DEBUG_NETWORK_VERBOSE, "num=%u\n", eax + ret + + .error: + DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_ptr_to_num: 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 (clean up) +; +; IN: edx = pid +; OUT: / +; +;------------------------------------------------------ +align 4 +SOCKET_process_end: + + cmp [net_sockets + SOCKET.NextPtr], 0 ; Are there any active sockets at all? + je .quickret ; nope, exit immediately + +; TODO: run the following code in another thread, to avoid deadlock + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_process_end: %x\n", edx + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 DEBUG_NETWORK_VERBOSE, "SOCKET_process_end: killing socket %x\n", ebx + + mov [ebx + SOCKET.PID], 0 + mov eax, ebx + mov ebx, [ebx + SOCKET.NextPtr] + + pusha + cmp [eax + SOCKET.Domain], AF_INET4 + jne .free + + cmp [eax + SOCKET.Protocol], IP_PROTO_TCP + jne .free + + call TCP_disconnect + jmp .closed + + .free: + call SOCKET_free + + .closed: + popa + jmp .next_socket_test + + .done: + pop ebx + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + .quickret: + ret + + + + +;----------------------------------------------------------------- +; +; SOCKET_is_connecting +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_connecting: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_connecting: %x\n", eax + + and [eax + SOCKET.state], not (SS_ISCONNECTED + SS_ISDISCONNECTING + SS_ISCONFIRMING) + or [eax + SOCKET.state], SS_ISCONNECTING + + ret + + + +;----------------------------------------------------------------- +; +; SOCKET_is_connected +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_connected: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_connected: %x\n", eax + + and [eax + SOCKET.state], not (SS_ISCONNECTING + SS_ISDISCONNECTING + SS_ISCONFIRMING) + or [eax + SOCKET.state], SS_ISCONNECTED + + jmp SOCKET_notify + + + + +;----------------------------------------------------------------- +; +; SOCKET_is_disconnecting +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_disconnecting: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_disconnecting: %x\n", eax + + and [eax + SOCKET.state], not (SS_ISCONNECTING) + or [eax + SOCKET.state], SS_ISDISCONNECTING + SS_CANTRCVMORE + SS_CANTSENDMORE + + jmp SOCKET_notify + + + +;----------------------------------------------------------------- +; +; SOCKET_is_disconnected +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_is_disconnected: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_disconnected: %x\n", eax + + and [eax + SOCKET.state], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING) + or [eax + SOCKET.state], SS_CANTRCVMORE + SS_CANTSENDMORE + + + jmp SOCKET_notify + + + +;----------------------------------------------------------------- +; +; SOCKET_cant_recv_more +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_cant_recv_more: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_cant_recv_more: %x\n", eax + + or [eax + SOCKET.state], SS_CANTRCVMORE + + call SOCKET_notify + + ret + + + +;----------------------------------------------------------------- +; +; SOCKET_cant_send_more +; +; IN: eax = socket ptr +; OUT: / +; +;----------------------------------------------------------------- + +align 4 +SOCKET_cant_send_more: + + DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_cant_send_more: %x\n", eax + + or [eax + SOCKET.state], SS_CANTSENDMORE + mov [eax + SOCKET.snd_proc], .notconn + + call SOCKET_notify + + ret + + .notconn: + mov dword[esp+20], ENOTCONN + mov dword[esp+32], -1 + ret \ No newline at end of file diff --git a/kernel/branches/kolibri-process/network/stack.inc b/kernel/branches/kolibri-process/network/stack.inc new file mode 100644 index 000000000..390072939 --- /dev/null +++ b/kernel/branches/kolibri-process/network/stack.inc @@ -0,0 +1,791 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: 3523 $ + +uglobal + net_10ms dd ? + net_tmr_count dw ? +endg + +DEBUG_NETWORK_ERROR = 1 +DEBUG_NETWORK_VERBOSE = 0 + +NET_DEVICES_MAX = 16 +ARP_BLOCK = 1 ; true or false + +EPHEMERAL_PORT_MIN = 49152 +EPHEMERAL_PORT_MAX = 61000 +MIN_EPHEMERAL_PORT_N = 0x00C0 ; same in Network byte order (FIXME) +MAX_EPHEMERAL_PORT_N = 0x48EE ; same in Network byte order (FIXME) + +; Ethernet protocol numbers +ETHER_PROTO_ARP = 0x0608 +ETHER_PROTO_IPv4 = 0x0008 +ETHER_PROTO_IPv6 = 0xDD86 +ETHER_PROTO_PPP_DISCOVERY = 0x6388 +ETHER_PROTO_PPP_SESSION = 0x6488 + +; Internet protocol numbers +IP_PROTO_IP = 0 +IP_PROTO_ICMP = 1 +IP_PROTO_TCP = 6 +IP_PROTO_UDP = 17 + +; PPP protocol numbers +PPP_PROTO_IPv4 = 0x2100 +PPP_PROTO_IPV6 = 0x5780 +PPP_PROTO_ETHERNET = 666 ; FIXME + +;Protocol family +AF_UNSPEC = 0 +AF_LOCAL = 1 +AF_INET4 = 2 +AF_INET6 = 10 +AF_PPP = 777 ; FIXME + +; 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_NONBLOCK = 1 shl 31 + +; Socket flags for user calls +MSG_PEEK = 0x02 +MSG_DONTWAIT = 0x40 + +; Socket level +SOL_SOCKET = 0 + + +; 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 + +SS_ASYNC = 0x1000 ; async i/o notify +SS_ISCONFIRMING = 0x2000 ; deciding to accept connection req +SS_MORETOCOME = 0x4000 + +SS_BLOCKED = 0x8000 + + +SOCKET_MAXDATA = 4096*8 ; must be 4096*(power of 2) where 'power of 2' is at least 8 +MAX_backlog = 20 ; maximum backlog for stream sockets + +; Error Codes +ENOBUFS = 1 +EINPROGRESS = 2 +EOPNOTSUPP = 4 +EWOULDBLOCK = 6 +ENOTCONN = 9 +EALREADY = 10 +EINVAL = 11 +EMSGSIZE = 12 +ENOMEM = 18 +EADDRINUSE = 20 +ECONNREFUSED = 61 +ECONNRESET = 52 +EISCONN = 56 +ETIMEDOUT = 60 +ECONNABORTED = 53 + +; 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 + +; Network device types +NET_DEVICE_LOOPBACK = 0 +NET_DEVICE_ETH = 1 +NET_DEVICE_SLIP = 2 + +; Network link types (link protocols) +NET_LINK_LOOPBACK = 0 ;;; Really a link type? +NET_LINK_MAC = 1 ; Media access control (ethernet, isdn, ...) +NET_LINK_PPP = 2 ; Point to Point Protocol (PPPoE, ...) +NET_LINK_IEEE802.11 = 3 ; IEEE 802.11 (WiFi) + +; Hardware acceleration bits +NET_HWACC_TCP_IPv4_IN = 1 shl 0 +NET_HWACC_TCP_IPv4_OUT = 1 shl 1 + +struct NET_DEVICE + + 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 ? ; + + link_state dd ? ; link state (0 = no link) + hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines) + +ends + + +; Exactly as it says.. +macro pseudo_random reg { + add reg, [esp] + rol reg, 5 + xor reg, [timer_ticks] +; add reg, [CPU_FREQ] + imul reg, 214013 + xor reg, 0xdeadbeef + rol reg, 9 +} + +; Network to Hardware byte order (dword) +macro ntohd reg { + + rol word reg, 8 + rol dword reg, 16 + rol word reg , 8 + +} + +; Network to Hardware byte order (word) +macro ntohw reg { + + rol word reg, 8 + +} + + +include "queue.inc" + +include "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" + + + +uglobal +align 4 + + NET_RUNNING dd ? + NET_DRV_LIST rd NET_DEVICES_MAX + +endg + + +;----------------------------------------------------------------- +; +; stack_init +; +; This function calls all network init procedures +; +; IN: / +; OUT: / +; +;----------------------------------------------------------------- +align 4 +stack_init: + +; Init the network drivers list + xor eax, eax + mov edi, NET_RUNNING + mov ecx, (NET_DEVICES_MAX + 2) + rep stosd + + ETH_init + + PPPoE_init + + IPv4_init +; IPv6_init + ICMP_init + + ARP_init + UDP_init + TCP_init + + SOCKET_init + + LOOP_init + + mov [net_tmr_count], 0 + + ret + + + +; Wakeup every tick. +proc stack_handler_has_work? + + mov eax, [timer_ticks] + cmp eax, [net_10ms] + + ret +endp + + +;----------------------------------------------------------------- +; +; stack_handler +; +; This function is called in kernel loop +; +; IN: / +; OUT: / +; +;----------------------------------------------------------------- +align 4 +stack_handler: + + ; Test for 10ms tick + mov eax, [timer_ticks] + cmp eax, [net_10ms] + je .exit + mov [net_10ms], eax + + cmp [NET_RUNNING], 0 + je .exit + + 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 + + +align 4 +NET_packet_free: + and dword[esp+4], not 0xfff + jmp kernel_free + + +align 4 +NET_link_changed: + + DEBUGF DEBUG_NETWORK_VERBOSE, "NET_link_changed device=0x%x status=0x%x\n", ebx, [ebx + NET_DEVICE.link_state] + +align 4 +NET_send_event: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + + ret + + + +;----------------------------------------------------------------- +; +; NET_add_device: +; +; This function is called by the network drivers, +; to register each running NIC to the kernel +; +; IN: Pointer to device structure in ebx +; OUT: Device num in eax, -1 on error +; +;----------------------------------------------------------------- +align 4 +NET_add_device: + + DEBUGF DEBUG_NETWORK_VERBOSE, "NET_Add_Device: %x\n", ebx ;;; TODO: use mutex to lock net device list + + cmp [NET_RUNNING], NET_DEVICES_MAX + jae .error + +;---------------------------------- +; Check if device is already listed + mov eax, ebx + mov ecx, NET_DEVICES_MAX ; We need to check whole list because a device may be removed without re-organizing list + mov edi, NET_DRV_LIST + + repne scasd ; See if device is already in the list + jz .error + +;---------------------------- +; Find empty slot in the list + xor eax, eax + mov ecx, NET_DEVICES_MAX + mov edi, NET_DRV_LIST + + repne scasd + jnz .error + + sub edi, 4 + +;----------------------------- +; Add device to the found slot + mov [edi], ebx ; add device to list + + mov eax, edi ; Calculate device number in eax + sub eax, NET_DRV_LIST + shr eax, 2 + + inc [NET_RUNNING] ; Indicate that one more network device is up and running + + call NET_send_event + + DEBUGF DEBUG_NETWORK_VERBOSE, "Device number: %u\n", eax + ret + + .error: + or eax, -1 + DEBUGF DEBUG_NETWORK_ERROR, "Adding network device failed\n" + ret + + + +;----------------------------------------------------------------- +; +; NET_Remove_Device: +; +; This function is called by network 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 + +;---------------------------- +; Find the driver in the list + + mov eax, ebx + mov ecx, NET_DEVICES_MAX + mov edi, NET_DRV_LIST + + repne scasd + jnz .error + +;------------------------ +; Remove it from the list + + xor eax, eax + mov dword [edi-4], eax + dec [NET_RUNNING] + + call NET_send_event + + xor eax, eax + 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: + + call NET_ptr_to_num4 + ror edi, 2 ; If -1, stay -1 + ; valid device numbers have last two bits 0, so do just shr + + ret + +align 4 +NET_ptr_to_num4: ; Todo, place number in device structure so we only need to verify? + + push ecx + + mov ecx, NET_DEVICES_MAX + mov edi, NET_DRV_LIST + .loop: + cmp ebx, [edi] + je .found + add edi, 4 + dec ecx + jnz .loop + + or edi, -1 + pop ecx + ret + + .found: + sub edi, NET_DRV_LIST + 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 DEBUG_NETWORK_VERBOSE, "Checksum: %x\n", dx + + ret + + + +;---------------------------------------------------------------- +; +; System function to work with network devices (74) +; +;---------------------------------------------------------------- +align 4 +sys_network: + + cmp bl, 255 + jne @f + + mov eax, [NET_RUNNING] + mov [esp+32], eax + ret + + @@: + cmp bh, NET_DEVICES_MAX ; 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 .packets_tx ; 6 + dd .packets_rx ; 7 + dd .bytes_tx ; 8 + dd .bytes_rx ; 9 + dd .state ; 10 + .number = ($ - .table) / 4 - 1 + + .get_type: + mov eax, [eax + NET_DEVICE.device_type] + mov [esp+32], eax + ret + + .get_dev_name: + mov esi, [eax + NET_DEVICE.name] + mov edi, ecx + + mov ecx, 64/4 ; max length + rep movsd + + xor eax, eax + mov [esp+32], eax + ret + + .reset: + call [eax + NET_DEVICE.reset] + mov [esp+32], eax + ret + + .stop: + call [eax + NET_DEVICE.unload] + mov [esp+32], eax + ret + + + .get_ptr: + mov [esp+32], eax + ret + + + .get_drv_name: + xor eax, eax + mov [esp+32], eax + ret + + .packets_tx: + mov eax, [eax + NET_DEVICE.packets_tx] + mov [esp+32], eax + ret + + .packets_rx: + mov eax, [eax + NET_DEVICE.packets_rx] + mov [esp+32], eax + ret + + .bytes_tx: + mov ebx, dword [eax + NET_DEVICE.bytes_tx + 4] + mov [esp+20], ebx + mov eax, dword [eax + NET_DEVICE.bytes_tx] + mov [esp+32], eax + ret + + .bytes_rx: + mov ebx, dword [eax + NET_DEVICE.bytes_rx + 4] + mov [esp+20], ebx + mov eax, dword [eax + NET_DEVICE.bytes_rx] + mov [esp+32], eax + ret + + .state: + mov eax, [eax + NET_DEVICE.link_state] + mov [esp+32], eax + ret + + + .doesnt_exist: + mov dword[esp+32], -1 + ret + + + +;---------------------------------------------------------------- +; +; System function to work with protocols (76) +; +;---------------------------------------------------------------- +align 4 +sys_protocols: + cmp bh, NET_DEVICES_MAX ; 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-process/network/tcp.inc b/kernel/branches/kolibri-process/network/tcp.inc new file mode 100644 index 000000000..25d096140 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp.inc @@ -0,0 +1,286 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: 3406 $ + +; 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 + +; 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 + +; 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 + +; 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 + +; 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 + +TCP_time_connect = 300 ; in 1/100s (default=3s) + +; timer constants +TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK +TCP_max_keepcnt = 8 ; max keepalive probes + +; +TCP_max_winshift = 14 +TCP_max_win = 65535 + +TCP_re_xmit_thresh = 3 + +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 + +; bits used by tcp_input and tcp_output +TCP_BIT_NEEDOUTPUT = 1 shl 0 +TCP_BIT_TIMESTAMP = 1 shl 1 +TCP_BIT_DROPSOCKET = 1 shl 2 + +TCP_BIT_SENDALOT = 1 shl 0 + +TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds + +TCP_QUEUE_SIZE = 50 + +struct TCP_header + + 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 ? + +ends + +struct TCP_queue_entry + + ip_ptr dd ? + segment_ptr dd ? + segment_size dd ? + device_ptr dd ? + + buffer_ptr dd ? + timestamp dd ? + +ends + +uglobal +align 4 + + TCP_segments_tx rd NET_DEVICES_MAX + TCP_segments_rx rd NET_DEVICES_MAX + TCP_segments_missed rd NET_DEVICES_MAX + TCP_segments_dumped rd NET_DEVICES_MAX +; TCP_bytes_rx rq NET_DEVICES_MAX +; TCP_bytes_tx rq NET_DEVICES_MAX + TCP_sequence_num dd ? + TCP_queue rd (TCP_QUEUE_SIZE*sizeof.TCP_queue_entry + sizeof.queue)/4 + TCP_input_event dd ? +endg + +uglobal +align 4 + + TCPS_accepts dd ? ; #SYNs received in LISTEN state + TCPS_closed dd ? ; #connections closed (includes drops) + TCPS_connattempt dd ? ; #connections initiated (calls to connect) + TCPS_conndrops dd ? ; #embryonic connections dropped (before SYN received) + TCPS_connects dd ? ; #connections established actively or passively + TCPS_delack dd ? ; #delayed ACKs sent + TCPS_drops dd ? ; #connections dropped (after SYN received) + TCPS_keepdrops dd ? ; #connections dropped in keepalive (established or awaiting SYN) + TCPS_keepprobe dd ? ; #keepalive probes sent + TCPS_keeptimeo dd ? ; #times keepalive timer or connections-establishment timer expire + TCPS_pawsdrop dd ? ; #segments dropped due to PAWS + TCPS_pcbcachemiss dd ? ; #times PCB cache comparison fails + TCPS_persisttimeo dd ? ; #times persist timer expires + TCPS_predack dd ? ; #times header prediction correct for ACKs + TCPS_preddat dd ? ; #times header prediction correct for data packets + TCPS_rcvackbyte dd ? ; #bytes ACKed by received ACKs + TCPS_rcvackpack dd ? ; #received ACK packets + TCPS_rcvacktoomuch dd ? ; #received ACKs for unsent data + TCPS_rcvafterclose dd ? ; #packets received after connection closed + TCPS_rcvbadoff dd ? ; #packets received with invalid header length + TCPS_rcvbadsum dd ? ; #packets received with checksum errors + TCPS_rcvbyte dd ? ; #bytes received in sequence + TCPS_rcvbyteafterwin dd ? ; #bytes received beyond advertised window + TCPS_rcvdupack dd ? ; #duplicate ACKs received + TCPS_rcvdupbyte dd ? ; #bytes receivedin completely duplicate packets + TCPS_rcvduppack dd ? ; #packets received with completely duplicate bytes + TCPS_rcvoobyte dd ? ; #out-of-order bytes received + TCPS_rcvoopack dd ? ; #out-of-order packets received + TCPS_rcvpack dd ? ; #packets received in sequence + TCPS_rcvpackafterwin dd ? ; #packets with some data beyond advertised window + TCPS_rcvpartdupbyte dd ? ; #duplicate bytes in part-duplicate packets + TCPS_rcvpartduppack dd ? ; #packets with some duplicate data + TCPS_rcvshort dd ? ; #packets received too short + TCPS_rcvtotal dd ? ; #total packets received + TCPS_rcvwinprobe dd ? ; #window probe packets received + TCPS_rcvwinupd dd ? ; #received window update packets + TCPS_rexmttimeo dd ? ; #retransmission timeouts + TCPS_rttupdated dd ? ; #times RTT estimators updated + TCPS_segstimed dd ? ; #segments for which TCP tried to measure RTT + TCPS_sndacks dd ? ; #ACK-only packets sent (data length = 0) + TCPS_sndbyte dd ? ; #data bytes sent + TCPS_sndctrl dd ? ; #control (SYN, FIN, RST) packets sent (data length = 0) + TCPS_sndpack dd ? ; #data packets sent (data length > 0) + TCPS_sndprobe dd ? ; #window probes sent (1 byte of data forced by persist timer) + TCPS_sndrexmitbyte dd ? ; #data bytes retransmitted + TCPS_sndrexmitpack dd ? ; #data packets retransmitted + TCPS_sndtotal dd ? ; total #packets sent + TCPS_sndurg dd ? ; #packets sent with URG-only (data length=0) + TCPS_sndwinup dd ? ; #window update-only packets sent (data length=0) + TCPS_timeoutdrop dd ? ; #connections dropped in retransmission timeout + +endg + + +;----------------------------------------------------------------- +; +; TCP_init +; +; This function resets all TCP variables +; +;----------------------------------------------------------------- +macro TCP_init { + + xor eax, eax + mov edi, TCP_segments_tx + mov ecx, (6*NET_DEVICES_MAX) + rep stosd + + pseudo_random eax + mov [TCP_sequence_num], eax + + init_queue TCP_queue + + movi ebx, 1 + mov ecx, TCP_process_input + call new_sys_threads + test eax, eax + jns @f + DEBUGF DEBUG_NETWORK_ERROR,'K : cannot create kernel thread for TCP, error %d\n', eax + @@: + +} + + +include 'tcp_timer.inc' +include 'tcp_subr.inc' +include 'tcp_usreq.inc' +include 'tcp_input.inc' +include 'tcp_output.inc' + + +;--------------------------------------------------------------------------- +; +; TCP_API +; +; 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: + + movzx eax, bh + shl eax, 2 + + 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 + + .packets_tx: + mov eax, [TCP_segments_tx + eax] + ret + + .packets_rx: + mov eax, [TCP_segments_rx + eax] + ret + + .packets_missed: + mov eax, [TCP_segments_missed + eax] + ret + + .packets_dumped: + mov eax, [TCP_segments_dumped + eax] + ret diff --git a/kernel/branches/kolibri-process/network/tcp_input.inc b/kernel/branches/kolibri-process/network/tcp_input.inc new file mode 100644 index 000000000..d61846191 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp_input.inc @@ -0,0 +1,1719 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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 + + call NET_ptr_to_num4 + inc [TCP_segments_rx + edi] + + xor edx, edx + mov eax, [TCP_input_event] + mov ebx, [eax + EVENT.id] + xor esi, esi + call raise_event + + ret + + .fail: + popf + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP incoming queue is full, discarding packet!\n" + + call NET_ptr_to_num4 + inc [TCP_segments_missed + edi] + + add esp, sizeof.TCP_queue_entry - 8 + call NET_packet_free + add esp, 4 + + ret + + + + +align 4 +proc TCP_process_input + +locals + dataoffset dd ? + timestamp dd ? + temp_bits db ? +endl + + 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] + pop [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 DEBUG_NETWORK_VERBOSE, "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], NET_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 + movzx eax, [edx + TCP_header.DataOffset] + and al, 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) + shr al, 2 + cmp al, 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 + mov [dataoffset], eax + + 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 DEBUG_NETWORK_VERBOSE, "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: + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + mov ebx, net_sockets + mov si, [edx + TCP_header.DestinationPort] + + .socket_loop: + mov ebx, [ebx + SOCKET.NextPtr] + or ebx, ebx + jz .no_socket ;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 + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: socket ptr=%x state=%u flags=%x\n", ebx, [ebx + TCP_SOCKET.t_state], [edx + TCP_header.Flags]:2 + +;---------------------------- +; 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 DEBUG_NETWORK_VERBOSE, "TCP_input: socket locked\n" + +;--------------------------- +; disable all temporary bits + + mov [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 DEBUG_NETWORK_VERBOSE, "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 [temp_bits], TCP_BIT_DROPSOCKET + + 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 + or [ebx + TCP_SOCKET.timer_flags], timer_flag_keepalive + +;-------------------- +; Process TCP options + +;;; 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 + + push ecx + + mov ecx, [dataoffset] + cmp ecx, sizeof.TCP_header ; Does header contain any options? + je .no_options + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Segment has options\n" + + 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 DEBUG_NETWORK_VERBOSE, "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 + + xor eax, eax + lodsw + rol ax, 8 + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Maxseg=%u\n", eax + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 + bswap eax + mov [ebx + TCP_SOCKET.ts_val], eax + lodsd ; timestamp echo reply + mov [ebx + TCP_SOCKET.ts_ecr], eax + or [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] + jbe .no_paws + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: PAWS: detected an old segment\n" + + mov eax, [timestamp] + 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 DEBUG_NETWORK_VERBOSE, "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 [temp_bits], TCP_BIT_TIMESTAMP + jz .no_timestamp_rtt + mov eax, [timestamp] + 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 + and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission + +; 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 DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction: we are receiving %u bytes\n", ecx + + mov esi, [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 + + 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 DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction failed\n" + +; Calculate receive window size + push edx + mov eax, SOCKET_MAXDATA + sub eax, [ebx + STREAM_SOCKET.rcv.size] + DEBUGF DEBUG_NETWORK_VERBOSE, "Space in receive buffer=%d\n", eax + mov edx, [ebx + TCP_SOCKET.RCV_ADV] + sub edx, [ebx + TCP_SOCKET.RCV_NXT] + DEBUGF DEBUG_NETWORK_VERBOSE, "Current advertised window=%d\n", edx + cmp eax, edx + jg @f + mov eax, edx + @@: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + +; 1. Check for duplicate data at beginning of segment + +; Calculate number of bytes we need to drop + mov eax, [ebx + TCP_SOCKET.RCV_NXT] + sub eax, [edx + TCP_header.SequenceNumber] + jle .no_duplicate + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: %u bytes duplicate data!\n", eax + + test [edx + TCP_header.Flags], TH_SYN + jz .no_dup_syn + + DEBUGF DEBUG_NETWORK_VERBOSE, "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: + +; 2. Check for entire duplicate segment + 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 + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + mov eax, ecx + +;;; TODO: update stats + +;----------------------------------------------- +; Remove duplicate data and update urgent offset + + .duplicate: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: trimming duplicate data\n" + +; Trim data from left side of window + add [dataoffset], eax + 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 + + .no_duplicate: + cmp [ebx + SOCKET.PID], 0 ;;; TODO: use socket flags instead?? + 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 + + .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 DEBUG_NETWORK_VERBOSE, "%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, we 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 esi, [edx + TCP_header.SequenceNumber] + cmp esi, [ebx + TCP_SOCKET.RCV_NXT] + jne .drop_after_ack + + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW +;;; TODO: update stats + .dont_drop_all: +;;; TODO: update stats + DEBUGF DEBUG_NETWORK_VERBOSE, "Trimming %u bytes from the right of the window\n" + sub ecx, eax ; remove data from the right side of window (decrease data length) + and [edx + TCP_header.Flags], not (TH_PUSH or TH_FIN) + .no_excess_data: + +;----------------- +; Record timestamp + +; If last ACK falls within this segments sequence numbers, record its timestamp + test [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 DEBUG_NETWORK_VERBOSE, "Recording timestamp\n" + + mov eax, [timestamp] + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "TCP_input: Connection refused\n" + + mov [ebx + SOCKET.errorcode], ECONNREFUSED + jmp .close + + .econnreset: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Connection reset\n" + + mov [ebx + SOCKET.errorcode], ECONNRESET + + .close: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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. + + test [ebx + TCP_SOCKET.timer_flags], timer_flag_retransmission + jz @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] + jbe @f + mov 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 + + and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission ; 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "TCP_input: acceptable ACK for %u bytes\n", edi + +;------------------------------------------ +; RTT measurements and retransmission timer + +; If we have a timestamp, update smoothed RTT + + test [temp_bits], TCP_BIT_TIMESTAMP + jz .timestamp_not_present + mov eax, [timestamp] + 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 + and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission + or [temp_bits], TCP_BIT_NEEDOUTPUT + jmp .no_restart + .more_data: + test [ebx + TCP_SOCKET.timer_flags], timer_flag_persist + jnz .no_restart + + mov eax, [ebx + TCP_SOCKET.t_rxtcur] + mov [ebx + TCP_SOCKET.timer_retransmission], eax + or [ebx + TCP_SOCKET.timer_flags], timer_flag_retransmission + .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 + jbe @f + mov 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 + or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait + @@: + 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 + or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait + mov eax, ebx + call SOCKET_is_disconnected + jmp .ack_processed + + .ack_la: + jnc .ack_processed + + push ebx + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + pop ebx + + mov eax, ebx + call TCP_close + jmp .drop_no_socket + + .ack_tw: + mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL + or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait + 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 DEBUG_NETWORK_VERBOSE, "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 + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval ;;;; macro + or [ebx + TCP_SOCKET.timer_flags], timer_flag_keepalive + + lea eax, [ebx + STREAM_SOCKET.snd] + call SOCKET_ring_create + + lea eax, [ebx + STREAM_SOCKET.rcv] + call SOCKET_ring_create + + and [temp_bits], not TCP_BIT_DROPSOCKET + + pusha + mov eax, ebx + call SOCKET_notify + popa + + jmp .trim_then_step6 + +;------------ +; Active Open + +align 4 + .SYN_SENT: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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: + and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission ; disable retransmission timer + + 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 DEBUG_NETWORK_VERBOSE, "TCP_input: active open\n" + +;;; TODO: update stats + +; set socket state to connected + push eax + mov eax, ebx + call SOCKET_is_connected + pop eax + 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 DEBUG_NETWORK_VERBOSE, "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] + +; Drop any received data that doesnt fit in the receive window. + cmp ecx, [ebx + TCP_SOCKET.RCV_WND] + jbe .dont_trim + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: received data does not fit in window, trimming %u bytes\n", eax + mov ecx, [ebx + TCP_SOCKET.RCV_WND] + and [edx + TCP_header.Flags], not (TH_FIN) +;;; TODO: update stats + + .dont_trim: + + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 [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 + jz .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 + mov esi, [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: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP data is out of order!\nSequencenumber is %u, we expected %u.\n", \ + [edx + TCP_header.SequenceNumber], [ebx + TCP_SOCKET.RCV_NXT] + +; Uh-oh, some data is out of order, lets call TCP reassemble for help + + call TCP_reassemble + +; Generate ACK immediately, to let the other end know that a segment was received out of order, +; and to tell it what sequence number is expected. This aids the fast-retransmit algorithm. + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + .data_done: + +;--------------- +; FIN processing + + test [edx + TCP_header.Flags], TH_FIN + jz .final_processing + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Processing FIN\n" + + cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT + jae .not_first_fin + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: First FIN for this connection\n" + + mov eax, ebx + call SOCKET_cant_recv_more + + or [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 + call SOCKET_is_disconnected + + .fin_timed: + mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL + or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait + +;----------------- +; Final processing + + .final_processing: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Final processing\n" + + push ebx + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + pop eax + + test [temp_bits], TCP_BIT_NEEDOUTPUT + jnz .need_output + + test [eax + TCP_SOCKET.t_flags], TF_ACKNOW + jz .dumpit + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n" + + .need_output: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n" + call TCP_output + + .dumpit: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n" + + call NET_packet_free + jmp .loop + + +;----------------- +; Drop the segment + + + .drop_after_ack: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 + +;--------- +; 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 + + .no_socket: + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + .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 DEBUG_NETWORK_VERBOSE, "TCP_input: Dropping segment\n" + + pusha + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + popa + + .destroy_new_socket: + test [temp_bits], TCP_BIT_DROPSOCKET + jz .drop_no_socket + + mov eax, ebx + call SOCKET_free + + .drop_no_socket: + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop (no socket)\n" + + call NET_packet_free + jmp .loop + +endp diff --git a/kernel/branches/kolibri-process/network/tcp_output.inc b/kernel/branches/kolibri-process/network/tcp_output.inc new file mode 100644 index 000000000..037df1999 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp_output.inc @@ -0,0 +1,657 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: eax = 0 on success/errorcode +; +;----------------------------------------------------------------- +align 4 +proc TCP_output + +locals + temp_bits db ? +endl + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x state=%u\n", eax, [eax + TCP_SOCKET.t_state] + + push eax + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + pop eax + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket locked\n" + +; 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 [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 DEBUG_NETWORK_VERBOSE, "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: + and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist + 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 + and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission + +; 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 [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 .send + + add ebx, esi ; offset + length + cmp ebx, [eax + STREAM_SOCKET.snd.size] + jb @f + + test [eax + TCP_SOCKET.t_flags], TF_NODELAY + jnz .send + + mov ebx, [eax + TCP_SOCKET.SND_MAX] + cmp ebx, [eax + TCP_SOCKET.SND_UNA] + je .send + @@: + + test [eax + TCP_SOCKET.t_force], -1 ;;; + jnz .send + + mov ebx, [eax + TCP_SOCKET.max_sndwnd] + shr ebx, 1 + cmp esi, ebx + jae .send + + mov ebx, [eax + TCP_SOCKET.SND_NXT] + cmp ebx, [eax + TCP_SOCKET.SND_MAX] + jb .send + + .len_zero: + +;---------------------------------------- +; Check if a window update should be sent (154) + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 .send + + shl ebx, 1 +; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark +; jae TCP_send + + .no_window: + +;-------------------------- +; Should a segment be sent? (174) + + test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK + jnz .send + + test dl, TH_SYN + TH_RST ; we need to send a SYN or RST + jnz .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 .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 .send + + mov ebx, [eax + TCP_SOCKET.SND_NXT] + cmp ebx, [eax + TCP_SOCKET.SND_UNA] + je .send + +;-------------------- +; Enter persist state (191) + + .enter_persist: + + cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? + jne @f + and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission + jne @f + + test [eax + TCP_SOCKET.timer_flags], timer_flag_persist ; Persist timer already expired? + jnz @f + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 +; +;----------------------------------------------- + .send: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 [temp_bits], TCP_BIT_SENDALOT + .no_overflow: + +;---------------------------------------------------- +; Calculate the receive window. +; Dont shrink window, but avoid silly window syndrome + + mov ebx, SOCKET_MAXDATA + sub ebx, [eax + STREAM_SOCKET.rcv.size] + + cmp ebx, SOCKET_MAXDATA/4 + jae @f + cmp ebx, [eax + TCP_SOCKET.t_maxseg] + jae @f + xor ebx, ebx + @@: + + cmp ebx, TCP_max_win + jbe @f + mov ebx, TCP_max_win + @@: + + mov ecx, [eax + TCP_SOCKET.RCV_ADV] + sub ecx, [eax + TCP_SOCKET.RCV_NXT] + cmp ebx, ecx + ja @f + mov ebx, ecx + @@: + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: window = %u\n", ebx + + mov cl, [eax + TCP_SOCKET.RCV_SCALE] + shr ebx, cl + xchg bl, bh + +;----------------------------------------------------------------- +; 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 bx ; .Window dw ? + 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 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 + test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission + jnz .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 + or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission + + test [eax + TCP_SOCKET.timer_flags], timer_flag_persist + jz .retransmit_set + and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist + mov [eax + TCP_SOCKET.t_rxtshift], 0 + + .retransmit_set: + +;-------------------- +; Create the checksum + + xor dx, dx + test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_OUT + jnz .checksum_ok + + TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) + + .checksum_ok: + mov [esi + TCP_header.Checksum], dx + +;---------------- +; Send the packet + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx + call [ebx + NET_DEVICE.transmit] + jnz .send_error + +;--------------- +; Ok, data sent! + + pop ecx + pop eax + + call NET_ptr_to_num4 + inc [TCP_segments_tx + edi] + +; 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] + +; clear the ACK flags + and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) + +;-------------- +; unlock socket + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: unlocking socket 0x%x\n", eax + + push eax + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + pop eax + +;----------------------------- +; Check if we need more output + + test [temp_bits], TCP_BIT_SENDALOT + jnz TCP_output.again + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission + + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + + DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: IP error\n" + + or eax, -1 + ret + + + .send_error: + add esp, 4 + pop eax + + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + + DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n" + + or eax, -2 + ret + + +endp \ No newline at end of file diff --git a/kernel/branches/kolibri-process/network/tcp_subr.inc b/kernel/branches/kolibri-process/network/tcp_subr.inc new file mode 100644 index 000000000..91514a8d2 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp_subr.inc @@ -0,0 +1,588 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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 + + push eax + call TCP_output + pop eax + +;;; 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_disconnect +; +; IN: eax = socket ptr +; OUT: eax = socket ptr / 0 +; +;------------------------- +align 4 +TCP_disconnect: + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_disconnect: %x\n", eax + + cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED + jb TCP_close ; Connection not yet synchronised, just get rid of the socket + +; TODO: implement LINGER + + call SOCKET_is_disconnecting + call TCP_usrclosed + + test eax, eax + jz @f + push eax + call TCP_output + pop eax + @@: + + ret + + +;------------------------- +; +; TCP_close +; +; IN: eax = socket ptr +; OUT: / +; +;------------------------- +align 4 +TCP_close: + + DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_close: %x\n", eax + +;;; TODO: update RTT and mean deviation +;;; TODO: update slow start threshold + + call SOCKET_is_disconnected + call SOCKET_free + + xor eax, eax + + 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 DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "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] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [TCP_segments_tx + edi] + @@: + ret + + .error: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE,"TCP_respond_segment: frame=%x flags=%x\n", edx, cl + +;--------------------- +; Create the IP packet + + push cx edx + mov edx, [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] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [TCP_segments_tx + edi] + @@: + ret + + .error: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "TCP_set_persist\n" + +; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive + + test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission + jnz .exit + +; 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 + or [ebx + TCP_SOCKET.timer_flags], timer_flag_persist + pop ebx + + cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift + jae @f + inc [eax + TCP_SOCKET.t_rxtshift] + @@: + .exit: + + ret + + + +; eax = rtt +; ebx = socket ptr + +align 4 +TCP_xmit_timer: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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-process/network/tcp_timer.inc b/kernel/branches/kolibri-process/network/tcp_timer.inc new file mode 100644 index 000000000..522a055e8 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp_timer.inc @@ -0,0 +1,172 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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: 3143 $ + +timer_flag_retransmission = 1 shl 0 +timer_flag_keepalive = 1 shl 1 +timer_flag_2msl = 1 shl 2 +timer_flag_persist = 1 shl 3 +timer_flag_wait = 1 shl 4 + + +;---------------------- +; 160 ms timer +;---------------------- +macro TCP_timer_160ms { + +local .loop +local .exit + + mov ebx, net_sockets + .loop: + mov ebx, [ebx + SOCKET.NextPtr] + test 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 { ; TODO: implement timed wait timer! + +local .loop +local .exit + +; Update TCP sequence number + + add [TCP_sequence_num], 64000 + +; scan through all the active TCP sockets, decrementing ALL timers +; When a timer reaches zero, we'll check wheter it was active or not + + 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 + test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission + jz .check_more2 + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + test [eax + TCP_SOCKET.timer_flags], timer_flag_keepalive + jz .check_more3 + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + test [eax + TCP_SOCKET.timer_flags], timer_flag_2msl + jz .check_more5 + + DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: 2MSL timer expired\n", eax + + .check_more5: + dec [eax + TCP_SOCKET.timer_persist] + jnz .loop + test [eax + TCP_SOCKET.timer_flags], timer_flag_persist + jz .loop + + DEBUGF DEBUG_NETWORK_VERBOSE, "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: + + mov [eax + TCP_SOCKET.timer_flags], 0 + + ret \ No newline at end of file diff --git a/kernel/branches/kolibri-process/network/tcp_usreq.inc b/kernel/branches/kolibri-process/network/tcp_usreq.inc new file mode 100644 index 000000000..65128da74 --- /dev/null +++ b/kernel/branches/kolibri-process/network/tcp_usreq.inc @@ -0,0 +1,203 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 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 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;------------------------- +; +; TCP_usrclose +; +; Move connection to next state, based on process close. +; +; IN: eax = socket ptr +; +;------------------------- +align 4 +TCP_usrclosed: + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + pop ebx + ret + + .last_ack: + mov [eax + TCP_SOCKET.t_state], TCPS_LAST_ACK + pop ebx + ret + + .disc: + call SOCKET_is_disconnected + .ret: + pop ebx + ret + + +;------------------------- +; +; TCP_connect +; +; IN: eax = socket ptr +; OUT: eax = 0 ok / -1 error +; ebx = error code +; +;------------------------- +align 4 +TCP_connect: + + test [eax + SOCKET.state], SS_ISCONNECTED + jnz .eisconn + + push eax edx + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + pop edx eax + +; Fill in local IP + cmp [eax + IP_SOCKET.LocalIP], 0 + jne @f + push [IP_LIST + 4] ; FIXME: use correct local IP + pop [eax + IP_SOCKET.LocalIP] + +; Fill in remote port and IP + pushw [edx + 2] + pop [eax + TCP_SOCKET.RemotePort] + + pushd [edx + 4] + pop [eax + IP_SOCKET.RemoteIP] + +; Find a local port, if user didnt define one + cmp [eax + TCP_SOCKET.LocalPort], 0 + jne @f + call SOCKET_find_port + @@: + +; Start the TCP sequence + 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, eax + lea eax, [ebx + STREAM_SOCKET.snd] + call SOCKET_ring_create + test eax, eax + jz .nomem + + lea eax, [ebx + STREAM_SOCKET.rcv] + call SOCKET_ring_create + test eax, eax + jz .nomem + + push ebx + lea ecx, [ebx + SOCKET.mutex] + call mutex_unlock + pop eax + + call SOCKET_is_connecting + +; Now send the SYN packet to remote end + push eax + call TCP_output + pop eax + + .block: + test [eax + SOCKET.options], SO_NONBLOCK + jz .waitforit + + xor eax, eax + dec eax + mov ebx, EINPROGRESS + ret + + .nomem: + xor eax, eax + dec eax + mov ebx, ENOMEM + ret + + .eisconn: + xor eax, eax + dec eax + mov ebx, EISCONN + ret + + .waitforit: + push eax + stdcall timer_hs, TCP_time_connect, 0, .timeout, eax + pop ebx + mov [ebx + TCP_SOCKET.timer_connect], eax + mov eax, ebx + + .loop: + cmp [eax + SOCKET.errorcode], 0 + jne .fail + cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED + je .established + + call SOCKET_block + jmp .loop + + .timeout: + mov eax, [esp+4] + mov [eax + SOCKET.errorcode], ETIMEDOUT + and [eax + SOCKET.state], not SS_ISCONNECTING + call SOCKET_notify.unblock + ret 4 + + .fail: + mov ebx, [eax + SOCKET.errorcode] + mov [eax + SOCKET.errorcode], 0 ; Clear the error, we only need to send it to the caller once + xor eax, eax + dec eax + ret + + .established: + stdcall cancel_timer_hs, [eax + TCP_SOCKET.timer_connect] + + xor eax, eax + ret \ No newline at end of file diff --git a/kernel/branches/kolibri-process/network/udp.inc b/kernel/branches/kolibri-process/network/udp.inc new file mode 100644 index 000000000..127748f4a --- /dev/null +++ b/kernel/branches/kolibri-process/network/udp.inc @@ -0,0 +1,424 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. 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: 2995 $ + + +struct UDP_header + + SourcePort dw ? + DestinationPort dw ? + Length dw ? ; Length of (UDP Header + Data) + Checksum dw ? + +ends + + +uglobal +align 4 + + UDP_PACKETS_TX rd NET_DEVICES_MAX + UDP_PACKETS_RX rd NET_DEVICES_MAX + +endg + + +;----------------------------------------------------------------- +; +; UDP_init +; +; This function resets all UDP variables +; +;----------------------------------------------------------------- +macro UDP_init { + + xor eax, eax + mov edi, UDP_PACKETS_TX + mov ecx, 2*NET_DEVICES_MAX + rep stosd +} + + +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 :) + +} + + +;----------------------------------------------------------------- +; +; 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: + + DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: size=%u\n", ecx + + ; First validate, checksum + + neg [esi + UDP_header.Checksum] ; substract checksum from 0 + jz .no_checksum ; if checksum is zero, it is considered valid + + ; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct + + UDP_checksum (edi), (edi+4) + jnz .checksum_mismatch + + .no_checksum: + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + + pusha + mov ecx, socket_mutex + call mutex_lock + popa + + 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 eax, [eax + SOCKET.NextPtr] + or eax, eax + jz .dump_ + + cmp [eax + SOCKET.Domain], AF_INET4 + jne .next_socket + + cmp [eax + SOCKET.Protocol], IP_PROTO_UDP + jne .next_socket + + cmp [eax + UDP_SOCKET.LocalPort], dx + jne .next_socket + + DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: socket=%x\n", eax + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + ;;; TODO: when packet is processed, check more sockets! + +; 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! + + cmp [eax + UDP_SOCKET.RemotePort], 0 + je .updateport + + cmp [eax + UDP_SOCKET.RemotePort], cx + jne .dump + + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + popa + + .updatesock: + call NET_ptr_to_num4 + inc [UDP_PACKETS_RX + edi] + + movzx ecx, [esi + UDP_header.Length] + sub ecx, sizeof.UDP_header + add esi, sizeof.UDP_header + + jmp SOCKET_input + + .updateport: + pusha + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "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 + jmp .updatesock + + .dump_: + + pusha + mov ecx, socket_mutex + call mutex_unlock + popa + + DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: no socket found\n" + + jmp .dump + + .checksum_mismatch: + DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: checksum mismatch\n" + + .dump: + call NET_packet_free + add esp, 4 ; pop (balance stack) + DEBUGF DEBUG_NETWORK_VERBOSE,"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 DEBUG_NETWORK_VERBOSE, "UDP_output: socket=%x bytes=%u data_ptr=%x\n", eax, ecx, esi + + mov dx, [eax + UDP_SOCKET.RemotePort] + DEBUGF DEBUG_NETWORK_VERBOSE, "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 DEBUG_NETWORK_VERBOSE, "local port=%x\n", dx + + sub esp, 8 ; Data ptr and data size will be placed here + push edx esi + 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 DEBUG_NETWORK_VERBOSE, "UDP_output: sending with device %x\n", ebx + call [ebx + NET_DEVICE.transmit] + test eax, eax + jnz @f + call NET_ptr_to_num4 + inc [UDP_PACKETS_TX + edi] + @@: + + ret + + .fail: + DEBUGF DEBUG_NETWORK_ERROR, "UDP_output: failed\n" + add esp, 4+4+8 + or eax, -1 + ret + + + + +;----------------------------------------------------------------- +; +; UDP_connect +; +; IN: eax = socket pointer +; OUT: eax = 0 ok / -1 error +; ebx = error code +; +;------------------------- +align 4 +UDP_connect: + + test [eax + SOCKET.state], SS_ISCONNECTED + jz @f + call UDP_disconnect + @@: + + push eax edx + lea ecx, [eax + SOCKET.mutex] + call mutex_lock + pop edx eax + +; Fill in local IP + cmp [eax + IP_SOCKET.LocalIP], 0 + jne @f + push [IP_LIST + 4] ; FIXME: use correct local IP + pop [eax + IP_SOCKET.LocalIP] + +; Fill in remote port and IP, overwriting eventually previous values + pushw [edx + 2] + pop [eax + UDP_SOCKET.RemotePort] + + pushd [edx + 4] + pop [eax + IP_SOCKET.RemoteIP] + +; Find a local port, if user didnt define one + cmp [eax + UDP_SOCKET.LocalPort], 0 + jne @f + call SOCKET_find_port + @@: + + push eax + init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue + pop eax + + push eax + lea ecx, [eax + SOCKET.mutex] + call mutex_unlock + pop eax + + call SOCKET_is_connected + + xor eax, eax + ret + + +;----------------------------------------------------------------- +; +; UDP_disconnect +; +; IN: eax = socket pointer +; OUT: eax = socket pointer +; +;------------------------- +align 4 +UDP_disconnect: + + ; TODO: remove the pending received data + + call SOCKET_is_disconnected + + 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-process/proc32.inc b/kernel/branches/kolibri-process/proc32.inc new file mode 100644 index 000000000..f94cb9c7a --- /dev/null +++ b/kernel/branches/kolibri-process/proc32.inc @@ -0,0 +1,277 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; Macroinstructions for defining and calling procedures + +macro stdcall proc,[arg] ; directly call STDCALL procedure + { common + if ~ arg eq + reverse + pushd arg + common + end if + call proc } + +macro invoke proc,[arg] ; indirectly call STDCALL procedure + { common + if ~ arg eq + reverse + pushd arg + common + end if + call [proc] } + +macro ccall proc,[arg] ; directly call CDECL procedure + { common + size@ccall = 0 + if ~ arg eq + reverse + pushd arg + size@ccall = size@ccall+4 + common + end if + call proc + if size@ccall + add esp, size@ccall + end if } + +macro cinvoke proc,[arg] ; indirectly call CDECL procedure + { common + size@ccall = 0 + if ~ arg eq + reverse + pushd arg + size@ccall = size@ccall+4 + common + end if + call [proc] + if size@ccall + add esp, size@ccall + end if } + +macro proc [args] ; define procedure + { common + match name params, args> + \{ define@proc name, \{ prologue name,flag,parmbytes,localbytes,reglist \} + macro locals + \{ virtual at ebp-localbytes+current + macro label . \\{ deflocal@proc .,:, \\} + struc db [val] \\{ \common deflocal@proc .,db,val \\} + struc dw [val] \\{ \common deflocal@proc .,dw,val \\} + struc dp [val] \\{ \common deflocal@proc .,dp,val \\} + struc dd [val] \\{ \common deflocal@proc .,dd,val \\} + struc dt [val] \\{ \common deflocal@proc .,dt,val \\} + struc dq [val] \\{ \common deflocal@proc .,dq,val \\} + struc rb cnt \\{ deflocal@proc .,rb cnt, \\} + struc rw cnt \\{ deflocal@proc .,rw cnt, \\} + struc rp cnt \\{ deflocal@proc .,rp cnt, \\} + struc rd cnt \\{ deflocal@proc .,rd cnt, \\} + struc rt cnt \\{ deflocal@proc .,rt cnt, \\} + struc rq cnt \\{ deflocal@proc .,rq cnt, \\} \} + macro endl + \{ purge label + restruc db,dw,dp,dd,dt,dq + restruc rb,rw,rp,rd,rt,rq + restruc byte,word,dword,pword,tword,qword + current = $-(ebp-localbytes) + end virtual \} + macro ret operand + \{ match any, operand \\{ retn operand \\} + match , operand \\{ match epilogue:reglist, epilogue@proc: + \\\{ epilogue name,flag,parmbytes,localbytes,reglist \\\} \\} \} + macro finish@proc \{ localbytes = (((current-1) shr 2)+1) shl 2 + end if \} } + +macro defargs@proc [arg] + { common + if ~ arg eq + forward + local ..arg,current@arg + match argname:type, arg + \{ current@arg equ argname + label ..arg type + argname equ ..arg + if dqword eq type + dd ?,?,?,? + else if tbyte eq type + dd ?,?,? + else if qword eq type | pword eq type + dd ?,? + else + dd ? + end if \} + match =current@arg,current@arg + \{ current@arg equ arg + arg equ ..arg + ..arg dd ? \} + common + args@proc equ current@arg + forward + restore current@arg + common + end if } + +macro deflocal@proc name,def,[val] + { common + match vars, all@vars \{ all@vars equ all@vars, \} + all@vars equ all@vars name + forward + local ..var,..tmp + ..var def val + match =?, val \{ ..tmp equ \} + match any =dup (=?), val \{ ..tmp equ \} + match tmp : value, ..tmp : val + \{ tmp: end virtual + initlocal@proc ..var,def value + virtual at tmp\} + common + match first rest, ..var, \{ name equ first \} } + +macro initlocal@proc name,def + { virtual at name + def + size@initlocal = $ - name + end virtual + position@initlocal = 0 + while size@initlocal > position@initlocal + virtual at name + def + if size@initlocal - position@initlocal < 2 + current@initlocal = 1 + load byte@initlocal byte from name+position@initlocal + else if size@initlocal - position@initlocal < 4 + current@initlocal = 2 + load word@initlocal word from name+position@initlocal + else + current@initlocal = 4 + load dword@initlocal dword from name+position@initlocal + end if + end virtual + if current@initlocal = 1 + mov byte [name+position@initlocal], byte@initlocal + else if current@initlocal = 2 + mov word [name+position@initlocal], word@initlocal + else + mov dword [name+position@initlocal], dword@initlocal + end if + position@initlocal = position@initlocal + current@initlocal + end while } + +macro endp + { purge ret,locals,endl + finish@proc + purge finish@proc + restore regs@proc + match all,args@proc \{ restore all \} + restore args@proc + match all,all@vars \{ restore all \} } + +macro local [var] + { common + locals + forward done@local equ + match varname[count]:vartype, var + \{ match =BYTE, vartype \\{ varname rb count + restore done@local \\} + match =WORD, vartype \\{ varname rw count + restore done@local \\} + match =DWORD, vartype \\{ varname rd count + restore done@local \\} + match =PWORD, vartype \\{ varname rp count + restore done@local \\} + match =QWORD, vartype \\{ varname rq count + restore done@local \\} + match =TBYTE, vartype \\{ varname rt count + restore done@local \\} + match =DQWORD, vartype \\{ label varname dqword + rq count+count + restore done@local \\} + match , done@local \\{ virtual + varname vartype + end virtual + rb count*sizeof.\#vartype + restore done@local \\} \} + match :varname:vartype, done@local:var + \{ match =BYTE, vartype \\{ varname db ? + restore done@local \\} + match =WORD, vartype \\{ varname dw ? + restore done@local \\} + match =DWORD, vartype \\{ varname dd ? + restore done@local \\} + match =PWORD, vartype \\{ varname dp ? + restore done@local \\} + match =QWORD, vartype \\{ varname dq ? + restore done@local \\} + match =TBYTE, vartype \\{ varname dt ? + restore done@local \\} + match =DQWORD, vartype \\{ label varname dqword + dq ?,? + restore done@local \\} + match , done@local \\{ varname vartype + restore done@local \\} \} + match ,done@local + \{ var + restore done@local \} + common + endl } diff --git a/kernel/branches/kolibri-process/sound/playnote.inc b/kernel/branches/kolibri-process/sound/playnote.inc new file mode 100644 index 000000000..f650cbd02 --- /dev/null +++ b/kernel/branches/kolibri-process/sound/playnote.inc @@ -0,0 +1,166 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; PLAYNOTE.INC version 1.1 22 November 2003 ;; +;; ;; +;; Player Notes for Speaker PC ;; +;; subfunction #55 from function #55 Menuet OS ;; +;; ;; +;; Copyright 2003 VaStaNi ;; +;; vastani@ukr.net ;; +;; >>>- SIMPLY - QUICKLY - SHORTLY -<<< ;; +;; ;; +;; Note: playnote.txt ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + +align 4 +sound_interface: + + cmp eax, ebx ; this is subfunction #55 ? + jne retFunc55 ; if no then return. + + cmp byte [sound_flag], 0 + jne retFunc55 + + movzx eax, byte [countDelayNote] + or al, al ; player is busy ? + jnz retFunc55 ; return counter delay Note + + mov [memAdrNote], esi;edx + call get_pid + mov [pidProcessNote], eax + xor eax, eax ; Ok! EAX = 0 + retFunc55: + mov [esp+32], eax ; return value EAX for application + ret + +iglobal +align 4 + kontrOctave dw 0x4742, 0x4342, 0x3F7C, 0x3BEC, 0x388F, 0x3562 + dw 0x3264, 0x2F8F, 0x2CE4, 0x2A5F, 0x2802, 0x25BF + memAdrNote dd 0 + pidProcessNote dd 0 + slotProcessNote dd 0 + count_timer_Note dd 1 + mem8253r42 dw 0 + countDelayNote db 0 +endg + +playNote: +; jmp NotPlayNotes + mov esi, [memAdrNote] + or esi, esi ; ESI = 0 ? - OFF Notes Play ? + jz NotPlayNotes ; if ESI = 0 -> ignore play pocedure + cmp eax, [count_timer_Note] + jb NotPlayNotes + push eax + inc eax + mov [count_timer_Note], eax + mov al, [countDelayNote] + dec al ; decrement counter Delay for Playing Note + jz NewLoadNote@Delay + cmp al, 0xFF ; this is first Note Play ? + jne NextDelayNote + ;This is FIRST Note, save counter channel 2 chip 8253 + mov al, 0xB6 ; control byte to timer chip 8253 + out 0x43, al ; Send it to the control port chip 8253 + in al, 0x42 ; Read Lower byte counter channel 2 chip 8253 + mov ah, al ; AH = Lower byte counter channel 2 + in al, 0x42 ; Read Upper byte counter channel 2 chip 8253 + mov [mem8253r42], ax ; Save counter channel 2 timer chip 8253 + NewLoadNote@Delay: + cld +; lodsb ; load AL - counter Delay + call ReadNoteByte + or al, al ; THE END ? + jz EndPlayNote + cmp al, 0x81 + jnc NoteforOctave + mov [countDelayNote], al +; lodsw ; load AX - counter for Note! + call ReadNoteByte + mov ah, al + call ReadNoteByte + xchg al, ah + jmp pokeNote + + EndPlayNote: ; THE END Play Notes! + in al, 0x61 ; Get contents of system port B chip 8255 + and al, 0xFC ; Turn OFF timer and speaker + out 0x61, al ; Send out new values to port B chip 8255 + mov ax, [mem8253r42] ; memorize counter channel 2 timer chip 8253 + xchg al, ah ; reverse byte in word + out 0x42, al ; restore Lower byte counter channel 2 + mov al, ah ; AL = Upper byte counter channel 2 + out 0x42, al ; restore Upper byte channel 2 + xor eax, eax ; EAX = 0 + mov [memAdrNote], eax; clear header control Delay-Note string + NextDelayNote: + mov [countDelayNote], al; save new counter delay Note + pop eax + NotPlayNotes: + RET + + NoteforOctave: + sub al, 0x81 ; correction value for delay Note + mov [countDelayNote], al; save counter delay this new Note +; lodsb ; load pack control code + call ReadNoteByte + cmp al, 0xFF ; this is PAUSE ? + jne packCode ; no, this is PACK CODE + in al, 0x61 ; Get contents of system port B chip 8255 + and al, 0xFC ; Turn OFF timer and speaker + out 0x61, al ; Send out new values to port B chip 8255 + jmp saveESI + + packCode: + mov cl, al ; save code + and al, 0xF ; clear upper bits + dec al ; correction + add al, al ; transform number to offset constant + movsx eax, al ; EAX - offset + add eax, dword kontrOctave; EAX - address from constant + mov ax, [eax] ; read constant + shr cl, 4 ; transform for number Octave + shr ax, cl ; calculate from Note this Octave! + pokeNote: + out 0x42, al ; Lower byte Out to channel 2 timer chip 8253 + mov al, ah + out 0x42, al ; Upper byte Out to channel 2 timer chip 8253 + in al, 0x61 ; Get contents of system port B chip 8255 + or al, 3 ; Turn ON timer and speaker + out 0x61, al ; Send out new values to port B chip 8255 + saveESI: +; mov [memAdrNote], esi ; save new header control Delay-Note string + pop eax + RET +ReadNoteByte: +;result: +; al - note + push eax + push ecx + push edx + push esi + + mov eax, [pidProcessNote] + call pid_to_slot + test eax, eax + jz .failed + lea ecx, [esp+12] + mov edx, 1 + mov esi, [memAdrNote] + inc [memAdrNote] + + call read_process_memory +.failed: + pop esi + pop edx + pop ecx + pop eax + ret +;------------------- END CODE ------------------- diff --git a/kernel/branches/kolibri-process/struct.inc b/kernel/branches/kolibri-process/struct.inc new file mode 100644 index 000000000..37f0b65b7 --- /dev/null +++ b/kernel/branches/kolibri-process/struct.inc @@ -0,0 +1,240 @@ + +; Macroinstructions for defining data structures + +macro struct name + { virtual at 0 + fields@struct equ name + match child parent, name \{ fields@struct equ child,fields@\#parent \} + sub@struct equ + struc db [val] \{ \common define field@struct .,db, + fields@struct equ fields@struct,field@struct \} + struc dw [val] \{ \common define field@struct .,dw, + fields@struct equ fields@struct,field@struct \} + struc du [val] \{ \common define field@struct .,du, + fields@struct equ fields@struct,field@struct \} + struc dd [val] \{ \common define field@struct .,dd, + fields@struct equ fields@struct,field@struct \} + struc dp [val] \{ \common define field@struct .,dp, + fields@struct equ fields@struct,field@struct \} + struc dq [val] \{ \common define field@struct .,dq, + fields@struct equ fields@struct,field@struct \} + struc dt [val] \{ \common define field@struct .,dt, + fields@struct equ fields@struct,field@struct \} + struc rb count \{ define field@struct .,db,count dup (?) + fields@struct equ fields@struct,field@struct \} + struc rw count \{ define field@struct .,dw,count dup (?) + fields@struct equ fields@struct,field@struct \} + struc rd count \{ define field@struct .,dd,count dup (?) + fields@struct equ fields@struct,field@struct \} + struc rp count \{ define field@struct .,dp,count dup (?) + fields@struct equ fields@struct,field@struct \} + struc rq count \{ define field@struct .,dq,count dup (?) + fields@struct equ fields@struct,field@struct \} + struc rt count \{ define field@struct .,dt,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro db [val] \{ \common \local anonymous + define field@struct anonymous,db, + fields@struct equ fields@struct,field@struct \} + macro dw [val] \{ \common \local anonymous + define field@struct anonymous,dw, + fields@struct equ fields@struct,field@struct \} + macro du [val] \{ \common \local anonymous + define field@struct anonymous,du, + fields@struct equ fields@struct,field@struct \} + macro dd [val] \{ \common \local anonymous + define field@struct anonymous,dd, + fields@struct equ fields@struct,field@struct \} + macro dp [val] \{ \common \local anonymous + define field@struct anonymous,dp, + fields@struct equ fields@struct,field@struct \} + macro dq [val] \{ \common \local anonymous + define field@struct anonymous,dq, + fields@struct equ fields@struct,field@struct \} + macro dt [val] \{ \common \local anonymous + define field@struct anonymous,dt, + fields@struct equ fields@struct,field@struct \} + macro rb count \{ \local anonymous + define field@struct anonymous,db,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro rw count \{ \local anonymous + define field@struct anonymous,dw,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro rd count \{ \local anonymous + define field@struct anonymous,dd,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro rp count \{ \local anonymous + define field@struct anonymous,dp,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro rq count \{ \local anonymous + define field@struct anonymous,dq,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro rt count \{ \local anonymous + define field@struct anonymous,dt,count dup (?) + fields@struct equ fields@struct,field@struct \} + macro union \{ fields@struct equ fields@struct,,union,< + sub@struct equ union \} + macro struct \{ fields@struct equ fields@struct,,substruct,< + sub@struct equ substruct \} } + +macro ends + { match , sub@struct \{ restruc db,dw,du,dd,dp,dq,dt + restruc rb,rw,rd,rp,rq,rt + purge db,dw,du,dd,dp,dq,dt + purge rb,rw,rd,rp,rq,rt + purge union,struct + match name tail,fields@struct, \\{ if $ + display 'Error: definition of ',\\`name,' contains illegal instructions.',0Dh,0Ah + err + end if \\} + match name=,fields,fields@struct \\{ fields@struct equ + make@struct name,fields + define fields@\\#name fields \\} + end virtual \} + match any, sub@struct \{ fields@struct equ fields@struct> \} + restore sub@struct } + +macro make@struct name,[field,type,def] + { common + local define + define equ name + forward + local sub + match , field \{ make@substruct type,name,sub def + define equ define,.,sub, \} + match any, field \{ define equ define,.#field,type, \} + common + match fields, define \{ define@struct fields \} } + +macro define@struct name,[field,type,def] + { common + virtual + db `name + load initial@struct byte from 0 + if initial@struct = '.' + display 'Error: name of structure should not begin with a dot.',0Dh,0Ah + err + end if + end virtual + local list + list equ + forward + if ~ field eq . + name#field type def + sizeof.#name#field = $ - name#field + else + label name#.#type + rb sizeof.#type + end if + local value + match any, list \{ list equ list, \} + list equ list + common + sizeof.#name = $ + restruc name + match values, list \{ + struc name value \\{ \\local \\..base + match any, fields@struct \\\{ fields@struct equ fields@struct,.,name, \\\} + match , fields@struct \\\{ label \\..base + forward + match , value \\\\{ field type def \\\\} + match any, value \\\\{ field type value + if ~ field eq . + rb sizeof.#name#field - ($-field) + end if \\\\} + common label . at \\..base \\\} + \\} + macro name value \\{ + match any, fields@struct \\\{ \\\local anonymous + fields@struct equ fields@struct,anonymous,name, \\\} + match , fields@struct \\\{ + forward + match , value \\\\{ type def \\\\} + match any, value \\\\{ \\\\local ..field + ..field = $ + type value + if ~ field eq . + rb sizeof.#name#field - ($-..field) + end if \\\\} + common \\\} \\} \} } + +macro enable@substruct + { macro make@substruct substruct,parent,name,[field,type,def] + \{ \common + \local define + define equ parent,name + \forward + \local sub + match , field \\{ match any, type \\\{ enable@substruct + make@substruct type,parent,sub def + purge make@substruct + define equ define,.,sub, \\\} \\} + match any, field \\{ define equ define,.\#field,type, \\} + \common + match fields, define \\{ define@\#substruct fields \\} \} } + +enable@substruct + +macro define@union parent,name,[field,type,def] + { common + virtual at parent#.#name + forward + if ~ field eq . + virtual at parent#.#name + parent#field type def + sizeof.#parent#field = $ - parent#field + end virtual + if sizeof.#parent#field > $ - parent#.#name + rb sizeof.#parent#field - ($ - parent#.#name) + end if + else + virtual at parent#.#name + label parent#.#type + type def + end virtual + label name#.#type at parent#.#name + if sizeof.#type > $ - parent#.#name + rb sizeof.#type - ($ - parent#.#name) + end if + end if + common + sizeof.#name = $ - parent#.#name + end virtual + struc name [value] \{ \common + label .\#name + last@union equ + forward + match any, last@union \\{ virtual at .\#name + field type def + end virtual \\} + match , last@union \\{ match , value \\\{ field type def \\\} + match any, value \\\{ field type value \\\} \\} + last@union equ field + common rb sizeof.#name - ($ - .\#name) \} + macro name [value] \{ \common \local ..anonymous + ..anonymous name value \} } + +macro define@substruct parent,name,[field,type,def] + { common + virtual at parent#.#name + forward + if ~ field eq . + parent#field type def + sizeof.#parent#field = $ - parent#field + else + label parent#.#type + rb sizeof.#type + end if + common + sizeof.#name = $ - parent#.#name + end virtual + struc name value \{ + label .\#name + forward + match , value \\{ field type def \\} + match any, value \\{ field type value + if ~ field eq . + rb sizeof.#parent#field - ($-field) + end if \\} + common \} + macro name value \{ \local ..anonymous + ..anonymous name \} } diff --git a/kernel/branches/kolibri-process/sys.conf b/kernel/branches/kolibri-process/sys.conf new file mode 100644 index 000000000..64a0dc295 --- /dev/null +++ b/kernel/branches/kolibri-process/sys.conf @@ -0,0 +1,18 @@ +[path] +/rd/1=/sys +/rd/1/dll=/sys/lib + +[net] +active=1 +addr=192.168.1.2 +mask=255.255.255.0 +gate=192.168.1.1 + +[gui] +mouse_speed=1 +mouse_delay=0x00A + +[dev] +sb16=0x220 +sound_dma=1 +midibase=0x320 diff --git a/kernel/branches/kolibri-process/unpacker.inc b/kernel/branches/kolibri-process/unpacker.inc new file mode 100644 index 000000000..306d7b128 --- /dev/null +++ b/kernel/branches/kolibri-process/unpacker.inc @@ -0,0 +1,528 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +; void __stdcall unpack(void* packed_data, void* unpacked_data); +unpack: + pushad + mov esi, [esp+32+4] + mov edi, [esp+32+8] + mov eax, [esi+8] + and al, 0xC0 + cmp al, 0xC0 + jz .failed + mov eax, [esi+8] + push eax + add esi, 12 + and al, not 0xC0 + dec al + jz .lzma +.failed: + pop eax + popad + ret 8 +.lzma: + call .lzma_unpack +.common: + pop eax + test al, 0x80 + jnz .ctr1 + test al, 0x40 + jz .ok + lodsd + mov ecx, eax + jecxz .ok + mov dl, [esi] + mov esi, [esp+32+8] +.c1: + lodsb + sub al, 0E8h + cmp al, 1 + ja .c1 + cmp byte [esi], dl + jnz .c1 + lodsd +; "bswap eax" is not supported on i386 + shr ax, 8 + ror eax, 16 + xchg al, ah + sub eax, esi + add eax, [esp+32+8] + mov [esi-4], eax + loop .c1 +.ok: + popad + ret 8 +.ctr1: + lodsd + mov ecx, eax + jecxz .ok + mov dl, [esi] + mov esi, [esp+32+8] +.c2: + lodsb +@@: + cmp al, 0xF + jnz .f + lodsb + cmp al, 80h + jb @b + cmp al, 90h + jb @f +.f: + sub al, 0E8h + cmp al, 1 + ja .c2 +@@: + cmp byte [esi], dl + jnz .c2 + lodsd + shr ax, 8 + ror eax, 16 + xchg al, ah + sub eax, esi + add eax, [esp+32+8] + mov [esi-4], eax + loop .c2 + jmp .ok + +.lzma_unpack: + +.pb = 2 ; pos state bits +.lp = 0 ; literal pos state bits +.lc = 3 ; literal context bits +.posStateMask = ((1 shl .pb)-1) +.literalPosMask = ((1 shl .lp)-1) + +.kNumPosBitsMax = 4 +.kNumPosStatesMax = (1 shl .kNumPosBitsMax) + +.kLenNumLowBits = 3 +.kLenNumLowSymbols = (1 shl .kLenNumLowBits) +.kLenNumMidBits = 3 +.kLenNumMidSymbols = (1 shl .kLenNumMidBits) +.kLenNumHighBits = 8 +.kLenNumHighSymbols = (1 shl .kLenNumHighBits) + +.LenChoice = 0 +.LenChoice2 = 1 +.LenLow = 2 +.LenMid = (.LenLow + (.kNumPosStatesMax shl .kLenNumLowBits)) +.LenHigh = (.LenMid + (.kNumPosStatesMax shl .kLenNumMidBits)) +.kNumLenProbs = (.LenHigh + .kLenNumHighSymbols) + +.kNumStates = 12 +.kNumLitStates = 7 +.kStartPosModelIndex = 4 +.kEndPosModelIndex = 14 +.kNumFullDistances = (1 shl (.kEndPosModelIndex/2)) +.kNumPosSlotBits = 6 +.kNumLenToPosStates = 4 +.kNumAlignBits = 4 +.kAlignTableSize = (1 shl .kNumAlignBits) +.kMatchMinLen = 2 + +.IsMatch = 0 +.IsRep = (.IsMatch + (.kNumStates shl .kNumPosBitsMax)) +.IsRepG0 = (.IsRep + .kNumStates) +.IsRepG1 = (.IsRepG0 + .kNumStates) +.IsRepG2 = (.IsRepG1 + .kNumStates) +.IsRep0Long = (.IsRepG2 + .kNumStates) +.PosSlot = (.IsRep0Long + (.kNumStates shl .kNumPosBitsMax)) +.SpecPos = (.PosSlot + (.kNumLenToPosStates shl .kNumPosSlotBits)) +.Align_ = (.SpecPos + .kNumFullDistances - .kEndPosModelIndex) +.Lencoder = (.Align_ + .kAlignTableSize) +.RepLencoder = (.Lencoder + .kNumLenProbs) +.Literal = (.RepLencoder + .kNumLenProbs) + +.LZMA_BASE_SIZE = 1846 ; must be ==Literal +.LZMA_LIT_SIZE = 768 + +.kNumTopBits = 24 +.kTopValue = (1 shl .kNumTopBits) + +.kNumBitModelTotalBits = 11 +.kBitModelTotal = (1 shl .kNumBitModelTotalBits) +.kNumMoveBits = 5 + + push edi +; int state=0; + xor ebx, ebx + mov [.previousByte], bl +; unsigned rep0=1,rep1=1,rep2=1,rep3=1; + mov eax, 1 + mov edi, .rep0 + stosd + stosd + stosd + stosd +; int len=0; +; result=0; + mov ecx, .Literal + (.LZMA_LIT_SIZE shl (.lc+.lp)) + mov eax, .kBitModelTotal/2 + mov edi, [.p] + rep stosd +; RangeDecoderInit +; rd->ExtraBytes = 0 +; rd->Buffer = stream +; rd->BufferLim = stream+bufferSize +; rd->Range = 0xFFFFFFFF + pop edi + mov ebp, [esi-8] ; dest_length + add ebp, edi ; ebp = destination limit + lodsd +; rd->code_ = eax + mov [.code_], eax + or [.range], -1 +.main_loop: + cmp edi, ebp + jae .main_loop_done + mov edx, edi + and edx, .posStateMask + mov eax, ebx + shl eax, .kNumPosBitsMax+2 + lea eax, [.IsMatch*4 + eax + edx*4] + add eax, [.p] + call .RangeDecoderBitDecode + jc .1 + movzx eax, [.previousByte] +if .literalPosMask + mov ah, dl + and ah, .literalPosMask +end if + shr eax, 8-.lc + imul eax, .LZMA_LIT_SIZE*4 + add eax, .Literal*4 + add eax, [.p] + cmp ebx, .kNumLitStates + jb .literal + xor edx, edx + sub edx, [.rep0] + mov dl, [edi + edx] + call .LzmaLiteralDecodeMatch + jmp @f +.literal: + call .LzmaLiteralDecode +@@: + mov [.previousByte], al + stosb + mov al, bl + cmp bl, 4 + jb @f + mov al, 3 + cmp bl, 10 + jb @f + mov al, 6 +@@: + sub bl, al + jmp .main_loop +.1: + lea eax, [.IsRep*4 + ebx*4] + add eax, [.p] + call .RangeDecoderBitDecode + jnc .10 + lea eax, [.IsRepG0*4 + ebx*4] + add eax, [.p] + call .RangeDecoderBitDecode + jc .111 + mov eax, ebx + shl eax, .kNumPosBitsMax+2 + lea eax, [.IsRep0Long*4 + eax + edx*4] + add eax, [.p] + call .RangeDecoderBitDecode + jc .1101 + cmp bl, 7 + setae bl + lea ebx, [9 + ebx + ebx] + xor edx, edx + sub edx, [.rep0] + mov al, [edi + edx] + stosb + mov [.previousByte], al + jmp .main_loop +.111: + lea eax, [.IsRepG1*4 + ebx*4] + add eax, [.p] + call .RangeDecoderBitDecode + mov eax, [.rep1] + jnc .l3 +.l1: + lea eax, [.IsRepG2*4 + ebx*4] + add eax, [.p] + call .RangeDecoderBitDecode + mov eax, [.rep2] + jnc .l2 + xchg [.rep3], eax +.l2: + push [.rep1] + pop [.rep2] +.l3: + xchg eax, [.rep0] + mov [.rep1], eax +.1101: + mov eax, .RepLencoder*4 + add eax, [.p] + call .LzmaLenDecode + cmp bl, 7 + setc bl + adc bl, bl + xor bl, 3 + add bl, 8 + jmp .repmovsb +.10: + mov eax, [.rep0] + xchg eax, [.rep1] + xchg eax, [.rep2] + xchg eax, [.rep3] + cmp bl, 7 + setc bl + adc bl, bl + xor bl, 3 + add bl, 7 + mov eax, .Lencoder*4 + add eax, [.p] + call .LzmaLenDecode + mov eax, .kNumLenToPosStates-1 + cmp eax, ecx + jb @f + mov eax, ecx +@@: + push ecx + mov ecx, .kNumPosSlotBits + shl eax, cl + shl eax, 2 + add eax, .PosSlot*4 + add eax, [.p] + call .RangeDecoderBitTreeDecode + mov [.rep0], ecx + cmp ecx, .kStartPosModelIndex + jb .l6 + push ecx + mov eax, ecx + and eax, 1 + shr ecx, 1 + or eax, 2 + dec ecx + shl eax, cl + mov [.rep0], eax + pop edx + cmp edx, .kEndPosModelIndex + jae .l5 + sub eax, edx + shl eax, 2 + add eax, (.SpecPos - 1)*4 + add eax, [.p] + call .RangeDecoderReverseBitTreeDecode + add [.rep0], ecx + jmp .l6 +.l5: + sub ecx, .kNumAlignBits + call .RangeDecoderDecodeDirectBits + mov ecx, .kNumAlignBits + shl eax, cl + add [.rep0], eax + mov eax, .Align_*4 + add eax, [.p] + call .RangeDecoderReverseBitTreeDecode + add [.rep0], ecx +.l6: + pop ecx + inc [.rep0] + jz .main_loop_done +.repmovsb: + add ecx, .kMatchMinLen + push esi + mov esi, edi + sub esi, [.rep0] + rep movsb + pop esi + mov al, [edi-1] + mov [.previousByte], al + jmp .main_loop +.main_loop_done: + ret + +.RangeDecoderBitDecode: +; in: eax->prob +; out: CF=bit; destroys eax + push edx + mov edx, [.range] + shr edx, .kNumBitModelTotalBits + imul edx, [eax] + cmp [.code_], edx + jae .ae + mov [.range], edx + mov edx, .kBitModelTotal + sub edx, [eax] + shr edx, .kNumMoveBits + add [eax], edx + clc +.n: + lahf + cmp [.range], .kTopValue + jae @f + shl [.range], 8 + shl [.code_], 8 + lodsb + mov byte [.code_], al +@@: + sahf + pop edx + ret +.ae: + sub [.range], edx + sub [.code_], edx + mov edx, [eax] + shr edx, .kNumMoveBits + sub [eax], edx + stc + jmp .n + +.RangeDecoderDecodeDirectBits: +; in: ecx=numTotalBits +; out: eax=result; destroys edx + xor eax, eax +.l: + shr [.range], 1 + shl eax, 1 + mov edx, [.code_] + sub edx, [.range] + jb @f + mov [.code_], edx + or eax, 1 +@@: + cmp [.range], .kTopValue + jae @f + shl [.range], 8 + shl [.code_], 8 + push eax + lodsb + mov byte [.code_], al + pop eax +@@: + loop .l + ret + +.LzmaLiteralDecode: +; in: eax->probs +; out: al=byte; destroys edx + push ecx + mov ecx, 1 +@@: + push eax + lea eax, [eax+ecx*4] + call .RangeDecoderBitDecode + pop eax + adc cl, cl + jnc @b +.LzmaLiteralDecode.ret: + mov al, cl + pop ecx + ret +.LzmaLiteralDecodeMatch: +; in: eax->probs, dl=matchByte +; out: al=byte; destroys edx + push ecx + mov ecx, 1 +.LzmaLiteralDecodeMatch.1: + add dl, dl + setc ch + push eax + lea eax, [eax+ecx*4+0x100*4] + call .RangeDecoderBitDecode + pop eax + adc cl, cl + jc .LzmaLiteralDecode.ret + xor ch, cl + test ch, 1 + mov ch, 0 + jnz @b + jmp .LzmaLiteralDecodeMatch.1 + +.LzmaLenDecode: +; in: eax->prob, edx=posState +; out: ecx=len + push eax + add eax, .LenChoice*4 + call .RangeDecoderBitDecode + pop eax + jnc .0 + push eax + add eax, .LenChoice2*4 + call .RangeDecoderBitDecode + pop eax + jc @f + mov ecx, .kLenNumMidBits + shl edx, cl + lea eax, [eax + .LenMid*4 + edx*4] + call .RangeDecoderBitTreeDecode + add ecx, .kLenNumLowSymbols + ret +@@: + add eax, .LenHigh*4 + mov ecx, .kLenNumHighBits + call .RangeDecoderBitTreeDecode + add ecx, .kLenNumLowSymbols + .kLenNumMidSymbols + ret +.0: + mov ecx, .kLenNumLowBits + shl edx, cl + lea eax, [eax + .LenLow*4 + edx*4] +.RangeDecoderBitTreeDecode: +; in: eax->probs,ecx=numLevels +; out: ecx=length; destroys edx + push ebx + mov edx, 1 + mov ebx, edx +@@: + push eax + lea eax, [eax+edx*4] + call .RangeDecoderBitDecode + pop eax + adc dl, dl + add bl, bl + loop @b + sub dl, bl + pop ebx + mov ecx, edx + ret +.RangeDecoderReverseBitTreeDecode: +; in: eax->probs,ecx=numLevels +; out: ecx=length; destroys edx + push ebx ecx + mov edx, 1 + xor ebx, ebx +@@: + push eax + lea eax, [eax+edx*4] + call .RangeDecoderBitDecode + lahf + adc edx, edx + sahf + rcr ebx, 1 + pop eax + loop @b + pop ecx + rol ebx, cl + mov ecx, ebx + pop ebx + ret + +uglobal +align 4 +;unpack.p rd unpack.LZMA_BASE_SIZE + (unpack.LZMA_LIT_SIZE shl (unpack.lc+unpack.lp)) +unpack.p dd ? +unpack.code_ dd ? +unpack.range dd ? +unpack.rep0 dd ? +unpack.rep1 dd ? +unpack.rep2 dd ? +unpack.rep3 dd ? +unpack.previousByte db ? +endg diff --git a/kernel/branches/kolibri-process/video/arrow.cur b/kernel/branches/kolibri-process/video/arrow.cur new file mode 100644 index 000000000..eb7ceebab Binary files /dev/null and b/kernel/branches/kolibri-process/video/arrow.cur differ diff --git a/kernel/branches/kolibri-process/video/arrow_clock.cur b/kernel/branches/kolibri-process/video/arrow_clock.cur new file mode 100644 index 000000000..1187c4899 Binary files /dev/null and b/kernel/branches/kolibri-process/video/arrow_clock.cur differ diff --git a/kernel/branches/kolibri-process/video/blitter.inc b/kernel/branches/kolibri-process/video/blitter.inc new file mode 100644 index 000000000..15223ee88 --- /dev/null +++ b/kernel/branches/kolibri-process/video/blitter.inc @@ -0,0 +1,429 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +struct BLITTER_BLOCK + xmin dd ? + ymin dd ? + xmax dd ? + ymax dd ? +ends + + +struct BLITTER + dc RECT + sc RECT + dst_x dd ? ; 32 + dst_y dd ? ; 36 + src_x dd ? ; 40 + src_y dd ? ; 44 + w dd ? ; 48 + h dd ? ; 52 + + bitmap dd ? ; 56 + stride dd ? ; 60 +ends + + + +align 4 +block_clip: +;esi= clip RECT ptr +;edi= RECT ptr +;return code: +;eax= 0 - draw, 1 - don't draw + + push ebx + + mov eax, [edi+RECT.left] + mov ebx, [edi+RECT.right] + mov ecx, [esi+RECT.left] ;clip.left + mov edx, [esi+RECT.right] ;clip.right + + cmp eax, edx ;left >= clip.right + jge .fail + + cmp ebx, ecx ;right < clip.left + jl .fail + + cmp eax, ecx ;left >= clip.left + jae @F + + mov eax, ecx +@@: + mov [edi+RECT.left], eax + + cmp ebx, edx ;right <= clip.right + jle @f + mov ebx, edx +@@: + mov [edi+RECT.right], ebx + + mov eax, [edi+RECT.top] + mov ebx, [edi+RECT.bottom] + mov ecx, [esi+RECT.top] ;clip.top + mov edx, [esi+RECT.bottom] ;clip.bottom + + cmp eax, edx ;top >= clip.bottom + jge .fail + + cmp ebx, ecx ;bottom < clip.top + jl .fail + + cmp eax, ecx ;top >= clip.top + jae @F + + mov eax, ecx +@@: + mov [edi+RECT.top], eax + + cmp ebx, edx ;bottom <= clip.bottom + jle @f + mov ebx, edx +@@: + mov [edi+RECT.bottom], ebx + pop ebx + xor eax, eax + ret +.fail: + pop ebx + mov eax, 1 + ret + + +align 4 +blit_clip: + +.sx0 equ 8 +.sy0 equ 12 +.sx1 equ 16 +.sy1 equ 20 + +.dx0 equ 24 +.dy0 equ 28 +.dx1 equ 32 +.dy1 equ 36 + + + push edi + push esi + push ebx + sub esp, 40 + + mov ebx, ecx + mov edx, [ecx+BLITTER.src_x] + mov [esp+.sx0], edx + mov eax, [ecx+BLITTER.src_y] + mov [esp+.sy0], eax + add edx, [ecx+BLITTER.w] + add eax, [ecx+BLITTER.h] + mov [esp+.sx1], edx + mov [esp+.sy1], eax + + lea edi, [esp+.sx0] + lea esi, [ebx+BLITTER.sc] + + call block_clip + test eax, eax + mov esi, 1 + jnz .done + + mov edi, [esp+.sx0] + mov edx, [ebx+BLITTER.dst_x] + add edx, edi + sub edx, [ebx+BLITTER.src_x] + mov [esp+.dx0], edx + + mov ecx, [esp+.sy0] + mov eax, [ebx+BLITTER.dst_y] + add eax, ecx + sub eax, [ebx+BLITTER.src_y] + mov [esp+.dy0], eax + + sub edx, edi + add edx, [esp+.sx1] + mov [esp+.dx1], edx + + sub eax, ecx + add eax, [esp+.sy1] + mov [esp+.dy1], eax + + lea edi, [esp+.dx0] + lea esi, [ebx+BLITTER.dc] + call block_clip + test eax, eax + mov esi, 1 + jnz .done + + mov edx, [esp+.dx0] + mov eax, [esp+.dx1] + sub eax, edx + mov [ebx+BLITTER.w], eax + + mov eax, [esp+.dy0] + mov ecx, [esp+.dy1] + sub ecx, eax + mov [ebx+BLITTER.h], ecx + + mov ecx, [ebx+BLITTER.src_x] + add ecx, edx + sub ecx, [ebx+BLITTER.dst_x] + mov [ebx+BLITTER.src_x], ecx + + mov ecx, [ebx+BLITTER.src_y] + add ecx, eax + sub ecx, [ebx+BLITTER.dst_y] + mov [ebx+BLITTER.src_y], ecx + mov [ebx+BLITTER.dst_x], edx + mov [ebx+BLITTER.dst_y], eax + xor esi, esi +.done: + mov eax, esi + add esp, 40 + pop ebx + pop esi + pop edi + + +purge .sx0 +purge .sy0 +purge .sx1 +purge .sy1 + +purge .dx0 +purge .dy0 +purge .dx1 +purge .dy1 + + ret + +align 4 +blit_32: + push ebp + push edi + push esi + push ebx + sub esp, 72 + + mov eax, [TASK_BASE] + mov ebx, [eax-twdw + WDATA.box.width] + mov edx, [eax-twdw + WDATA.box.height] + inc ebx + inc edx + + xor eax, eax + + mov [esp+BLITTER.dc.left], eax + mov [esp+BLITTER.dc.top], eax + mov [esp+BLITTER.dc.right], ebx + mov [esp+BLITTER.dc.bottom], edx + + mov [esp+BLITTER.sc.left], eax + mov [esp+BLITTER.sc.top], eax + mov eax, [ecx+24] + + mov [esp+BLITTER.sc.right], eax + mov eax, [ecx+28] + + mov [esp+BLITTER.sc.bottom], eax + + mov eax, [ecx] + mov [esp+BLITTER.dst_x], eax + mov eax, [ecx+4] + mov [esp+BLITTER.dst_y], eax + + mov eax, [ecx+16] + mov [esp+BLITTER.src_x], eax + mov eax, [ecx+20] + mov [esp+BLITTER.src_y], eax + mov eax, [ecx+8] + mov [esp+BLITTER.w], eax + mov eax, [ecx+12] + mov [esp+BLITTER.h], eax + + + mov eax, [ecx+32] + mov [esp+56], eax + mov eax, [ecx+36] + mov [esp+60], eax + + mov ecx, esp + call blit_clip + test eax, eax + jne .L57 + + mov eax, [TASK_BASE] + + mov ebx, [esp+BLITTER.dst_x] + mov ebp, [esp+BLITTER.dst_y] + add ebx, [eax-twdw + WDATA.box.left] + add ebp, [eax-twdw + WDATA.box.top] + + mov ecx, ebx + add ecx, [esp+BLITTER.w] + shl ecx, 16 + mov cx, bp + add ecx, [esp+BLITTER.h] + + mov edi, ebp + +; imul edi, [_display.pitch] + mov edi, [BPSLine_calc_area+edi*4] +; imul ebp, [_display.width] + mov ebp, [d_width_calc_area+ebp*4] + + add ebp, ebx + add ebp, [_WinMapAddress] + + mov eax, [esp+BLITTER.src_y] + imul eax, [esp+BLITTER.stride] + mov esi, [esp+BLITTER.src_x] + lea esi, [eax+esi*4] + add esi, [esp+BLITTER.bitmap] + + mov eax, ecx + mov ecx, [esp+BLITTER.h] + mov edx, [esp+BLITTER.w] + + test ecx, ecx ;FIXME check clipping + jz .L57 + + test edx, edx + jz .L57 + + cmp [_display.bpp], 32 + jne .core_24 + + lea edi, [edi+ebx*4] + + mov ebx, [CURRENT_TASK] +align 4 +.outer32: + xor ecx, ecx + +align 4 +.inner32: + cmp [ebp+ecx], bl + jne .skip +;-------------------------------------- + push eax + mov eax, [esi+ecx*4] + +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: + push ecx + + mov ecx, [esp+4] + ror ecx, 16 + sub ecx, edx + rol ecx, 16 + sub ecx, [esp+BLITTER.h + 8] + +; check mouse area for putpixel + call [_display.check_mouse] + pop ecx +;-------------------------------------- +align 4 +.no_mouseunder: +; store to real LFB + mov [LFB_BASE+edi+ecx*4], eax + pop eax +;-------------------------------------- +align 4 +.skip: + inc ecx + dec edx + jnz .inner32 + + add esi, [esp+BLITTER.stride] + add edi, [_display.pitch] + add ebp, [_display.width] + + mov edx, [esp+BLITTER.w] + dec [esp+BLITTER.h] + jnz .outer32 + +.done: +; call [draw_pointer] +; call __sys_draw_pointer +.L57: + add esp, 72 + pop ebx + pop esi + pop edi + pop ebp + ret + +.core_24: + lea ebx, [ebx+ebx*2] + lea edi, [LFB_BASE+edi+ebx] + mov ebx, [CURRENT_TASK] + +align 4 +.outer24: + mov [esp+64], edi + xor ecx, ecx + +align 4 +.inner24: + cmp [ebp+ecx], bl + jne .skip_1 +;-------------------------------------- + push eax + mov eax, [esi+ecx*4] + + lea edi, [edi+ecx*2] + +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder_1 +;-------------------------------------- +align 4 +@@: + push ecx + + mov ecx, [esp+4] + ror ecx, 16 + sub ecx, edx + rol ecx, 16 + sub ecx, [esp+BLITTER.h + 8] + +; check mouse area for putpixel + call [_display.check_mouse] + pop ecx +;-------------------------------------- +align 4 +.no_mouseunder_1: + mov [edi+ecx], ax + shr eax, 16 + mov [edi+ecx+2], al + + pop eax +;-------------------------------------- +align 4 +.skip_1: + mov edi, [esp+64] + inc ecx + dec edx + jnz .inner24 + + add esi, [esp+BLITTER.stride] + add edi, [_display.pitch] + add ebp, [_display.width] + + mov edx, [esp+BLITTER.w] + dec [esp+BLITTER.h] + jnz .outer24 + + jmp .done diff --git a/kernel/branches/kolibri-process/video/cursors.inc b/kernel/branches/kolibri-process/video/cursors.inc new file mode 100644 index 000000000..49d9b758c --- /dev/null +++ b/kernel/branches/kolibri-process/video/cursors.inc @@ -0,0 +1,1042 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 4181 $ + + +LOAD_FROM_FILE equ 0 +LOAD_FROM_MEM equ 1 +LOAD_INDIRECT equ 2 +LOAD_SYSTEM equ 3 + +struct BITMAPINFOHEADER + Size dd ? + Width dd ? + Height dd ? + Planes dw ? + BitCount dw ? + Compression dd ? + SizeImage dd ? + XPelsPerMeter dd ? + YPelsPerMeter dd ? + ClrUsed dd ? + ClrImportant dd ? +ends +;------------------------------------------------------------------------------ +align 4 +proc init_cursor stdcall, dst:dword, src:dword + locals + rBase dd ? + pQuad dd ? + pBits dd ? + pAnd dd ? + width dd ? + height dd ? + counter dd ? + endl + + mov esi, [src] + add esi, [esi+18] + mov eax, esi + + cmp [esi+BITMAPINFOHEADER.BitCount], 24 + je .img_24 + cmp [esi+BITMAPINFOHEADER.BitCount], 8 + je .img_8 + cmp [esi+BITMAPINFOHEADER.BitCount], 4 + je .img_4 +;-------------------------------------- +align 4 +.img_2: + add eax, [esi] + mov [pQuad], eax + add eax, 8 + mov [pBits], eax + add eax, 128 + mov [pAnd], eax + mov eax, [esi+4] + mov [width], eax + mov ebx, [esi+8] + shr ebx, 1 + mov [height], ebx + + mov edi, [dst] + add edi, 32*31*4 + mov [rBase], edi + + mov esi, [pQuad] +;-------------------------------------- +align 4 +.l21: + mov ebx, [pBits] + mov ebx, [ebx] + bswap ebx + mov eax, [pAnd] + mov eax, [eax] + bswap eax + mov [counter], 32 +;-------------------------------------- +align 4 +@@: + xor edx, edx + shl eax, 1 + setc dl + dec edx + + xor ecx, ecx + shl ebx, 1 + setc cl + mov ecx, [esi+ecx*4] + and ecx, edx + and edx, 0xFF000000 + or edx, ecx + mov [edi], edx + + add edi, 4 + dec [counter] + jnz @B + + add [pBits], 4 + add [pAnd], 4 + mov edi, [rBase] + sub edi, 128 + mov [rBase], edi + sub [height], 1 + jnz .l21 + ret +;-------------------------------------- +align 4 +.img_4: + add eax, [esi] + mov [pQuad], eax + add eax, 64 + mov [pBits], eax + add eax, 0x200 + mov [pAnd], eax + mov eax, [esi+4] + mov [width], eax + mov ebx, [esi+8] + shr ebx, 1 + mov [height], ebx + + mov edi, [dst] + add edi, 32*31*4 + mov [rBase], edi + + mov esi, [pQuad] + mov ebx, [pBits] +;-------------------------------------- +align 4 +.l4: + mov eax, [pAnd] + mov eax, [eax] + bswap eax + mov [counter], 16 +;-------------------------------------- +align 4 +@@: + xor edx, edx + shl eax, 1 + setc dl + dec edx + + movzx ecx, byte [ebx] + and cl, 0xF0 + shr ecx, 2 + mov ecx, [esi+ecx] + and ecx, edx + and edx, 0xFF000000 + or edx, ecx + mov [edi], edx + + xor edx, edx + shl eax, 1 + setc dl + dec edx + + movzx ecx, byte [ebx] + and cl, 0x0F + mov ecx, [esi+ecx*4] + and ecx, edx + and edx, 0xFF000000 + or edx, ecx + mov [edi+4], edx + + inc ebx + add edi, 8 + dec [counter] + jnz @B + + add [pAnd], 4 + mov edi, [rBase] + sub edi, 128 + mov [rBase], edi + sub [height], 1 + jnz .l4 + ret +;-------------------------------------- +align 4 +.img_8: + add eax, [esi] + mov [pQuad], eax + add eax, 1024 + mov [pBits], eax + add eax, 1024 + mov [pAnd], eax + mov eax, [esi+4] + mov [width], eax + mov ebx, [esi+8] + shr ebx, 1 + mov [height], ebx + + mov edi, [dst] + add edi, 32*31*4 + mov [rBase], edi + + mov esi, [pQuad] + mov ebx, [pBits] +;-------------------------------------- +align 4 +.l81: + mov eax, [pAnd] + mov eax, [eax] + bswap eax + mov [counter], 32 +;-------------------------------------- +align 4 +@@: + xor edx, edx + shl eax, 1 + setc dl + dec edx + + movzx ecx, byte [ebx] + mov ecx, [esi+ecx*4] + and ecx, edx + and edx, 0xFF000000 + or edx, ecx + mov [edi], edx + + inc ebx + add edi, 4 + dec [counter] + jnz @B + + add [pAnd], 4 + mov edi, [rBase] + sub edi, 128 + mov [rBase], edi + sub [height], 1 + jnz .l81 + ret +;-------------------------------------- +align 4 +.img_24: + add eax, [esi] + mov [pQuad], eax + add eax, 0xC00 + mov [pAnd], eax + mov eax, [esi+BITMAPINFOHEADER.Width] + mov [width], eax + mov ebx, [esi+BITMAPINFOHEADER.Height] + shr ebx, 1 + mov [height], ebx + + mov edi, [dst] + add edi, 32*31*4 + mov [rBase], edi + + mov esi, [pAnd] + mov ebx, [pQuad] +;-------------------------------------- +align 4 +.row_24: + mov eax, [esi] + bswap eax + mov [counter], 32 +;-------------------------------------- +align 4 +@@: + xor edx, edx + shl eax, 1 + setc dl + dec edx + + mov ecx, [ebx] + and ecx, 0x00FFFFFF + and ecx, edx + and edx, 0xFF000000 + or edx, ecx + mov [edi], edx + add ebx, 3 + add edi, 4 + dec [counter] + jnz @B + + add esi, 4 + mov edi, [rBase] + sub edi, 128 + mov [rBase], edi + sub [height], 1 + jnz .row_24 + ret +endp +;------------------------------------------------------------------------------ +align 4 +proc set_cursor stdcall, hcursor:dword + mov eax, [hcursor] + cmp [eax+CURSOR.magic], 'CURS' + jne .fail +; cmp [eax+CURSOR.size], CURSOR_SIZE +; jne .fail + mov ebx, [current_slot] + xchg eax, [ebx+APPDATA.cursor] + mov [redrawmouse_unconditional], 1 + call __sys_draw_pointer + ret +;-------------------------------------- +align 4 +.fail: + mov eax, [def_cursor] + mov ebx, [current_slot] + xchg eax, [ebx+APPDATA.cursor] + ret +endp +;------------------------------------------------------------------------------ +align 4 +; param +; eax= pid +; ebx= src +; ecx= flags + +create_cursor: +.src equ esp +.flags equ esp+4 +.hcursor equ esp+8 + + sub esp, 4 ;space for .hcursor + push ecx + push ebx + + mov ebx, eax + mov eax, sizeof.CURSOR + call create_kernel_object + test eax, eax + jz .fail + + mov [.hcursor], eax + + xor ebx, ebx + mov [eax+CURSOR.magic], 'CURS' + mov [eax+CURSOR.destroy], destroy_cursor + mov [eax+CURSOR.hot_x], ebx + mov [eax+CURSOR.hot_y], ebx + + stdcall kernel_alloc, 0x1000 + test eax, eax + jz .fail + + mov edi, [.hcursor] + mov [edi+CURSOR.base], eax + + mov esi, [.src] + mov ebx, [.flags] + cmp bx, LOAD_INDIRECT + je .indirect + + movzx ecx, word [esi+10] + movzx edx, word [esi+12] + mov [edi+CURSOR.hot_x], ecx + mov [edi+CURSOR.hot_y], edx + + stdcall init_cursor, eax, esi + +align 4 +.add_cursor: + mov ecx, [.hcursor] + lea ecx, [ecx+CURSOR.list_next] + lea edx, [_display.cr_list.next] + + pushfd + cli + list_add ecx, edx ;list_add_tail(new, head) + popfd + + mov eax, [.hcursor] + cmp [_display.init_cursor], 0 + je .fail + + push eax + call [_display.init_cursor] + add esp, 4 + + mov eax, [.hcursor] +;-------------------------------------- +align 4 +.fail: + add esp, 12 + ret +;-------------------------------------- +align 4 +.indirect: + shr ebx, 16 + movzx ecx, bh + movzx edx, bl + mov [edi+CURSOR.hot_x], ecx + mov [edi+CURSOR.hot_y], edx + + xchg edi, eax + mov ecx, 1024 + cld + rep movsd + jmp .add_cursor +;------------------------------------------------------------------------------ +align 4 +proc load_cursor stdcall, src:dword, flags:dword + locals + handle dd ? + endl + + xor eax, eax + cmp [create_cursor], eax + je .fail2 + + mov [handle], eax + cmp word [flags], LOAD_FROM_FILE + jne @F + + stdcall load_file, [src] + test eax, eax + jz .fail + mov [src], eax +;-------------------------------------- +align 4 +@@: + push ebx + push esi + push edi + + mov eax, [CURRENT_TASK] + shl eax, 5 + mov eax, [CURRENT_TASK+eax+4] + mov ebx, [src] + mov ecx, [flags] + call create_cursor ;eax, ebx, ecx + mov [handle], eax + + cmp word [flags], LOAD_FROM_FILE + jne .exit + stdcall kernel_free, [src] +;-------------------------------------- +align 4 +.exit: + pop edi + pop esi + pop ebx +;-------------------------------------- +align 4 +.fail: + mov eax, [handle] +;-------------------------------------- +align 4 +.fail2: + ret +endp +;------------------------------------------------------------------------------ +align 4 +proc delete_cursor stdcall, hcursor:dword + +; DEBUGF 1,'K : delete_cursor %x\n', [hcursor] + + mov esi, [hcursor] + + cmp [esi+CURSOR.magic], 'CURS' + jne .fail + + mov ebx, [CURRENT_TASK] + shl ebx, 5 + mov ebx, [CURRENT_TASK+ebx+4] + cmp ebx, [esi+CURSOR.pid] + jne .fail + + mov ebx, [current_slot] + cmp esi, [ebx+APPDATA.cursor] + jne @F + mov eax, [def_cursor] + mov [ebx+APPDATA.cursor], eax +;-------------------------------------- +align 4 +@@: + mov eax, [hcursor] + call [eax+APPOBJ.destroy] +;-------------------------------------- +align 4 +.fail: + ret +endp +;------------------------------------------------------------------------------ +align 4 +; param +; eax= cursor +destroy_cursor: + + push eax + stdcall kernel_free, [eax+CURSOR.base] + + mov eax, [esp] + lea eax, [eax+CURSOR.list_next] + + pushfd + cli + list_del eax + popfd + + pop eax + call destroy_kernel_object + ret +;------------------------------------------------------------------------------ +align 4 +select_cursor: + mov eax, [esp+4] + mov [_display.cursor], eax + ret 4 +;------------------------------------------------------------------------------ +align 4 +proc restore_24 stdcall, x:dword, y:dword + + push ebx + + mov ebx, [cur_saved_base] + mov edx, [cur.h] + test edx, edx + jz .ret + + push esi + push edi + + mov esi, cur_saved_data + mov ecx, [cur.w] + lea ecx, [ecx+ecx*2] + push ecx +;-------------------------------------- +align 4 +@@: + mov edi, ebx + add ebx, [_display.pitch] + + mov ecx, [esp] + rep movsb + dec edx + jnz @B + + pop ecx + pop edi + pop esi +;-------------------------------------- +align 4 +.ret: + pop ebx + ret +endp +;------------------------------------------------------------------------------ +align 4 +proc restore_32 stdcall, x:dword, y:dword + + push ebx + + mov ebx, [cur_saved_base] + mov edx, [cur.h] + test edx, edx + jz .ret + + push esi + push edi + + mov esi, cur_saved_data +;-------------------------------------- +align 4 +@@: + mov edi, ebx + add ebx, [_display.pitch] + + mov ecx, [cur.w] + rep movsd + dec edx + jnz @B + + pop edi +;-------------------------------------- +align 4 +.ret: + pop esi + pop ebx + ret +endp +;------------------------------------------------------------------------------ +align 4 +proc move_cursor_24 stdcall, hcursor:dword, x:dword, y:dword + locals + h dd ? + _dx dd ? + _dy dd ? + endl + + mov esi, [hcursor] + mov ecx, [x] + mov eax, [y] +; mov ebx, [BytesPerScanLine] + + xor edx, edx + sub ecx, [esi+CURSOR.hot_x] + lea ebx, [ecx+32-1] + mov [x], ecx + sets dl + dec edx + and ecx, edx ;clip x to 0<=x + mov [cur.left], ecx + mov edi, ecx + sub edi, [x] + mov [_dx], edi + + xor edx, edx + sub eax, [esi+CURSOR.hot_y] + lea edi, [eax+32-1] + mov [y], eax + sets dl + dec edx + and eax, edx ;clip y to 0<=y + mov [cur.top], eax + mov edx, eax + sub edx, [y] + mov [_dy], edx + +; mul dword [BytesPerScanLine] + mov eax, [BPSLine_calc_area+eax*4] + lea edx, [LFB_BASE+ecx*3] + add edx, eax + mov [cur_saved_base], edx + + cmp ebx, [Screen_Max_X] + jbe @F + mov ebx, [Screen_Max_X] +;-------------------------------------- +align 4 +@@: + cmp edi, [Screen_Max_Y] + jbe @F + mov edi, [Screen_Max_Y] +;-------------------------------------- +align 4 +@@: + mov [cur.right], ebx + mov [cur.bottom], edi + + sub ebx, [x] + sub edi, [y] + inc ebx + inc edi + sub ebx, [_dx] + sub edi, [_dy] + + mov [cur.w], ebx + mov [cur.h], edi + mov [h], edi + + mov eax, edi + mov edi, cur_saved_data +;-------------------------------------- +align 4 +@@: + mov esi, edx + add edx, [_display.pitch] + mov ecx, [cur.w] + lea ecx, [ecx+ecx*2] + rep movsb + dec eax + jnz @B + +;draw cursor + mov ebx, [cur_saved_base] + mov eax, [_dy] + shl eax, 5 + add eax, [_dx] + + mov esi, [hcursor] + mov esi, [esi+CURSOR.base] + lea edx, [esi+eax*4] +;-------------------------------------- +align 4 +.row: + mov ecx, [cur.w] + mov esi, edx + mov edi, ebx + add edx, 32*4 + add ebx, [_display.pitch] +;-------------------------------------- +align 4 +.pix: + lodsd + test eax, 0xFF000000 + jz @F + mov [edi], ax + shr eax, 16 + mov [edi+2], al +;-------------------------------------- +align 4 +@@: + add edi, 3 + dec ecx + jnz .pix + + dec [h] + jnz .row + ret +endp +;------------------------------------------------------------------------------ +align 4 +proc move_cursor_32 stdcall, hcursor:dword, x:dword, y:dword + locals + h dd ? + _dx dd ? + _dy dd ? + endl + + mov esi, [hcursor] + mov ecx, [x] + mov eax, [y] + + xor edx, edx + sub ecx, [esi+CURSOR.hot_x] + lea ebx, [ecx+32-1] + mov [x], ecx + sets dl + dec edx + and ecx, edx ;clip x to 0<=x + mov [cur.left], ecx + mov edi, ecx + sub edi, [x] + mov [_dx], edi + + xor edx, edx + sub eax, [esi+CURSOR.hot_y] + lea edi, [eax+32-1] + mov [y], eax + sets dl + dec edx + and eax, edx ;clip y to 0<=y + mov [cur.top], eax + mov edx, eax + sub edx, [y] + mov [_dy], edx + +; mul dword [BytesPerScanLine] + mov eax, [BPSLine_calc_area+eax*4] + lea edx, [LFB_BASE+eax+ecx*4] + mov [cur_saved_base], edx + + cmp ebx, [Screen_Max_X] + jbe @F + mov ebx, [Screen_Max_X] +;-------------------------------------- +align 4 +@@: + cmp edi, [Screen_Max_Y] + jbe @F + mov edi, [Screen_Max_Y] +;-------------------------------------- +align 4 +@@: + mov [cur.right], ebx + mov [cur.bottom], edi + + sub ebx, [x] + sub edi, [y] + inc ebx + inc edi + sub ebx, [_dx] + sub edi, [_dy] + + mov [cur.w], ebx + mov [cur.h], edi + mov [h], edi + + mov eax, edi + mov edi, cur_saved_data +;-------------------------------------- +align 4 +@@: + mov esi, edx + add edx, [_display.pitch] + mov ecx, [cur.w] + rep movsd + dec eax + jnz @B + +;draw cursor + mov ebx, [cur_saved_base] + mov eax, [_dy] + shl eax, 5 + add eax, [_dx] + + mov esi, [hcursor] + mov esi, [esi+CURSOR.base] + lea edx, [esi+eax*4] +;-------------------------------------- +align 4 +.row: + mov ecx, [cur.w] + mov esi, edx + mov edi, ebx + add edx, 32*4 + add ebx, [_display.pitch] +;-------------------------------------- +align 4 +.pix: + lodsd + test eax, 0xFF000000 + jz @F + mov [edi], eax +;-------------------------------------- +align 4 +@@: + add edi, 4 + dec ecx + jnz .pix + + dec [h] + jnz .row + ret +endp +;------------------------------------------------------------------------------ +align 4 +check_mouse_area_for_getpixel_new: +; in: +; eax = x +; ebx = y +; out: +; ecx = new color +;-------------------------------------- +; check for Y + cmp bx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area + + cmp bx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area +;-------------------------------------- +; check for X + cmp ax, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + cmp ax, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area +;-------------------------------------- + push eax ebx +; offset X + movzx ecx, word [X_UNDER_subtraction_CUR_hot_x] + sub eax, ecx ; x1 +; offset Y + movzx ecx, word [Y_UNDER_subtraction_CUR_hot_y] + sub ebx, ecx ; y1 +;-------------------------------------- +; ebx = offset y +; eax = offset x + imul ebx, [cur.w] ;y + add eax, ebx + mov ebx, eax + shl eax, 2 + cmp byte [_display.bpp], 32 + je @f + sub eax, ebx +;-------------------------------------- +align 4 +@@: + add eax, cur_saved_data + mov ecx, [eax] + and ecx, 0xffffff + add ecx, 0xff000000 + pop ebx eax + ret +;-------------------------------------- +align 4 +.no_mouse_area: + xor ecx, ecx + ret +;----------------------------------------------------------------------------- +align 4 +check_mouse_area_for_putpixel_new: +; in: +; ecx = x shl 16 + y +; eax = color +; out: +; eax = new color +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area + + rol ecx, 16 +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + ror ecx, 16 +;-------------------------------------- +align 4 +.1: + push eax +;-------------------------------------- +; ecx = (offset x) shl 16 + (offset y) + push ebx + mov ebx, ecx + shr ebx, 16 ; x + and ecx, 0xffff ; y + + cmp ecx, [cur.h] + jae @f + + cmp ebx, [cur.w] + jb .ok +;-------------------------------------- +align 4 +@@: +; DEBUGF 1, "K : SHIT HAPPENS: %x %x \n", ecx,ebx + pop ebx + jmp .sh ; SORRY! SHIT HAPPENS! +;-------------------------------------- +align 4 +.ok: +; ecx = offset y +; ebx = offset x + push ebx ecx + imul ecx, [cur.w] ;y + add ecx, ebx + mov ebx, ecx + shl ecx, 2 + cmp byte [_display.bpp], 24 + je .24 + and eax, 0xFFFFFF + mov [ecx + cur_saved_data], eax ;store new color to + jmp @f +;-------------------------------------- +align 4 +.24: + sub ecx, ebx + mov [ecx + cur_saved_data], ax ;store new color to + shr eax, 16 + mov [ecx + cur_saved_data + 2], al ;store new color to +;-------------------------------------- +align 4 +@@: + pop ecx ebx + + shl ecx, 5 + add ecx, ebx + + mov eax, [current_cursor] + mov eax, [eax+CURSOR.base] + lea eax, [eax+ecx*4] + mov eax, [eax] + + pop ebx + + test eax, 0xFF000000 + jz @f + + add esp, 4 + ret +;-------------------------------------- +align 4 +.sh: + mov ecx, -1 +;-------------------------------------- +align 4 +@@: + pop eax +;-------------------------------------- +align 4 +.no_mouse_area: + ret +;------------------------------------------------------------------------------ +align 4 +get_display: + mov eax, _display + ret +;------------------------------------------------------------------------------ +align 4 +init_display: + xor eax, eax + mov edi, _display + + mov [edi+display_t.init_cursor], eax + mov [edi+display_t.select_cursor], eax + mov [edi+display_t.show_cursor], eax + mov [edi+display_t.move_cursor], eax + mov [edi+display_t.restore_cursor], eax + + lea ecx, [edi+display_t.cr_list.next] + mov [edi+display_t.cr_list.next], ecx + mov [edi+display_t.cr_list.prev], ecx + + cmp [SCR_MODE], word 0x13 + jbe .fail + + test word [SCR_MODE], 0x4000 + jz .fail + + mov ebx, restore_32 + mov ecx, move_cursor_32 + mov edx, Vesa20_putpixel32_new + mov eax, [_display.bpp] + cmp al, 32 + jne .24 + +.set: + mov [_display.select_cursor], select_cursor + mov [_display.move_cursor], ecx + mov [_display.restore_cursor], ebx + mov [_display.check_mouse], check_mouse_area_for_putpixel_new + mov [_display.check_m_pixel], check_mouse_area_for_getpixel_new + + cmp [PUTPIXEL], dword VGA_putpixel + je @f + 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 + +.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 + mov [_display.move_cursor], eax + ret +;------------------------------------------------------------------------------ +align 4 +def_arrow: + file 'arrow.cur' +;------------------------------------------------------------------------------ +align 4 +clock_arrow: + file 'arrow_clock.cur' +;------------------------------------------------------------------------------ + diff --git a/kernel/branches/kolibri-process/video/vesa12.inc b/kernel/branches/kolibri-process/video/vesa12.inc new file mode 100644 index 000000000..3b0a69c47 --- /dev/null +++ b/kernel/branches/kolibri-process/video/vesa12.inc @@ -0,0 +1,1004 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; VESA12.INC ;; +;; ;; +;; Vesa 1.2 functions for MenuetOS ;; +;; ;; +;; Copyright 2002 Ville Turjanmaa ;; +;; ;; +;; quickcode@mail.ru - bankswitch for S3 cards ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2455 $ + + +TRIDENT equ 0 +S3_VIDEO equ 0 +INTEL_VIDEO equ 0 + +if TRIDENT + if S3_VIDEO or INTEL_VIDEO + stop + end if +end if + +if S3_VIDEO + if TRIDENT or INTEL_VIDEO + stop + end if +end if + +if INTEL_VIDEO + if S3_VIDEO or TRIDENT + stop + end if +end if + + +; A complete video driver should include the following types of function +; +; Putpixel +; Getpixel +; +; Drawimage +; Drawbar +; +; Drawbackground +; +; +; Modifying the set_bank -function is mostly enough +; for different Vesa 1.2 setups. + +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; set_bank for Trident videocards, work on Trident 9440 +; modified by Mario79 + +if TRIDENT +set_bank: + pushfd + cli + cmp al, [BANK_RW] + je .retsb + + mov [BANK_RW], al + push dx + mov dx, 3D8h + out dx, al + pop dx +.retsb: + popfd + ret +end if + +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +; set_bank for S3 videocards, work on S3 ViRGE PCI (325) +; modified by kmeaw + +if S3_VIDEO +set_bank: + pushfd + cli + cmp al, [BANK_RW] + je .retsb + + mov [BANK_RW], al + push ax + push dx + push cx + mov cl, al + mov dx, 0x3D4 + mov al, 0x38 + out dx, al ;CR38 Register Lock 1 ;Note: Traditionally 48h is used to + ;unlock and 00h to lock + inc dx + mov al, 0x48 + out dx, al ;3d5 -? + dec dx + mov al, 0x31 + out dx, al ;CR31 Memory Configuration Register +;0 Enable Base Address Offset (CPUA BASE). Enables bank operation if set, ;disables if clear. +;4-5 Bit 16-17 of the Display Start Address. For the 801/5,928 see index 51h, +;for the 864/964 see index 69h. + + inc dx + in al, dx + dec dx + mov ah, al + mov al, 0x31 + out dx, ax + mov al, ah + or al, 9 + inc dx + out dx, al + dec dx + mov al, 0x35 + out dx, al ;CR35 CRT Register Lock + inc dx + in al, dx + dec dx + and al, 0xF0 + mov ch, cl + and ch, 0x0F + or ch, al + mov al, 0x35 + out dx, al + inc dx + mov al, ch + out dx, ax + dec dx + mov al, 0x51 ;Extended System Control 2 Register + out dx, al + inc dx + in al, dx + dec dx + and al, 0xF3 + shr cl, 2 + and cl, 0x0C + or cl, al + mov al, 0x51 + out dx, al + inc dx + mov al, cl + out dx, al + dec dx + mov al, 0x38 + out dx, al + inc dx + xor al, al + out dx, al + dec dx + pop cx + pop dx + pop ax +.retsb: + popfd + ret +end if + +;Set bank function for Intel 810/815 chipsets +; *****Modified by Protopopius, Russia.***** +; ********* http://menuetos.hut.ru ************** +; ************************************************ + +if INTEL_VIDEO + +set_bank: + pushfd + cli + + cmp al, [BANK_RW] + je .retsb + + mov [BANK_RW], al + push ax + push dx + mov dx, 3CEh + mov ah, al ; Save value for later use + mov al, 10h ; Index GR10 (Address Mapping) + out dx, al ; Select GR10 + inc dl + mov al, 3 ; Set bits 0 and 1 (Enable linear page mapping) + out dx, al ; Write value + dec dl + mov al, 11h ; Index GR11 (Page Selector) + out dx, al ; Select GR11 + inc dl + mov al, ah ; Write address + out dx, al ; Write the value + pop dx + pop ax +.retsb: + popfd + ret +end if + +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!} + +if (TRIDENT or S3_VIDEO or INTEL_VIDEO) +else +set_bank: + pushfd + cli + + cmp al, [BANK_RW] + je .retsb + + mov [BANK_RW], al + push ax + push dx + mov ah, al + mov dx, 0x03D4 + mov al, 0x39 + out dx, al + inc dl + mov al, 0xA5 + out dx, al + dec dl + mov al, 6Ah + out dx, al + inc dl + mov al, ah + out dx, al + dec dl + mov al, 0x39 + out dx, al + inc dl + mov al, 0x5A + out dx, al + dec dl + pop dx + pop ax + +.retsb: + popfd + ret +end if + +vesa12_drawbackground: + + call [_display.disable_mouse] + + push eax + push ebx + push ecx + push edx + + xor edx, edx + mov eax, dword[BgrDataWidth] + mov ebx, dword[BgrDataHeight] + mul ebx + mov ebx, 3 + mul ebx + mov [imax], eax + mov eax, [draw_data+32+RECT.left] + mov ebx, [draw_data+32+RECT.top] + xor edi, edi;no force + + v12dp3: + + push eax + push ebx + + cmp [BgrDrawMode], dword 1 ; tiled background + jne no_vesa12_tiled_bgr + + push edx + + xor edx, edx + div dword [BgrDataWidth] + + push edx + mov eax, ebx + xor edx, edx + div dword [BgrDataHeight] + mov ebx, edx + pop eax + + pop edx + + no_vesa12_tiled_bgr: + + cmp [BgrDrawMode], dword 2 ; stretched background + jne no_vesa12_stretched_bgr + + push edx + + mul dword [BgrDataWidth] + mov ecx, [Screen_Max_X] + inc ecx + div ecx + + push eax + mov eax, ebx + mul dword [BgrDataHeight] + mov ecx, [Screen_Max_Y] + inc ecx + div ecx + mov ebx, eax + pop eax + + pop edx + + no_vesa12_stretched_bgr: + + + mov esi, ebx + imul esi, dword [BgrDataWidth] + add esi, eax + lea esi, [esi*3] + add esi, [img_background];IMG_BACKGROUND + pop ebx + pop eax + + v12di4: + + mov cl, [esi+2] + shl ecx, 16 + mov cx, [esi] + pusha + mov esi, eax + mov edi, ebx + mov eax, [Screen_Max_X] + add eax, 1 + mul ebx + add eax, [_WinMapAddress] + cmp [eax+esi], byte 1 + jnz v12nbgp + mov eax, [BytesPerScanLine] + mov ebx, edi + mul ebx + add eax, esi + lea eax, [VGABasePtr+eax+esi*2] + cmp [ScreenBPP], byte 24 + jz v12bgl3 + add eax, esi + + v12bgl3: + + push ebx + push eax + + sub eax, VGABasePtr + + shr eax, 16 + call set_bank + pop eax + and eax, 65535 + add eax, VGABasePtr + pop ebx + + mov [eax], cx + add eax, 2 + shr ecx, 16 + mov [eax], cl + sti + + v12nbgp: + + popa + add esi, 3 + inc eax + cmp eax, [draw_data+32+RECT.right] + jg v12nodp31 + jmp v12dp3 + + v12nodp31: + + mov eax, [draw_data+32+RECT.left] + inc ebx + cmp ebx, [draw_data+32+RECT.bottom] + jg v12dp4 + jmp v12dp3 + + v12dp4: + + pop edx + pop ecx + pop ebx + pop eax + ret + + +vesa12_drawbar: + + call [_display.disable_mouse] + +;; mov [novesachecksum],dword 0 + sub edx, ebx + sub ecx, eax + push esi + push edi + push eax + push ebx + push ecx + push edx + mov ecx, [TASK_BASE] + add eax, [ecx-twdw+WDATA.box.left] + add ebx, [ecx-twdw+WDATA.box.top] + push eax + mov eax, ebx ; y + mov ebx, [BytesPerScanLine] + mul ebx + pop ecx + add eax, ecx ; x + add eax, ecx + add eax, ecx + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? - x start + jz dbpi2412 + add eax, ecx + + dbpi2412: + + add eax, VGABasePtr + mov edi, eax + + ; x size + + mov eax, [esp+4]; [esp+6] + mov ecx, eax + add ecx, eax + add ecx, eax + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? - x size + jz dbpi24312 + add ecx, eax + + dbpi24312: + + mov ebx, [esp+0] + + ; check limits ? + + push eax + push ecx + mov eax, [TASK_BASE] + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.left] + cmp ecx, 0 + jnz dbcblimitlset12 + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.top] + cmp ecx, 0 + jnz dbcblimitlset12 + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.right] + cmp ecx, [Screen_Max_X] + jnz dbcblimitlset12 + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.bottom] + cmp ecx, [Screen_Max_Y] + jnz dbcblimitlset12 + pop ecx + pop eax + push dword 0 + jmp dbcblimitlno12 + + dbcblimitlset12: + + pop ecx + pop eax + push dword 1 + + dbcblimitlno12: + + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? + jz dbpi24bit12 + jmp dbpi32bit12 + + +; DRAWBAR 24 BBP + + +dbpi24bit12: + + push eax + push ebx + push edx + mov eax, ecx + mov ebx, 3 + div ebx + mov ecx, eax + pop edx + pop ebx + pop eax + cld + + dbnewpi12: + + push ebx + push edi + push ecx + + xor edx, edx + mov eax, edi + sub eax, VGABasePtr + mov ebx, 3 + div ebx + add eax, [_WinMapAddress] + mov ebx, [CURRENT_TASK] + cld + + dbnp2412: + + mov dl, [eax] + push eax + push ecx + cmp dl, bl + jnz dbimp24no12 + cmp [esp+5*4], dword 0 + jz dbimp24yes12 +; call dbcplimit +; jnz dbimp24no12 + + dbimp24yes12: + + push edi + mov eax, edi + sub eax, VGABasePtr + shr eax, 16 + call set_bank + and edi, 0xffff + add edi, VGABasePtr + mov eax, [esp+8+3*4+16+4+4] + stosw + shr eax, 16 + stosb + sti + pop edi + add edi, 3 + pop ecx + pop eax + inc eax + loop dbnp2412 + jmp dbnp24d12 + + dbimp24no12: + + pop ecx + pop eax + cld + add edi, 3 + inc eax + loop dbnp2412 + + dbnp24d12: + + mov eax, [esp+3*4+16+4] + test eax, 0x80000000 + jz nodbgl2412 + cmp al, 0 + jz nodbgl2412 + dec eax + mov [esp+3*4+16+4], eax + + nodbgl2412: + + pop ecx + pop edi + pop ebx + add edi, [BytesPerScanLine] + dec ebx + jz dbnonewpi12 + jmp dbnewpi12 + + dbnonewpi12: + + add esp, 7*4 + + ret + + +; DRAWBAR 32 BBP + + + dbpi32bit12: + + cld + shr ecx, 2 + + dbnewpi3212: + + push ebx + push edi + push ecx + + mov eax, edi + sub eax, VGABasePtr + shr eax, 2 + add eax, [_WinMapAddress] + mov ebx, [CURRENT_TASK] + cld + + dbnp3212: + + mov dl, [eax] + push eax + push ecx + cmp dl, bl + jnz dbimp32no12 + cmp [esp+5*4], dword 0 + jz dbimp32yes12 +; call dbcplimit +; jnz dbimp32no12 + + dbimp32yes12: + + push edi + mov eax, edi + sub eax, VGABasePtr + shr eax, 16 + call set_bank + and edi, 0xffff + add edi, VGABasePtr + mov eax, [esp+8+3*4+16+4+4] + stosw + shr eax, 16 + stosb + sti + pop edi + add edi, 4 + inc ebp + pop ecx + pop eax + inc eax + loop dbnp3212 + jmp dbnp32d12 + + dbimp32no12: + + pop ecx + pop eax + inc eax + add edi, 4 + inc ebp + loop dbnp3212 + + dbnp32d12: + + mov eax, [esp+12+16+4] + test eax, 0x80000000 + jz nodbgl3212 + cmp al, 0 + jz nodbgl3212 + dec eax + mov [esp+12+16+4], eax + + nodbgl3212: + + pop ecx + pop edi + pop ebx + add edi, [BytesPerScanLine] + dec ebx + jz nodbnewpi3212 + jmp dbnewpi3212 + + nodbnewpi3212: + + add esp, 7*4 + ret + + +Vesa12_putpixel24: + + mov edi, eax; x + mov eax, ebx; y + lea edi, [edi+edi*2] + mov ebx, [BytesPerScanLine] + mul ebx + add edi, eax + mov eax, edi + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov eax, [esp+28] + stosw + shr eax, 16 + mov [edi], al + sti + ret + + + +Vesa12_putpixel32: + + mov edi, eax; x + mov eax, ebx; y + shl edi, 2 + mov ebx, [BytesPerScanLine] + mul ebx + add edi, eax + mov eax, edi + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov ecx, [esp+28] + mov [edi], ecx + sti + ret + + +Vesa12_getpixel24: + + mov edi, eax; x + mov eax, ebx; y + lea edi, [edi+edi*2] + mov ebx, [BytesPerScanLine] + mul ebx + add edi, eax + mov eax, edi + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov ecx, [edi] + and ecx, 255*256*256+255*256+255 + sti + ret + + +Vesa12_getpixel32: + + mov edi, eax; x + mov eax, ebx; y + shl edi, 2 + mov ebx, [BytesPerScanLine] + xor edx, edx + mul ebx + add edi, eax + mov eax, edi + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov ecx, [edi] + and ecx, 255*256*256+255*256+255 + sti + + ret + + + +vesa12_putimage: +; ebx = pointer to image +; ecx = size [x|y] +; edx = coordinates [x|y] +; ebp = pointer to 'get' function +; esi = pointer to 'init' function +; edi = parameter for 'get' function + +; mov ebx,image +; mov ecx,320*65536+240 +; mov edx,20*65536+20 + + call [_display.disable_mouse] + + mov [novesachecksum], dword 0 + push esi + push edi + push eax + push ebx + push ecx + push edx + movzx eax, word [esp+2] + movzx ebx, word [esp+0] + mov ecx, [TASK_BASE] + add eax, [ecx-twdw+WDATA.box.left] + add ebx, [ecx-twdw+WDATA.box.top] + push eax + mov eax, ebx ; y + mul dword [BytesPerScanLine] + pop ecx + add eax, ecx ; x + add eax, ecx + add eax, ecx + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? - x start + jz pi2412 + add eax, ecx + + pi2412: + + add eax, VGABasePtr + mov edi, eax + + ; x size + + movzx ecx, word [esp+6] + + mov esi, [esp+8] + movzx ebx, word [esp+4] + + ; check limits while draw ? + + push ecx + mov eax, [TASK_BASE] + cmp dword [eax+draw_data-CURRENT_TASK+RECT.left], 0 + jnz dbcblimitlset212 + cmp dword [eax+draw_data-CURRENT_TASK+RECT.top], 0 + jnz dbcblimitlset212 + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.right] + cmp ecx, [Screen_Max_X] + jnz dbcblimitlset212 + mov ecx, [eax+draw_data-CURRENT_TASK+RECT.bottom] + cmp ecx, [Screen_Max_Y] + jnz dbcblimitlset212 + pop ecx + push 0 + jmp dbcblimitlno212 + + dbcblimitlset212: + + pop ecx + push 1 + + dbcblimitlno212: + + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? + jnz pi32bit12 + + pi24bit12: + + newpi12: + + push edi + push ecx + push ebx + + mov edx, edi + sub edx, VGABasePtr + mov ebx, 3 + div ebx + add edx, [_WinMapAddress] + mov ebx, [CURRENT_TASK] + mov bh, [esp+4*3] + + np2412: + + cmp bl, [edx] + jnz imp24no12 +; mov eax,[esi] + push dword [esp+4*3+20] + call ebp +; cmp bh,0 +; jz imp24yes12 +; call dbcplimit +; jnz imp24no12 + + imp24yes12: + + push edi + push eax + mov eax, edi + sub eax, VGABasePtr + shr eax, 16 + call set_bank + pop eax + and edi, 0xffff + add edi, VGABasePtr + mov [edi], ax + shr eax, 16 + mov [edi+2], al + pop edi + + imp24no12: + + inc edx +; add esi,3 + add edi, 3 + dec ecx + jnz np2412 + + np24d12: + + pop ebx + pop ecx + pop edi + + add edi, [BytesPerScanLine] + add esi, [esp+32] + cmp ebp, putimage_get1bpp + jz .correct + cmp ebp, putimage_get2bpp + jz .correct + cmp ebp, putimage_get4bpp + jnz @f +.correct: + mov eax, [esp+20] + mov byte[eax], 80h +@@: + dec ebx + jnz newpi12 + + nonewpi12: + + pop eax edx ecx ebx eax edi esi + xor eax, eax + ret + + + pi32bit12: + + newpi3212: + + push edi + push ecx + push ebx + + mov edx, edi + sub edx, VGABasePtr + shr edx, 2 + add edx, [_WinMapAddress] + mov ebx, [CURRENT_TASK] + mov bh, [esp+4*3] + + np3212: + + cmp bl, [edx] + jnz imp32no12 +; mov eax,[esi] + push dword [esp+4*3+20] + call ebp +; cmp bh,0 +; jz imp32yes12 +; call dbcplimit +; jnz imp32no12 + + imp32yes12: + + push edi + push eax + mov eax, edi + sub eax, VGABasePtr + shr eax, 16 + call set_bank + pop eax + and edi, 0xffff + mov [edi+VGABasePtr], eax + pop edi + + imp32no12: + + inc edx +; add esi,3 + add edi, 4 + dec ecx + jnz np3212 + + np32d12: + + pop ebx + pop ecx + pop edi + + add edi, [BytesPerScanLine] + cmp ebp, putimage_get1bpp + jz .correct + cmp ebp, putimage_get2bpp + jz .correct + cmp ebp, putimage_get4bpp + jnz @f +.correct: + mov eax, [esp+20] + mov byte[eax], 80h +@@: + dec ebx + jnz newpi3212 + + nonewpi3212: + + pop eax edx ecx ebx eax edi esi + xor eax, eax + ret + + +vesa12_read_screen_pixel: + + and eax, 0x3FFFFF + cmp [ScreenBPP], byte 24; 24 or 32 bpp ? + jz v12rsp24 + mov edi, eax + shl edi, 2 + mov eax, edi + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov eax, [edi] + and eax, 0x00ffffff + ret + v12rsp24: + + imul eax, 3 + mov edi, eax + shr eax, 16 + call set_bank + and edi, 65535 + add edi, VGABasePtr + mov eax, [edi] + and eax, 0x00ffffff + ret + + diff --git a/kernel/branches/kolibri-process/video/vesa20.inc b/kernel/branches/kolibri-process/video/vesa20.inc new file mode 100644 index 000000000..9b45f453a --- /dev/null +++ b/kernel/branches/kolibri-process/video/vesa20.inc @@ -0,0 +1,2150 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; VESA20.INC ;; +;; ;; +;; Vesa 2.0 functions for MenuetOS ;; +;; ;; +;; Copyright 2002 Ville Turjanmaa ;; +;; Alexey, kgaz@crosswindws.net ;; +;; - Voodoo compatible graphics ;; +;; Juan M. Caravaca ;; +;; - Graphics optimimizations eg. drawline ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3606 $ + + +; If you're planning to write your own video driver I suggest +; you replace the VESA12.INC file and see those instructions. + +;Screen_Max_X equ 0xfe00 +;Screen_Max_Y equ 0xfe04 +;BytesPerScanLine equ 0xfe08 +;LFBAddress equ 0xfe80 +;ScreenBPP equ 0xfbf1 + + + +;----------------------------------------------------------------------------- +; getpixel +; +; in: +; eax = x coordinate +; ebx = y coordinate +; +; ret: +; ecx = 00 RR GG BB +;----------------------------------------------------------------------------- +align 4 +getpixel: + push eax ebx edx edi + call dword [GETPIXEL] + pop edi edx ebx eax + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_getpixel24: +; eax = x +; ebx = y +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: +; check mouse area for putpixel + test ecx, 0x04000000 ; don't load to mouseunder area + jnz .no_mouseunder + call [_display.check_m_pixel] + test ecx, ecx ;0xff000000 + jnz @f +;-------------------------------------- +align 4 +.no_mouseunder: +;-------------------------------------- +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [eax+eax*2]; edi = x*3 + add edi, ebx ; edi = x*3+(y*y multiplier) + mov ecx, [LFB_BASE+edi] +;-------------------------------------- +align 4 +@@: + and ecx, 0xffffff + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_getpixel32: +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: +; check mouse area for putpixel + test ecx, 0x04000000 ; don't load to mouseunder area + jnz .no_mouseunder + call [_display.check_m_pixel] + test ecx, ecx ;0xff000000 + jnz @f +;-------------------------------------- +align 4 +.no_mouseunder: +;-------------------------------------- +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [ebx+eax*4]; edi = x*4+(y*y multiplier) + mov ecx, [LFB_BASE+edi] +;-------------------------------------- +align 4 +@@: + and ecx, 0xffffff + ret +;----------------------------------------------------------------------------- +virtual at esp + putimg: + .real_sx dd ? + .real_sy dd ? + .image_sx dd ? + .image_sy dd ? + .image_cx dd ? + .image_cy dd ? + .pti dd ? + .abs_cx dd ? + .abs_cy dd ? + .line_increment dd ? + .winmap_newline dd ? + .screen_newline dd ? + .real_sx_and_abs_cx dd ? + .real_sy_and_abs_cy dd ? + .stack_data = 4*14 + .edi dd ? + .esi dd ? + .ebp dd ? + .esp dd ? + .ebx dd ? + .edx dd ? + .ecx dd ? + .eax dd ? + .ret_addr dd ? + .arg_0 dd ? +end virtual +;----------------------------------------------------------------------------- +align 16 +; ebx = pointer +; ecx = size [x|y] +; edx = coordinates [x|y] +; ebp = pointer to 'get' function +; esi = pointer to 'init' function +; edi = parameter for 'get' function +vesa20_putimage: + pushad + sub esp, putimg.stack_data +; save pointer to image + mov [putimg.pti], ebx +; unpack the size + mov eax, ecx + and ecx, 0xFFFF + shr eax, 16 + mov [putimg.image_sx], eax + mov [putimg.image_sy], ecx +; unpack the coordinates + mov eax, edx + and edx, 0xFFFF + shr eax, 16 + mov [putimg.image_cx], eax + mov [putimg.image_cy], edx +; calculate absolute (i.e. screen) coordinates + mov eax, [TASK_BASE] + mov ebx, [eax-twdw + WDATA.box.left] + add ebx, [putimg.image_cx] + mov [putimg.abs_cx], ebx + mov ebx, [eax-twdw + WDATA.box.top] + add ebx, [putimg.image_cy] + mov [putimg.abs_cy], ebx +; real_sx = MIN(wnd_sx-image_cx, image_sx); + mov ebx, [eax-twdw + WDATA.box.width]; ebx = wnd_sx +; \begin{diamond}[20.08.2006] +; note that WDATA.box.width is one pixel less than real window x-size + inc ebx +; \end{diamond}[20.08.2006] + sub ebx, [putimg.image_cx] + ja @f + add esp, putimg.stack_data + popad + ret +;-------------------------------------- +align 4 +@@: + cmp ebx, [putimg.image_sx] + jbe .end_x + mov ebx, [putimg.image_sx] +;-------------------------------------- +align 4 +.end_x: + mov [putimg.real_sx], ebx +; init real_sy + mov ebx, [eax-twdw + WDATA.box.height]; ebx = wnd_sy +; \begin{diamond}[20.08.2006] + inc ebx +; \end{diamond}[20.08.2006] + sub ebx, [putimg.image_cy] + ja @f + add esp, putimg.stack_data + popad + ret +;-------------------------------------- +align 4 +@@: + cmp ebx, [putimg.image_sy] + jbe .end_y + mov ebx, [putimg.image_sy] +;-------------------------------------- +align 4 +.end_y: + mov [putimg.real_sy], ebx +; line increment + mov eax, [putimg.image_sx] + mov ecx, [putimg.real_sx] + sub eax, ecx +;; imul eax, [putimg.source_bpp] +; lea eax, [eax + eax * 2] + call esi + add eax, [putimg.arg_0] + mov [putimg.line_increment], eax +; winmap new line increment + mov eax, [Screen_Max_X] + inc eax + sub eax, [putimg.real_sx] + mov [putimg.winmap_newline], eax +; screen new line increment + mov eax, [_display.pitch] + mov ebx, [_display.bpp] + shr ebx, 3 + imul ecx, ebx + sub eax, ecx + mov [putimg.screen_newline], eax +; pointer to image + mov esi, [putimg.pti] +; pointer to screen + mov edx, [putimg.abs_cy] +; imul edx, [BytesPerScanLine] + mov edx, [BPSLine_calc_area+edx*4] + mov eax, [putimg.abs_cx] +; movzx ebx, byte [ScreenBPP] +; shr ebx, 3 + imul eax, ebx + add edx, eax +; pointer to pixel map + mov eax, [putimg.abs_cy] +; imul eax, [Screen_Max_X] +; add eax, [putimg.abs_cy] + mov eax, [d_width_calc_area + eax*4] + + add eax, [putimg.abs_cx] + add eax, [_WinMapAddress] + xchg eax, ebp +;-------------------------------------- + mov ecx, [putimg.real_sx] + add ecx, [putimg.abs_cx] + mov [putimg.real_sx_and_abs_cx], ecx + mov ecx, [putimg.real_sy] + add ecx, [putimg.abs_cy] + mov [putimg.real_sy_and_abs_cy], ecx +;-------------------------------------- +; get process number + mov ebx, [CURRENT_TASK] + cmp byte [_display.bpp], 32 + je put_image_end_32 +;-------------------------------------- +put_image_end_24: + mov edi, [putimg.real_sy] +;-------------------------------------- +; check for hardware cursor + mov ecx, [_display.select_cursor] + cmp ecx, select_cursor + je put_image_end_24_new + cmp ecx, 0 + je put_image_end_24_old +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- +; store to real LFB + mov [LFB_BASE+edx], ax + shr eax, 16 + mov [LFB_BASE+edx+2], al +;-------------------------------------- +align 4 +.skip: + add edx, 3 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line +;-------------------------------------- +align 4 +.finish: + add esp, putimg.stack_data + popad + ret +;------------------------------------------------------------------------------ +align 4 +put_image_end_24_old: +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- + push ecx + + neg ecx + add ecx, [putimg.real_sx_and_abs_cx + 4] + shl ecx, 16 + add ecx, [putimg.real_sy_and_abs_cy + 4] + sub ecx, edi + +; check mouse area for putpixel + call check_mouse_area_for_putpixel + pop ecx +; store to real LFB + mov [LFB_BASE+edx], ax + shr eax, 16 + mov [LFB_BASE+edx+2], al +;-------------------------------------- +align 4 +.skip: + add edx, 3 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line + jmp put_image_end_24.finish +;------------------------------------------------------------------------------ +align 4 +put_image_end_24_new: +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- + push ecx +;-------------------------------------- +align 4 +.sh: + neg ecx + add ecx, [putimg.real_sx_and_abs_cx + 4] +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + shl ecx, 16 + + add ecx, [putimg.real_sy_and_abs_cy + 4] + sub ecx, edi +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area +;-------------------------------------- +; check mouse area for putpixel + call check_mouse_area_for_putpixel_new.1 + cmp ecx, -1 ;SHIT HAPPENS? + jne .no_mouse_area + + mov ecx, [esp] + jmp .sh +;-------------------------------------- +align 4 +.no_mouse_area: + pop ecx +; store to real LFB + mov [LFB_BASE+edx], ax + shr eax, 16 + mov [LFB_BASE+edx+2], al +;-------------------------------------- +align 4 +.skip: + add edx, 3 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line + jmp put_image_end_24.finish +;------------------------------------------------------------------------------ +align 4 +put_image_end_32: + mov edi, [putimg.real_sy] +;-------------------------------------- +; check for hardware cursor + mov ecx, [_display.select_cursor] + cmp ecx, select_cursor + je put_image_end_32_new + cmp ecx, 0 + je put_image_end_32_old +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- +; store to real LFB + mov [LFB_BASE+edx], eax +;-------------------------------------- +align 4 +.skip: + add edx, 4 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line +;-------------------------------------- +align 4 +.finish: + add esp, putimg.stack_data + popad + cmp [SCR_MODE], 0x12 + jne @f + call VGA__putimage +;-------------------------------------- +align 4 +@@: + mov [EGA_counter], 1 + ret +;------------------------------------------------------------------------------ +align 4 +put_image_end_32_old: +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- + push ecx + + neg ecx + add ecx, [putimg.real_sx_and_abs_cx + 4] + shl ecx, 16 + add ecx, [putimg.real_sy_and_abs_cy + 4] + sub ecx, edi + +; check mouse area for putpixel + call check_mouse_area_for_putpixel + pop ecx +; store to real LFB + mov [LFB_BASE+edx], eax +;-------------------------------------- +align 4 +.skip: + add edx, 4 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line + jmp put_image_end_32.finish +;------------------------------------------------------------------------------ +align 4 +put_image_end_32_new: +;-------------------------------------- +align 4 +.new_line: + mov ecx, [putimg.real_sx] +;-------------------------------------- +align 4 +.new_x: + push [putimg.edi] + mov eax, [putimg.ebp+4] + call eax + cmp [ebp], bl + jne .skip +;-------------------------------------- + push ecx +;-------------------------------------- +align 4 +.sh: + neg ecx + add ecx, [putimg.real_sx_and_abs_cx + 4] +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + shl ecx, 16 + + add ecx, [putimg.real_sy_and_abs_cy + 4] + sub ecx, edi +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area +;-------------------------------------- +; check mouse area for putpixel + call check_mouse_area_for_putpixel_new.1 + cmp ecx, -1 ;SHIT HAPPENS? + jne .no_mouse_area + + mov ecx, [esp] + jmp .sh +;-------------------------------------- +align 4 +.no_mouse_area: + pop ecx +; store to real LFB + mov [LFB_BASE+edx], eax +;-------------------------------------- +align 4 +.skip: + add edx, 4 + inc ebp + dec ecx + jnz .new_x + + add esi, [putimg.line_increment] + add edx, [putimg.screen_newline];[BytesPerScanLine] + add ebp, [putimg.winmap_newline];[Screen_Max_X] + + cmp [putimg.ebp], putimage_get1bpp + jz .correct + cmp [putimg.ebp], putimage_get2bpp + jz .correct + cmp [putimg.ebp], putimage_get4bpp + jnz @f +;-------------------------------------- +align 4 +.correct: + mov eax, [putimg.edi] + mov byte [eax], 80h +;-------------------------------------- +align 4 +@@: + dec edi + jnz .new_line + jmp put_image_end_32.finish +;------------------------------------------------------------------------------ +align 4 +__sys_putpixel: + +; eax = x coordinate +; ebx = y coordinate +; ecx = ?? RR GG BB ; 0x01000000 negation + ; 0x02000000 used for draw_rectangle without top line + ; for example drawwindow_III and drawwindow_IV +; edi = 0x00000001 force + +;;; mov [novesachecksum], dword 0 + pushad + cmp [Screen_Max_X], eax + jb .exit + cmp [Screen_Max_Y], ebx + jb .exit + test edi, 1 ; force ? + jnz .forced + +; not forced: + mov edx, [d_width_calc_area + ebx*4] + add edx, [_WinMapAddress] + movzx edx, byte [eax+edx] + cmp edx, [CURRENT_TASK] + jne .exit +;-------------------------------------- +align 4 +.forced: +; check if negation + test ecx, 0x01000000 + jz .noneg + + call getpixel + not ecx + + rol ecx, 8 + mov cl, [esp+32-8+3] + ror ecx, 8 + mov [esp+32-8], ecx +;-------------------------------------- +align 4 +.noneg: +; OK to set pixel + call dword [PUTPIXEL]; call the real put_pixel function +;-------------------------------------- +align 4 +.exit: + popad + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_putpixel24: +; eax = x +; ebx = y + mov ecx, eax + shl ecx, 16 + mov cx, bx + +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [eax+eax*2]; edi = x*3 + mov eax, [esp+32-8+4] +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], 0 + jne @f +; check mouse area for putpixel + test eax, 0x04000000 + jnz @f + call check_mouse_area_for_putpixel +;-------------------------------------- +align 4 +@@: +; store to real LFB + mov [LFB_BASE+ebx+edi], ax + shr eax, 16 + mov [LFB_BASE+ebx+edi+2], al + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_putpixel24_new: +; eax = x +; ebx = y + mov ecx, eax + shl ecx, 16 + mov cx, bx + +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [eax+eax*2]; edi = x*3 + mov eax, [esp+32-8+4] +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + jne @f +; check mouse area for putpixel + test eax, 0x04000000 + jnz @f +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae @f + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb @f + + rol ecx, 16 +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae @f + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb @f + + ror ecx, 16 + + call check_mouse_area_for_putpixel_new.1 +;-------------------------------------- +align 4 +@@: +; store to real LFB + mov [LFB_BASE+ebx+edi], ax + shr eax, 16 + mov [LFB_BASE+ebx+edi+2], al + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_putpixel32: +; eax = x +; ebx = y + mov ecx, eax + shl ecx, 16 + mov cx, bx + +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [ebx+eax*4]; edi = x*4+(y*y multiplier) + mov eax, [esp+32-8+4]; eax = color +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], 0 + jne @f +; check mouse area for putpixel + test eax, 0x04000000 + jnz @f + call check_mouse_area_for_putpixel +;-------------------------------------- +align 4 +@@: + and eax, 0xffffff +; store to real LFB + mov [LFB_BASE+edi], eax + ret +;----------------------------------------------------------------------------- +align 4 +Vesa20_putpixel32_new: +; eax = x +; ebx = y + mov ecx, eax + shl ecx, 16 + mov cx, bx + +; imul ebx, [BytesPerScanLine] ; ebx = y * y multiplier + mov ebx, [BPSLine_calc_area+ebx*4] + lea edi, [ebx+eax*4]; edi = x*4+(y*y multiplier) + mov eax, [esp+32-8+4]; eax = color +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + jne @f +; check mouse area for putpixel + test eax, 0x04000000 + jnz @f +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae @f + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb @f + + rol ecx, 16 +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae @f + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb @f + + ror ecx, 16 + + call check_mouse_area_for_putpixel_new.1 +;-------------------------------------- +align 4 +@@: + and eax, 0xffffff +; store to real LFB + mov [LFB_BASE+edi], eax + ret +;----------------------------------------------------------------------------- +align 4 +calculate_edi: +; mov edi, ebx +; imul edi, [Screen_Max_X] +; add edi, ebx + mov edi, [d_width_calc_area + ebx*4] + add edi, eax + ret +;----------------------------------------------------------------------------- +; DRAWLINE +;----------------------------------------------------------------------------- +align 4 +__sys_draw_line: +; draw a line +; eax = HIWORD = x1 +; LOWORD = x2 +; ebx = HIWORD = y1 +; LOWORD = y2 +; ecx = color +; edi = force ? + pusha + +dl_x1 equ esp+20 +dl_y1 equ esp+16 +dl_x2 equ esp+12 +dl_y2 equ esp+8 +dl_dx equ esp+4 +dl_dy equ esp+0 + + xor edx, edx ; clear edx + xor esi, esi ; unpack arguments + xor ebp, ebp + mov si, ax ; esi = x2 + mov bp, bx ; ebp = y2 + shr eax, 16 ; eax = x1 + shr ebx, 16 ; ebx = y1 + push eax ; save x1 + push ebx ; save y1 + push esi ; save x2 + push ebp ; save y2 +; checking x-axis... + sub esi, eax ; esi = x2-x1 + push esi ; save y2-y1 + jl .x2lx1 ; is x2 less than x1 ? + jg .no_vline ; x1 > x2 ? + mov edx, ebp ; else (if x1=x2) + call vline + push edx ; necessary to rightly restore stack frame at .exit + jmp .exit +;-------------------------------------- +align 4 +.x2lx1: + neg esi ; get esi absolute value +;-------------------------------------- +align 4 +.no_vline: +; checking y-axis... + sub ebp, ebx ; ebp = y2-y1 + push ebp ; save y2-y1 + jl .y2ly1 ; is y2 less than y1 ? + jg .no_hline ; y1 > y2 ? + mov edx, [dl_x2]; else (if y1=y2) + call hline + jmp .exit +;-------------------------------------- +align 4 +.y2ly1: + neg ebp ; get ebp absolute value +;-------------------------------------- +align 4 +.no_hline: + cmp ebp, esi + jle .x_rules ; |y2-y1| < |x2-x1| ? + cmp [dl_y2], ebx; make sure y1 is at the begining + jge .no_reverse1 + neg dword [dl_dx] + mov edx, [dl_x2] + mov [dl_x2], eax + mov [dl_x1], edx + mov edx, [dl_y2] + mov [dl_y2], ebx + mov [dl_y1], edx +;-------------------------------------- +align 4 +.no_reverse1: + mov eax, [dl_dx] + cdq ; extend eax sing to edx + shl eax, 16 ; using 16bit fix-point maths + idiv ebp ; eax = ((x2-x1)*65536)/(y2-y1) +;-------------------------------------- +; correction for the remainder of the division + shl edx, 1 + cmp ebp, edx + jb @f + inc eax +;-------------------------------------- +align 4 +@@: +;-------------------------------------- + mov edx, ebp ; edx = counter (number of pixels to draw) + mov ebp, 1 *65536; <<16 ; ebp = dy = 1.0 + mov esi, eax ; esi = dx + jmp .y_rules +;-------------------------------------- +align 4 +.x_rules: + cmp [dl_x2], eax ; make sure x1 is at the begining + jge .no_reverse2 + neg dword [dl_dy] + mov edx, [dl_x2] + mov [dl_x2], eax + mov [dl_x1], edx + mov edx, [dl_y2] + mov [dl_y2], ebx + mov [dl_y1], edx +;-------------------------------------- +align 4 +.no_reverse2: + xor edx, edx + mov eax, [dl_dy] + cdq ; extend eax sing to edx + shl eax, 16 ; using 16bit fix-point maths + idiv esi ; eax = ((y2-y1)*65536)/(x2-x1) +;-------------------------------------- +; correction for the remainder of the division + shl edx, 1 + cmp esi, edx + jb @f + inc eax +;-------------------------------------- +align 4 +@@: +;-------------------------------------- + mov edx, esi ; edx = counter (number of pixels to draw) + mov esi, 1 *65536;<< 16 ; esi = dx = 1.0 + mov ebp, eax ; ebp = dy +;-------------------------------------- +align 4 +.y_rules: + mov eax, [dl_x1] + mov ebx, [dl_y1] + shl eax, 16 + shl ebx, 16 + + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +;----------------------------------------------------------------------------- +align 4 +.draw: + push eax ebx +;-------------------------------------- +; correction for the remainder of the division + test ah, 0x80 + jz @f + add eax, 1 shl 16 +;-------------------------------------- +align 4 +@@: +;-------------------------------------- + shr eax, 16 +;-------------------------------------- +; correction for the remainder of the division + test bh, 0x80 + jz @f + add ebx, 1 shl 16 +;-------------------------------------- +align 4 +@@: +;-------------------------------------- + shr ebx, 16 +; and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel + pop ebx eax + add ebx, ebp ; y = y+dy + add eax, esi ; x = x+dx + dec edx + jnz .draw +; force last drawn pixel to be at (x2,y2) + mov eax, [dl_x2] + mov ebx, [dl_y2] +; and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +; call [putpixel] + call __sys_putpixel +;-------------------------------------- +align 4 +.exit: + add esp, 6*4 + popa +; call [draw_pointer] + ret +;------------------------------------------------------------------------------ +align 4 +hline: +; draw an horizontal line +; eax = x1 +; edx = x2 +; ebx = y +; ecx = color +; edi = force ? + push eax edx + cmp edx, eax ; make sure x2 is above x1 + jge @f + xchg eax, edx +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +;-------------------------------------- +align 4 +@@: +; call [putpixel] + call __sys_putpixel + inc eax + cmp eax, edx + jle @b + pop edx eax + ret +;------------------------------------------------------------------------------ +align 4 +vline: +; draw a vertical line +; eax = x +; ebx = y1 +; edx = y2 +; ecx = color +; edi = force ? + push ebx edx + cmp edx, ebx ; make sure y2 is above y1 + jge @f + xchg ebx, edx +;-------------------------------------- +align 4 +@@: + and ecx, 0xFBFFFFFF ;negate 0x04000000 save to mouseunder area +;-------------------------------------- +align 4 +@@: +; call [putpixel] + call __sys_putpixel + inc ebx + cmp ebx, edx + jle @b + pop edx ebx + ret +;------------------------------------------------------------------------------ +align 4 +virtual at esp +drbar: + .bar_sx dd ? + .bar_sy dd ? + .bar_cx dd ? + .bar_cy dd ? + .abs_cx dd ? + .abs_cy dd ? + .real_sx dd ? + .real_sy dd ? + .color dd ? + .line_inc_scr dd ? + .line_inc_map dd ? + .real_sx_and_abs_cx dd ? + .real_sy_and_abs_cy dd ? + .stack_data = 4*13 +end virtual +;-------------------------------------- +align 4 +; eax cx +; ebx cy +; ecx xe +; edx ye +; edi color +vesa20_drawbar: + pushad + sub esp, drbar.stack_data + mov [drbar.color], edi + sub edx, ebx + jle .exit ;// mike.dld, 2005-01-29 + sub ecx, eax + jle .exit ;// mike.dld, 2005-01-29 + mov [drbar.bar_sy], edx + mov [drbar.bar_sx], ecx + mov [drbar.bar_cx], eax + mov [drbar.bar_cy], ebx + mov edi, [TASK_BASE] + add eax, [edi-twdw + WDATA.box.left]; win_cx + add ebx, [edi-twdw + WDATA.box.top]; win_cy + mov [drbar.abs_cx], eax + mov [drbar.abs_cy], ebx +; real_sx = MIN(wnd_sx-bar_cx, bar_sx); + mov ebx, [edi-twdw + WDATA.box.width]; ebx = wnd_sx +; \begin{diamond}[20.08.2006] +; note that WDATA.box.width is one pixel less than real window x-size + inc ebx +; \end{diamond}[20.08.2006] + sub ebx, [drbar.bar_cx] + ja @f +;-------------------------------------- +align 4 +.exit: ;// mike.dld, 2005-01-29 + add esp, drbar.stack_data + popad + xor eax, eax + inc eax + ret +;-------------------------------------- +align 4 +@@: + cmp ebx, [drbar.bar_sx] + jbe .end_x + mov ebx, [drbar.bar_sx] +;-------------------------------------- +align 4 +.end_x: + mov [drbar.real_sx], ebx +; real_sy = MIN(wnd_sy-bar_cy, bar_sy); + mov ebx, [edi-twdw + WDATA.box.height]; ebx = wnd_sy +; \begin{diamond}[20.08.2006] + inc ebx +; \end{diamond} + sub ebx, [drbar.bar_cy] + ja @f + add esp, drbar.stack_data + popad + xor eax, eax + inc eax + ret +;-------------------------------------- +align 4 +@@: + cmp ebx, [drbar.bar_sy] + jbe .end_y + mov ebx, [drbar.bar_sy] +;-------------------------------------- +align 4 +.end_y: + mov [drbar.real_sy], ebx +; line_inc_map + mov eax, [Screen_Max_X] + sub eax, [drbar.real_sx] + inc eax + mov [drbar.line_inc_map], eax +; line_inc_scr + mov eax, [drbar.real_sx] + mov ebx, [_display.bpp] + shr ebx, 3 + imul eax, ebx + neg eax + add eax, [_display.pitch] + mov [drbar.line_inc_scr], eax +; pointer to screen + mov edx, [drbar.abs_cy] +; imul edx, [BytesPerScanLine] + mov edx, [BPSLine_calc_area+edx*4] + mov eax, [drbar.abs_cx] + imul eax, ebx + add edx, eax +; pointer to pixel map + mov eax, [drbar.abs_cy] +; imul eax, [Screen_Max_X] +; add eax, [drbar.abs_cy] + mov eax, [d_width_calc_area + eax*4] + + add eax, [drbar.abs_cx] + add eax, [_WinMapAddress] + xchg eax, ebp +;-------------------------------------- + mov ebx, [drbar.real_sx] + add ebx, [drbar.abs_cx] + mov [drbar.real_sx_and_abs_cx], ebx + mov ebx, [drbar.real_sy] + add ebx, [drbar.abs_cy] + mov [drbar.real_sy_and_abs_cy], ebx + + add edx, LFB_BASE +;-------------------------------------- +; get process number + mov ebx, [CURRENT_TASK] ; bl - process num + mov esi, [drbar.real_sy] + mov eax, [drbar.color] ; BBGGRR00 + rol eax, 8 + mov bh, al ; 0x80 drawing gradient bars + ror eax, 8 + cmp byte [_display.bpp], 24 + jne draw_bar_end_32 +;-------------------------------------- +align 4 +draw_bar_end_24: +; eax - color high RRGGBB +; bl - process num +; ecx - temp +; edx - pointer to screen +; esi - counter +; edi - counter +;-------------------------------------- +; check for hardware cursor + mov ecx, [_display.select_cursor] + cmp ecx, select_cursor + je draw_bar_end_24_new + cmp ecx, 0 + je draw_bar_end_24_old +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- +; store to real LFB + mov [edx], ax + shr eax, 16 + mov [edx + 2], al +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 3 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y +;-------------------------------------- +align 4 +.end: + add esp, drbar.stack_data + popad + xor eax, eax + ret +;------------------------------------------------------------------------------ +align 4 +draw_bar_end_24_old: +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- + mov ecx, [drbar.real_sx_and_abs_cx] + sub ecx, edi + shl ecx, 16 + add ecx, [drbar.real_sy_and_abs_cy] + sub ecx, esi +; check mouse area for putpixel + call check_mouse_area_for_putpixel +; store to real LFB + mov [edx], ax + shr eax, 16 + mov [edx + 2], al + mov eax, [drbar.color] +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 3 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y + jmp draw_bar_end_24.end +;------------------------------------------------------------------------------ +align 4 +draw_bar_end_24_new: +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- + mov ecx, [drbar.real_sy_and_abs_cy] + sub ecx, esi +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area + + rol ecx, 16 + add ecx, [drbar.real_sx_and_abs_cx] + sub ecx, edi +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + ror ecx, 16 +;-------------------------------------- +; check mouse area for putpixel + push eax + call check_mouse_area_for_putpixel_new.1 + mov [edx], ax + shr eax, 16 + mov [edx + 2], al + pop eax + jmp .skip +; store to real LFB +;-------------------------------------- +align 4 +.no_mouse_area: + mov [edx], ax + ror eax, 16 + mov [edx + 2], al + rol eax, 16 +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 3 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y + jmp draw_bar_end_24.end +;------------------------------------------------------------------------------ +align 4 +draw_bar_end_32: +; eax - color high RRGGBB +; bl - process num +; ecx - temp +; edx - pointer to screen +; esi - counter +; edi - counter +;-------------------------------------- +; check for hardware cursor + mov ecx, [_display.select_cursor] + cmp ecx, select_cursor + je draw_bar_end_32_new + cmp ecx, 0 + je draw_bar_end_32_old +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- +; store to real LFB + mov [edx], eax + mov eax, [drbar.color] +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 4 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y +;-------------------------------------- +align 4 +.end: + add esp, drbar.stack_data + popad + cmp [SCR_MODE], 0x12 + jne @f + call VGA_draw_bar +;-------------------------------------- +align 4 +@@: + xor eax, eax + mov [EGA_counter], 1 + ret +;------------------------------------------------------------------------------ +align 4 +draw_bar_end_32_old: +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- + mov ecx, [drbar.real_sx_and_abs_cx] + sub ecx, edi + shl ecx, 16 + add ecx, [drbar.real_sy_and_abs_cy] + sub ecx, esi + +; check mouse area for putpixel + call check_mouse_area_for_putpixel +; store to real LFB + mov [edx], eax + mov eax, [drbar.color] +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 4 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y + jmp draw_bar_end_32.end +;------------------------------------------------------------------------------ +align 4 +draw_bar_end_32_new: +;-------------------------------------- +align 4 +.new_y: + mov edi, [drbar.real_sx] +;-------------------------------------- +align 4 +.new_x: + cmp byte [ebp], bl + jne .skip +;-------------------------------------- + mov ecx, [drbar.real_sy_and_abs_cy] + sub ecx, esi +;-------------------------------------- +; check for Y + cmp cx, [Y_UNDER_sub_CUR_hot_y_add_curh] + jae .no_mouse_area + + sub cx, [Y_UNDER_subtraction_CUR_hot_y] + jb .no_mouse_area + + rol ecx, 16 + add ecx, [drbar.real_sx_and_abs_cx] + sub ecx, edi +;-------------------------------------- +; check for X + cmp cx, [X_UNDER_sub_CUR_hot_x_add_curh] + jae .no_mouse_area + + sub cx, [X_UNDER_subtraction_CUR_hot_x] + jb .no_mouse_area + + ror ecx, 16 +;-------------------------------------- +; check mouse area for putpixel + push eax + call check_mouse_area_for_putpixel_new.1 + mov [edx], eax + pop eax + jmp .skip +; store to real LFB +;-------------------------------------- +align 4 +.no_mouse_area: + mov [edx], eax +;-------------------------------------- +align 4 +.skip: +; add pixel + add edx, 4 + inc ebp + dec edi + jnz .new_x +; add line + add edx, [drbar.line_inc_scr] + add ebp, [drbar.line_inc_map] +; drawing gradient bars + test bh, 0x80 + jz @f + test al, al + jz @f + dec al +;-------------------------------------- +align 4 +@@: + dec esi + jnz .new_y + jmp draw_bar_end_32.end +;------------------------------------------------------------------------------ +align 4 +vesa20_drawbackground_tiled: + pushad +; External loop for all y from start to end + mov ebx, [draw_data+32+RECT.top] ; y start +;-------------------------------------- +align 4 +dp2: + mov ebp, [draw_data+32+RECT.left] ; x start +; 1) Calculate pointers in WinMapAddress (does pixel belong to OS thread?) [ebp] +; and LFB data (output for our function) [edi] +; mov eax, [BytesPerScanLine] +; mul ebx + mov eax, [BPSLine_calc_area+ebx*4] + xchg ebp, eax + add ebp, eax + add ebp, eax + add ebp, eax + cmp byte [_display.bpp], 24 ; 24 or 32 bpp ? - x size + jz @f + add ebp, eax +;-------------------------------------- +align 4 +@@: + add ebp, LFB_BASE +; ebp:=Y*BytesPerScanLine+X*BytesPerPixel+AddrLFB + call calculate_edi + xchg edi, ebp + add ebp, [_WinMapAddress] +; Now eax=x, ebx=y, edi->output, ebp=offset in WinMapAddress +; 2) Calculate offset in background memory block + push eax + xor edx, edx + mov eax, ebx + div dword [BgrDataHeight] ; edx := y mod BgrDataHeight + pop eax + push eax + mov ecx, [BgrDataWidth] + mov esi, edx + imul esi, ecx ; esi := (y mod BgrDataHeight) * BgrDataWidth + xor edx, edx + div ecx ; edx := x mod BgrDataWidth + sub ecx, edx + add esi, edx ; esi := (y mod BgrDataHeight)*BgrDataWidth + (x mod BgrDataWidth) + pop eax + lea esi, [esi*3] + add esi, [img_background] + xor edx, edx + inc edx +; 3) Loop through redraw rectangle and copy background data +; Registers meaning: +; eax = x, ebx = y (screen coordinates) +; ecx = deltax - number of pixels left in current tile block +; edx = 1 +; esi -> bgr memory, edi -> output +; ebp = offset in WinMapAddress +;-------------------------------------- +align 4 +dp3: + cmp [ebp], dl + jnz nbgp +;-------------------------------------- + push eax ecx + + mov ecx, eax + shl ecx, 16 + add ecx, ebx + + mov eax, [esi] + +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: + and eax, 0xffffff +; check mouse area for putpixel + call [_display.check_mouse] +;-------------------------------------- +align 4 +.no_mouseunder: +; store to real LFB + mov [edi], ax + shr eax, 16 + mov [edi+2], al + + pop ecx eax +;-------------------------------------- +align 4 +nbgp: + add esi, 3 + add edi, 3 +;-------------------------------------- +align 4 +@@: + 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 + add eax, edx + cmp eax, [draw_data+32+RECT.right] + ja dp4 + sub ecx, edx + jnz dp3 +; next tile block on x-axis + mov ecx, [BgrDataWidth] + sub esi, ecx + sub esi, ecx + sub esi, ecx + jmp dp3 +;-------------------------------------- +align 4 +dp4: +; next scan line + inc ebx + cmp ebx, [draw_data+32+RECT.bottom] + jbe dp2 + popad + mov [EGA_counter], 1 + cmp [SCR_MODE], 0x12 + jne @f + call VGA_drawbackground +;-------------------------------------- +align 4 +@@: + ret +;------------------------------------------------------------------------------ +align 4 +vesa20_drawbackground_stretch: + pushad +; Helper variables +; calculate 2^32*(BgrDataWidth-1) mod (ScreenWidth-1) + mov eax, [BgrDataWidth] + dec eax + xor edx, edx + div dword [Screen_Max_X] + push eax ; high + xor eax, eax + div dword [Screen_Max_X] + push eax ; low +; the same for height + mov eax, [BgrDataHeight] + dec eax + xor edx, edx + div dword [Screen_Max_Y] + push eax ; high + xor eax, eax + div dword [Screen_Max_Y] + push eax ; low +; External loop for all y from start to end + mov ebx, [draw_data+32+RECT.top] ; y start + mov ebp, [draw_data+32+RECT.left] ; x start +; 1) Calculate pointers in WinMapAddress (does pixel belong to OS thread?) [ebp] +; and LFB data (output for our function) [edi] +; mov eax, [BytesPerScanLine] +; mul ebx + mov eax, [BPSLine_calc_area+ebx*4] + xchg ebp, eax + add ebp, eax + add ebp, eax + add ebp, eax + cmp byte [_display.bpp], 24 ; 24 or 32 bpp ? - x size + jz @f + add ebp, eax +;-------------------------------------- +align 4 +@@: +; ebp:=Y*BytesPerScanLine+X*BytesPerPixel+AddrLFB + call calculate_edi + xchg edi, ebp +; Now eax=x, ebx=y, edi->output, ebp=offset in WinMapAddress + push ebx + push eax +; 2) Calculate offset in background memory block + mov eax, ebx + imul ebx, dword [esp+12] + mul dword [esp+8] + add edx, ebx ; edx:eax = y * 2^32*(BgrDataHeight-1)/(ScreenHeight-1) + mov esi, edx + imul esi, [BgrDataWidth] + push edx + push eax + mov eax, [esp+8] + mul dword [esp+28] + push eax + mov eax, [esp+12] + mul dword [esp+28] + add [esp], edx + pop edx ; edx:eax = x * 2^32*(BgrDataWidth-1)/(ScreenWidth-1) + add esi, edx + lea esi, [esi*3] + add esi, [img_background] + push eax + push edx + push esi +; 3) Smooth horizontal +;-------------------------------------- +align 4 +bgr_resmooth0: + mov ecx, [esp+8] + mov edx, [esp+4] + mov esi, [esp] + push edi + mov edi, bgr_cur_line + call smooth_line +;-------------------------------------- +align 4 +bgr_resmooth1: + mov eax, [esp+16+4] + inc eax + cmp eax, [BgrDataHeight] + jae bgr.no2nd + mov ecx, [esp+8+4] + mov edx, [esp+4+4] + mov esi, [esp+4] + add esi, [BgrDataWidth] + add esi, [BgrDataWidth] + add esi, [BgrDataWidth] + mov edi, bgr_next_line + call smooth_line +;-------------------------------------- +align 4 +bgr.no2nd: + pop edi +;-------------------------------------- +align 4 +sdp3: + xor esi, esi + mov ecx, [esp+12] +; 4) Loop through redraw rectangle and copy background data +; Registers meaning: +; esi = offset in current line, edi -> output +; ebp = offset in WinMapAddress +; dword [esp] = offset in bgr data +; qword [esp+4] = x * 2^32 * (BgrDataWidth-1) / (ScreenWidth-1) +; qword [esp+12] = y * 2^32 * (BgrDataHeight-1) / (ScreenHeight-1) +; dword [esp+20] = x +; dword [esp+24] = y +; precalculated constants: +; qword [esp+28] = 2^32*(BgrDataHeight-1)/(ScreenHeight-1) +; qword [esp+36] = 2^32*(BgrDataWidth-1)/(ScreenWidth-1) +;-------------------------------------- +align 4 +sdp3a: + mov eax, [_WinMapAddress] + cmp [ebp+eax], byte 1 + jnz snbgp + mov eax, [bgr_cur_line+esi] + test ecx, ecx + jz .novert + mov ebx, [bgr_next_line+esi] + call [overlapping_of_points_ptr] +;-------------------------------------- +align 4 +.novert: + push ecx +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: + mov ecx, [esp+20+4] ;x + shl ecx, 16 + add ecx, [esp+24+4] ;y +; check mouse area for putpixel + call [_display.check_mouse] +;-------------------------------------- +align 4 +.no_mouseunder: +; store to real LFB + mov [LFB_BASE+edi], ax + shr eax, 16 + mov [LFB_BASE+edi+2], al + pop ecx +;-------------------------------------- +align 4 +snbgp: + cmp byte [_display.bpp], 25 + sbb edi, -4 + add ebp, 1 + mov eax, [esp+20] + add eax, 1 + mov [esp+20], eax + add esi, 4 + cmp eax, [draw_data+32+RECT.right] + jbe sdp3a +;-------------------------------------- +align 4 +sdp4: +; next y + mov ebx, [esp+24] + add ebx, 1 + mov [esp+24], ebx + cmp ebx, [draw_data+32+RECT.bottom] + ja sdpdone +; advance edi, ebp to next scan line + sub eax, [draw_data+32+RECT.left] + sub ebp, eax + add ebp, [Screen_Max_X] + add ebp, 1 + sub edi, eax + sub edi, eax + sub edi, eax + cmp byte [_display.bpp], 24 + jz @f + sub edi, eax +;-------------------------------------- +align 4 +@@: + add edi, [_display.pitch] +; restore ecx,edx; advance esi to next background line + mov eax, [esp+28] + mov ebx, [esp+32] + add [esp+12], eax + mov eax, [esp+16] + adc [esp+16], ebx + sub eax, [esp+16] + mov ebx, eax + lea eax, [eax*3] + imul eax, [BgrDataWidth] + sub [esp], eax + mov eax, [draw_data+32+RECT.left] + mov [esp+20], eax + test ebx, ebx + jz sdp3 + cmp ebx, -1 + jnz bgr_resmooth0 + push edi + mov esi, bgr_next_line + mov edi, bgr_cur_line + mov ecx, [Screen_Max_X] + inc ecx + rep movsd + jmp bgr_resmooth1 +;-------------------------------------- +align 4 +sdpdone: + add esp, 44 + popad + mov [EGA_counter], 1 + cmp [SCR_MODE], 0x12 + jne @f + call VGA_drawbackground +;-------------------------------------- +align 4 +@@: + ret + +uglobal +;-------------------------------------- +align 4 +bgr_cur_line rd 1920 ; maximum width of screen +bgr_next_line rd 1920 +;-------------------------------------- +endg +;-------------------------------------- +align 4 +smooth_line: + mov al, [esi+2] + shl eax, 16 + mov ax, [esi] + test ecx, ecx + jz @f + mov ebx, [esi+2] + shr ebx, 8 + call [overlapping_of_points_ptr] +;-------------------------------------- +align 4 +@@: + stosd + mov eax, [esp+20+8] + add eax, 1 + mov [esp+20+8], eax + cmp eax, [draw_data+32+RECT.right] + ja @f + add ecx, [esp+36+8] + mov eax, edx + adc edx, [esp+40+8] + sub eax, edx + lea eax, [eax*3] + sub esi, eax + jmp smooth_line +;-------------------------------------- +align 4 +@@: + mov eax, [draw_data+32+RECT.left] + mov [esp+20+8], eax + ret +;------------------------------------------------------------------------------ +align 16 +overlapping_of_points: +if 0 +; this version of procedure works, but is slower than next version + push ecx edx + mov edx, eax + push esi + shr ecx, 24 + mov esi, ecx + mov ecx, ebx + movzx ebx, dl + movzx eax, cl + sub eax, ebx + movzx ebx, dh + imul eax, esi + add dl, ah + movzx eax, ch + sub eax, ebx + imul eax, esi + add dh, ah + ror ecx, 16 + ror edx, 16 + movzx eax, cl + movzx ebx, dl + sub eax, ebx + imul eax, esi + pop esi + add dl, ah + mov eax, edx + pop edx + ror eax, 16 + pop ecx + ret +else + push ecx edx + mov edx, eax + push esi + shr ecx, 26 + mov esi, ecx + mov ecx, ebx + shl esi, 9 + movzx ebx, dl + movzx eax, cl + sub eax, ebx + movzx ebx, dh + add dl, [BgrAuxTable+(eax+0x100)+esi] + movzx eax, ch + sub eax, ebx + add dh, [BgrAuxTable+(eax+0x100)+esi] + ror ecx, 16 + ror edx, 16 + movzx eax, cl + movzx ebx, dl + sub eax, ebx + add dl, [BgrAuxTable+(eax+0x100)+esi] + pop esi + mov eax, edx + pop edx + ror eax, 16 + pop ecx + ret +end if + +iglobal +;-------------------------------------- +align 4 +overlapping_of_points_ptr dd overlapping_of_points +;-------------------------------------- +endg +;------------------------------------------------------------------------------ +align 4 +init_background: + mov edi, BgrAuxTable + xor edx, edx +;-------------------------------------- +align 4 +.loop2: + mov eax, edx + shl eax, 8 + neg eax + mov ecx, 0x200 +;-------------------------------------- +align 4 +.loop1: + mov byte [edi], ah + inc edi + add eax, edx + loop .loop1 + add dl, 4 + jnz .loop2 + test byte [cpu_caps+(CAPS_MMX/8)], 1 shl (CAPS_MMX mod 8) + jz @f + mov [overlapping_of_points_ptr], overlapping_of_points_mmx +;-------------------------------------- +align 4 +@@: + ret +;------------------------------------------------------------------------------ +align 16 +overlapping_of_points_mmx: + movd mm0, eax + movd mm4, eax + movd mm1, ebx + pxor mm2, mm2 + punpcklbw mm0, mm2 + punpcklbw mm1, mm2 + psubw mm1, mm0 + movd mm3, ecx + psrld mm3, 24 + packuswb mm3, mm3 + packuswb mm3, mm3 + pmullw mm1, mm3 + psrlw mm1, 8 + packuswb mm1, mm2 + paddb mm4, mm1 + movd eax, mm4 + ret +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-process/video/vga.inc b/kernel/branches/kolibri-process/video/vga.inc new file mode 100644 index 000000000..07df16442 --- /dev/null +++ b/kernel/branches/kolibri-process/video/vga.inc @@ -0,0 +1,534 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; VGA.INC ;; +;; ;; +;; 640x480 mode 0x12 VGA functions for MenuetOS ;; +;; ;; +;; Paul Butcher, paul.butcher@asa.co.uk ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 3606 $ + +;------------------------------------------------------------------------------ +align 4 +paletteVGA: + +;16 colour palette + mov dx, 0x3c8 + mov al, 0 + out dx, al + + mov ecx, 16 + mov dx, 0x3c9 + xor eax, eax +;-------------------------------------- +align 4 +palvganew: + mov al, 0 + test ah, 4 + jz palvgalbl1 + add al, 31 + test ah, 8 + jz palvgalbl1 + add al, 32 +;-------------------------------------- +align 4 +palvgalbl1: + out dx, al; red 0,31 or 63 + mov al, 0 + test ah, 2 + jz palvgalbl2 + add al, 31 + test ah, 8 + jz palvgalbl2 + add al, 32 +;-------------------------------------- +align 4 +palvgalbl2: + out dx, al; blue 0,31 or 63 + mov al, 0 + test ah, 1 + jz palvgalbl3 + add al, 31 + test ah, 8 + jz palvgalbl3 + add al, 32 +;-------------------------------------- +align 4 +palvgalbl3: + out dx, al; green 0,31 or 63 + add ah, 1 + loop palvganew +; mov dx, 3ceh +; mov ax, 0005h +; out dx, ax + ret +;------------------------------------------------------------------------------ +align 4 +palette320x200: + mov edx, 0x3c8 + xor eax, eax + out dx, al + mov ecx, 256 + mov edx, 0x3c9 + xor eax, eax +;-------------------------------------- +align 4 +palnew: + mov al, 0 + test ah, 64 + jz pallbl1 + add al, 21 +;-------------------------------------- +align 4 +pallbl1: + test ah, 128 + jz pallbl2 + add al, 42 +;-------------------------------------- +align 4 +pallbl2: + out dx, al + mov al, 0 + test ah, 8 + jz pallbl3 + add al, 8 +;-------------------------------------- +align 4 +pallbl3: + test ah, 16 + jz pallbl4 + add al, 15 +;-------------------------------------- +align 4 +pallbl4: + test ah, 32 + jz pallbl5 + add al, 40 +;-------------------------------------- +align 4 +pallbl5: + out dx, al + mov al, 0 + test ah, 1 + jz pallbl6 + add al, 8 +;-------------------------------------- +align 4 +pallbl6: + test ah, 2 + jz pallbl7 + add al, 15 +;-------------------------------------- +align 4 +pallbl7: + test ah, 4 + jz pallbl8 + add al, 40 +;-------------------------------------- +align 4 +pallbl8: + out dx, al + add ah, 1 + loop palnew + ret +;------------------------------------------------------------------------------ +align 4 +uglobal + novesachecksum dd 0x0 + EGA_counter db 0 + VGA_drawing_screen db 0 + VGA_8_pixels: + rb 16 + temp: + .cx dd 0 +endg +;------------------------------------------------------------------------------ +align 4 +checkVga_N13: + cmp [SCR_MODE], 0x13 + jne @f + + pushad + cmp [EGA_counter], 1 + je novesal + mov ecx, [MOUSE_X] + cmp ecx, [novesachecksum] + jne novesal + popad +;-------------------------------------- +align 4 +@@: + ret +;-------------------------------------- +align 4 +novesal: + mov [novesachecksum], ecx + mov ecx, 0 + movzx eax, word [MOUSE_Y] + cmp eax, 100 + jge m13l3 + mov eax, 100 +;-------------------------------------- +align 4 +m13l3: + cmp eax, 480-100 + jbe m13l4 + mov eax, 480-100 +;-------------------------------------- +align 4 +m13l4: + sub eax, 100 + imul eax, 640*4 + add ecx, eax + movzx eax, word [MOUSE_X] + cmp eax, 160 + jge m13l1 + mov eax, 160 +;-------------------------------------- +align 4 +m13l1: + cmp eax, 640-160 + jbe m13l2 + mov eax, 640-160 +;-------------------------------------- +align 4 +m13l2: + sub eax, 160 + shl eax, 2 + add ecx, eax + mov esi, [LFBAddress] + add esi, ecx + mov edi, VGABasePtr + mov edx, 200 + mov ecx, 320 + cld +;-------------------------------------- +align 4 +m13pix: + lodsd + test eax, eax + jz .save_pixel + push eax + mov ebx, eax + and eax, (128+64+32) ; blue + shr eax, 5 + and ebx, (128+64+32)*256; green + shr ebx, 8+2 + add eax, ebx + pop ebx + and ebx, (128+64)*256*256; red + shr ebx, 8+8 + add eax, ebx +;-------------------------------------- +align 4 +.save_pixel: + stosb + loop m13pix + mov ecx, 320 + add esi, 4*(640-320) + dec edx + jnz m13pix + mov [EGA_counter], 0 + popad + ret +;------------------------------------------------------------------------------ +align 4 +VGA_drawbackground: +; draw all + pushad + mov esi, [LFBAddress] + mov edi, VGABasePtr + mov ebx, 640/32; 640*480/(8*4) + mov edx, 480 +;-------------------------------------- +align 4 +@@: + push ebx edx esi edi + shl edx, 9 + lea edx, [edx+edx*4] + add esi, edx + shr edx, 5 + add edi, edx + call VGA_draw_long_line + pop edi esi edx ebx + dec edx + jnz @r + call VGA_draw_long_line_1 + popad + ret +;------------------------------------------------------------------------------ +align 4 +VGA_draw_long_line: + mov dx, 3ceh + mov ax, 0ff08h + cli + out dx, ax + mov ax, 0005h + out dx, ax +;-------------------------------------- +align 4 +m12pix: + call VGA_draw_32_pixels + dec ebx + jnz m12pix + mov dx, 3c4h + mov ax, 0ff02h + out dx, ax + mov dx, 3ceh + mov ax, 0205h + out dx, ax + mov dx, 3ceh + mov al, 08h + out dx, al + sti + ret +;------------------------------------------------------------------------------ +align 4 +VGA_draw_32_pixels: + xor eax, eax + mov ebp, VGA_8_pixels + mov [ebp], eax + mov [ebp+4], eax + mov [ebp+8], eax + mov [ebp+12], eax + mov ch, 4 +;-------------------------------------- +align 4 +.main_loop: + mov cl, 8 +;-------------------------------------- +align 4 +.convert_pixels_to_VGA: + lodsd ; eax = 24bit colour + test eax, eax + jz .end + rol eax, 8 + mov al, ch + ror eax, 8 + mov ch, 1 + dec cl + shl ch, cl + cmp al, 85 + jbe .p13green + or [ebp], ch + cmp al, 170 + jbe .p13green + or [ebp+12], ch +;-------------------------------------- +align 4 +.p13green: + cmp ah, 85 + jbe .p13red + or [ebp+4], ch + cmp ah, 170 + jbe .p13red + or [ebp+12], ch +;-------------------------------------- +align 4 +.p13red: + shr eax, 8 + cmp ah, 85 + jbe .p13cont + or [ebp+8], ch + cmp ah, 170 + jbe .p13cont + or [ebp+12], ch +;-------------------------------------- +align 4 +.p13cont: + ror eax, 8 + mov ch, ah + inc cl +;-------------------------------------- +align 4 +.end: + dec cl + jnz .convert_pixels_to_VGA + inc ebp + dec ch + jnz .main_loop + push esi + sub ebp, 4 + mov esi, ebp + mov dx, 3c4h + mov ah, 1h +;-------------------------------------- +align 4 +@@: + mov al, 02h + out dx, ax + xchg ax, bp + lodsd + mov [edi], eax + xchg ax, bp + shl ah, 1 + cmp ah, 10h + jnz @r + add edi, 4 + pop esi + ret +;------------------------------------------------------------------------------ +align 4 +VGA_putpixel: + ; eax = x + ; ebx = y + mov ecx, eax + mov eax, [esp+32-8+4] ; color +;-------------------------------------- +; check for hardware cursor + cmp [_display.select_cursor], select_cursor + je @f + cmp [_display.select_cursor], 0 + jne .no_mouseunder +;-------------------------------------- +align 4 +@@: + push ecx + shl ecx, 16 + mov cx, bx +; check mouse area for putpixel + test eax, 0x04000000 + jnz @f + call [_display.check_mouse] +;-------------------------------------- +align 4 +@@: + pop ecx +;-------------------------------------- +align 4 +.no_mouseunder: + shl ebx, 9 + lea ebx, [ebx+ebx*4] ; умножение на 5 + lea edx, [ebx+ecx*4] ; + x*BytesPerPixel (Vesa2.0 32) + mov edi, edx + add edi, [LFBAddress] ; + LFB address + mov [edi], eax ; write to LFB for Vesa2.0 + shr edx, 5 ; change BytesPerPixel to 1/8 + mov edi, edx + add edi, VGABasePtr ; address of pixel in VGA area + and ecx, 0x07 ; bit no. (modulo 8) + pushfd + ; edi = address, eax = 24bit colour, ecx = bit no. (modulo 8) + xor edx, edx + test eax, eax + jz .p13cont + cmp al, 85 + jbe .p13green + or dl, 0x01 + cmp al, 170 + jbe .p13green + or dl, 0x08 +;-------------------------------------- +align 4 +.p13green: + cmp ah, 85 + jbe .p13red + or dl, 0x02 + cmp ah, 170 + jbe .p13red + or dl, 0x08 +;-------------------------------------- +align 4 +.p13red: + shr eax, 8 + cmp ah, 85 + jbe .p13cont + or dl, 0x04 + cmp ah, 170 + jbe .p13cont + or dl, 0x08 +;-------------------------------------- +align 4 +.p13cont: + ror edx, 8 + inc cl + xor eax, eax + inc ah + shr ax, cl + mov dx, 3cfh + cli + out dx, al + mov al, [edi] ; dummy read + rol edx, 8 + mov [edi], dl + popfd + ret +;------------------------------------------------------------------------------ +align 4 +VGA__putimage: +; ecx = size [x|y] +; edx = coordinates [x|y] + pushad + rol edx, 16 + movzx eax, dx + rol edx, 16 + movzx ebx, dx + movzx edx, cx + rol ecx, 16 + movzx ecx, cx + call VGA_draw_bar_1 + popad + ret +;------------------------------------------------------------------------------ +align 4 +VGA_draw_bar: +; eax cx +; ebx cy +; ecx xe +; edx ye + pushad + sub ecx, eax + sub edx, ebx + and eax, 0xffff + and ebx, 0xffff + and ecx, 0xffff + and edx, 0xffff + call VGA_draw_bar_1 + popad + ret +;------------------------------------------------------------------------------ +align 4 +VGA_draw_bar_1: + mov [temp.cx], eax + mov eax, [TASK_BASE] + add ebx, [eax-twdw + 4] + mov eax, [eax-twdw + 0] + add eax, [temp.cx] + and eax, 0xfff8 + shl ebx, 9 + lea ebx, [ebx+ebx*4]; умножение на 5 + lea ebx, [ebx+eax*4] ; + x*BytesPerPixel (Vesa2.0 32) + mov esi, ebx + add esi, [LFBAddress] ; + LFB address + shr ebx, 5 ; change BytesPerPixel to 1/8 + mov edi, ebx + add edi, VGABasePtr ; address of pixel in VGA area + mov ebx, ecx + shr ebx, 5 + inc ebx +;-------------------------------------- +align 4 +.main_loop: + call VGA_draw_long_line_1 + dec edx + jnz .main_loop + call VGA_draw_long_line_1 + ret +;------------------------------------------------------------------------------ +align 4 +VGA_draw_long_line_1: + push ebx edx esi edi + shl edx, 9 + lea edx, [edx+edx*4] + add esi, edx + shr edx, 5 + add edi, edx + call VGA_draw_long_line + pop edi esi edx ebx + ret +;------------------------------------------------------------------------------