;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$


DRV_COMPAT   equ  5  ;minimal required drivers version
DRV_CURRENT  equ  5  ;current drivers model version

DRV_VERSION equ (DRV_COMPAT shl 16) or DRV_CURRENT
PID_KERNEL  equ 1    ;os_idle thread

align 4
proc attach_int_handler stdcall, irq:dword, handler:dword, access_rights:dword

         push ebx

         mov  ebx, [irq]                   ;irq num
         test ebx, ebx
         jz   .err
         cmp  ebx, 15                      ; hidnplayr says: we only have 16 IRQ's
         ja   .err
         mov  eax, [handler]
         test eax, eax
         jz   .err
         cmp  [irq_owner + 4 * ebx], 0
         je   @f

         mov  ecx, [irq_rights + 4 * ebx]  ; Rights : 0 - full access, 1 - read only, 2 - forbidden
         test ecx, ecx
         jnz  .err

@@:
         mov  [irq_tab+ebx*4], eax

         mov  eax, [access_rights]
         mov  [irq_rights + 4 * ebx], eax

         mov  [irq_owner + 4 * ebx], PID_KERNEL  ; all handlers belong to a kernel

         stdcall enable_irq, [irq]
         pop ebx
         mov eax, 1
         ret
.err:
         pop ebx
         xor eax, eax
         ret
endp

uglobal

	irq_rights	 rd	 16

endg

proc get_int_handler stdcall, irq:dword

	mov	eax, [irq]

	cmp	[irq_rights + 4 * eax], dword 1
	ja	.err

	mov	eax, [irq_tab + 4 * eax]
	ret

     .err:
	xor	eax, eax
	ret

endp

align 4
proc  detach_int_handler

	   ret
endp

align 4
proc enable_irq stdcall, irq_line:dword
	   mov ebx, [irq_line]
	   mov edx, 0x21
	   cmp ebx, 8
	   jb @F
	   mov edx, 0xA1
	   sub ebx,8
@@:
	   in al,dx
	   btr eax, ebx
	   out dx, al
	   ret
endp

align 16
;; proc irq_serv

irq_serv:

.irq_1:
	   push 1
	   jmp .main
align 4
.irq_2:
	   push 2
	   jmp .main
align 4
.irq_3:
	   push 3
	   jmp .main
align 4
.irq_4:
	   push 4
	   jmp .main
align 4
.irq_5:
	   push 5
	   jmp .main
; align 4
; .irq_6:
;	   push 6
;	   jmp .main
align 4
.irq_7:
	   push 7
	   jmp .main
align 4
.irq_8:
	   push 8
	   jmp .main
align 4
.irq_9:
	   push 9
	   jmp .main
align 4
.irq_10:
	   push 10
	   jmp .main
align 4
.irq_11:
	   push 11
	   jmp .main
align 4
.irq_12:
	   push 12
	   jmp .main
; align 4
; .irq_13:
;	   push 13
;	   jmp .main
; align 4
; .irq_14:
;	   push 14
;	   jmp .main
; align 4
; .irq_15:
;	   push 15
;	   jmp .main

align 16
.main:
	   save_ring3_context
	   mov	 eax, [esp + 32]
	   mov	 bx, app_data  ;os_data
	   mov	 ds, bx
	   mov	 es, bx

	   cmp	 [v86_irqhooks+eax*8], 0
	   jnz	 v86_irq

	   mov ebx, [irq_tab+eax*4]
	   test ebx, ebx
	   jz .exit

	   call ebx
	   mov	[check_idle_semaphore],5

.exit:

	   cmp dword [esp + 32], 8
	   mov al, 0x20
	   jb @f
	   out 0xa0, al
@@:
	   out 0x20, al

	   restore_ring3_context
	   add	 esp, 4

	   iret

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 edx
	   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 edx ebx
	   ret
endp

align 4
proc pci_read16 stdcall, bus:dword, devfn:dword, reg:dword
	   push ebx edx
	   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 edx ebx
	   ret
endp

align 4
proc pci_read8 stdcall, bus:dword, devfn:dword, reg:dword
	   push ebx edx
	   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 edx ebx
	   ret
endp

align 4
proc pci_write8 stdcall, bus:dword, devfn:dword, reg:dword, val:dword
	   push ebx edx
	   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 edx ebx
	   ret
endp

align 4
proc pci_write16 stdcall, bus:dword, devfn:dword, reg:dword, val:dword
	   push ebx edx
	   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 edx ebx
	   ret
endp

align 4
proc pci_write32 stdcall, bus:dword, devfn:dword, reg:dword, val:dword
	   push ebx edx
	   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 edx 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], SRV.sizeof
	   jne .fail

	   stdcall [edi+SRV.srv_proc], 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], SRV.sizeof
	   jne .fail

	   stdcall [eax+SRV.srv_proc], 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_OFFSET
	   je .not_load

	   stdcall strncmp, edx, [sz_name], 16
	   test eax, eax
	   je .ok

	   mov edx, [edx+SRV.fd]
	   jmp @B
.not_load:
	   pop ebp
	   jmp load_driver
.ok:
	   mov eax, edx
	   ret
endp

align 4
proc reg_service stdcall, name:dword, handler:dword

	   push ebx

           xor eax, eax

	   cmp [name], eax
	   je .fail

	   cmp [handler], eax
	   je .fail

       mov eax, SRV.sizeof
       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], SRV.sizeof

	   mov ebx, srv.fd-SRV_FD_OFFSET
	   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
	   push eax
	   lea eax, [cmd]
	   call file_system_lfn
	   pop eax
	   popad
	   ret
endp

; description
;  allocate kernel memory and loads the specified file
;
; param
;  file_name= full 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

	   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
       pushfd
       cli
	   stdcall unpack, [file], eax
       popfd
	   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

align 4
proc get_proc_ex stdcall, proc_name:dword, imports:dword

.look_up:
	   mov edx, [imports]
	   test edx, edx
	   jz .end
	   mov edx, [edx]
	   test edx, edx
	   jz .end
.next:
	   mov eax, [edx]
	   test eax, eax
	   jz .next_table

	   push edx
       stdcall strncmp, eax, [proc_name], 256
	   pop edx
	   test eax, eax
	   jz .ok

	   add edx,8
	   jmp .next
.next_table:
	   add [imports], 4
	   jmp .look_up
.ok:
	   mov eax, [edx+4]
	   ret
.end:
	   xor eax, eax
	   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+CSYM.SectionNumber]
	   test ebx, ebx
	   jnz .internal
	   mov eax, dword [edi+CSYM.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+CSYM.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+CFS.VirtualAddress]
	   add [edi+CSYM.Value], eax
.next:
	   add edi, CSYM_SIZE
	   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+CFH.nSections]
	   mov [n_sec], ebx
	   lea esi, [eax+20]
.fix_sec:
	   mov edi, [esi+CFS.PtrReloc]
	   add edi, [coff]

	   movzx ecx, [esi+CFS.NumReloc]
	   test ecx, ecx
	   jz .next
.reloc_loop:
	   mov ebx, [edi+CRELOC.SymIndex]
	   add ebx,ebx
	   lea ebx,[ebx+ebx*8]
	   add ebx, [sym]

	   mov edx, [ebx+CSYM.Value]

	   cmp [edi+CRELOC.Type], 6
	   je .dir_32

	   cmp [edi+CRELOC.Type], 20
	   jne .next_reloc
.rel_32:
	   mov eax, [edi+CRELOC.VirtualAddress]
	   add eax, [esi+CFS.VirtualAddress]
	   sub edx, eax
	   sub edx, 4
	   jmp .fix
.dir_32:
	   mov eax, [edi+CRELOC.VirtualAddress]
	   add eax, [esi+CFS.VirtualAddress]
.fix:
	   add eax, [delta]
	   add [eax], edx
.next_reloc:
	   add edi, 10
	   dec ecx
	   jnz .reloc_loop
.next:
	   add esi, COFF_SECTION_SIZE
	   dec [n_sec]
	   jnz .fix_sec
.exit:
	   ret
endp

proc rebase_coff stdcall uses ebx esi, coff:dword, sym:dword, \
	delta:dword
	   locals
	     n_sec     dd ?
	   endl

	   mov eax, [coff]
	   movzx ebx, [eax+CFH.nSections]
	   mov [n_sec], ebx
	   lea esi, [eax+20]
	   mov edx, [delta]
.fix_sec:
	   mov edi, [esi+CFS.PtrReloc]
	   add edi, [coff]

	   movzx ecx, [esi+CFS.NumReloc]
	   test ecx, ecx
	   jz .next
.reloc_loop:
	   cmp [edi+CRELOC.Type], 6
	   jne .next_reloc
.dir_32:
	   mov eax, [edi+CRELOC.VirtualAddress]
	   add eax, [esi+CFS.VirtualAddress]
	   add [eax+edx], edx
.next_reloc:
	   add edi, 10
	   dec ecx
	   jnz .reloc_loop
.next:
	   add esi, COFF_SECTION_SIZE
	   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 ?

	     exports   dd ?   ;fake exports table
		       dd ?
	     file_name rb 13+16+4+1	 ; '/sys/drivers/<up-to-16-chars>.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+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, 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+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 ebx, [exports]
	   mov dword [ebx], kernel_export
	   mov dword [ebx+4], 0
	   lea eax, [edx+20]

	   stdcall fix_coff_symbols, eax, [sym], [edx+CFH.nSymbols],\
				     [strings], ebx
	   test eax, eax
	   jz .link_fail

	   mov ebx, [coff]
	   stdcall fix_coff_relocs, ebx, [sym], 0

	   stdcall get_coff_sym,[sym],[ebx+CFH.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+CFH.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]
           cmp     dword [file_name+13], 'SOUN'
           jnz     @f
           cmp     dword [file_name+17], 'D.ob'
           jnz     @f
           cmp     word [file_name+21], 'j'
           jnz     @f
           mov     esi, aSis
           jmp     .redo
@@:
	   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+CFS.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

	   cli

; 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
	   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]
	   ret
.next_in_process:
	   mov esi, [esi+HDLL.fd]
	   jmp .scan_in_process
.not_in_process:

; scan in full list, compare timestamp
	   lea eax, [fileinfo]
	   stdcall get_fileinfo, edi, eax
	   test eax, eax
	   jnz .fail
	   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:
; 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+DLLDESCR.sizeof]
	   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
; 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

; calculate size of loaded DLL
	   mov edx, [coff]
	   movzx ecx, [edx+CFH.nSections]
	   xor ebx, ebx

	   add edx, 20
@@:
	   call coff_get_align
	   add ebx, eax
	   not eax
	   and ebx, eax
	   add ebx, [edx+CFS.SizeOfRawData]
	   add edx, COFF_SECTION_SIZE
	   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+CFH.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+CFS.VirtualAddress], ecx
	   add ecx, [edx+CFS.SizeOfRawData]
	   mov esi, [edx+CFS.PtrRawData]
	   push ecx
	   mov ecx, [edx+CFS.SizeOfRawData]
	   test esi, esi
	   jnz .copy
	   xor eax, eax
	   rep stosb
	   jmp .next
.copy:
	   add esi, [coff]
	   rep movsb
.next:
           pop ecx
	   add edx, COFF_SECTION_SIZE
	   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+CFH.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+CFH.nSections]
	   lea ecx, [ecx*5]
	   lea edi, [edi+ecx*8+20]
	   add edx, 20
@@:
	   movzx eax, [edx+CFS.NumReloc]
	   lea eax, [eax*5]
	   lea edi, [edi+eax*2]
	   add edx, COFF_SECTION_SIZE
	   sub ecx, 5
	   jnz @b
	   stdcall kernel_alloc, edi
	   test eax, eax
	   jz  .fail_and_free_data
	   mov edx, [coff]
	   movzx ecx, [edx+CFH.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+CFH.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+CFH.nSections]
	   lea edx, [ebx+20]
@@:
           movzx ecx, [edx+CFS.NumReloc]
           lea ecx, [ecx*5]
           mov esi, [edx+CFS.PtrReloc]
           mov [edx+CFS.PtrReloc], edi
           sub [edx+CFS.PtrReloc], ebx
           add esi, [coff]
           shr ecx, 1
           rep movsd
           adc ecx, ecx
           rep movsw
           add edx, COFF_SECTION_SIZE
           dec eax
           jnz @b
	   pop esi

; fixup symbols
	   mov edx, ebx
	   mov eax, [ebx+CFH.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+CFH.nSymbols],szEXPORTS
	   test eax, eax
	   jnz @F

	   stdcall get_coff_sym,[esi+DLLDESCR.symbols_ptr],[ebx+CFH.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]

.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, HDLL.sizeof
	   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]
	   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
	   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.dir_table]
	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.dir_table], 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.dir_table]
; 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_OFFSET
	   je .done
	   cmp [edx+SRV.magic], ' SRV'
	   jne .next
       cmp [edx+SRV.size], SRV.sizeof
	   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



if 0

irq:

.irq0:
	   pusfd
	   pushad
	   push IRQ_0
	   jmp .master
.irq_1:
	   pusfd
	   pushad
	   push IRQ_1
	   jmp .master

.master:
	   mov ax, app_data
	   mov ds, eax
	   mov es, eax
	   mov ebx, [esp+4]  ;IRQ_xx
	   mov eax, [irq_handlers+ebx+4]
	   call intr_handler
	   mov ecx, [esp+4]
	   cmp [irq_actids+ecx*4], 0
	   je @F
	   in al, 0x21
	   bts eax, ecx
	   out 0x21, al
	   mov al, 0x20
	   out 0x20, al
	   jmp .restart

.slave:
	   mov ax, app_data
	   mov ds, eax
	   mov es, eax
	   mov ebx, [esp+4]  ;IRQ_xx
	   mov eax, [irq_handlers+ebx+4]
	   call intr_handler
	   mov ecx, [esp+4]
	   sub ecx, 8
	   cmp [irq_actids+ecx*4], 0
	   je @F
	   in al, 0xA1
	   bts eax, ecx
	   out 0xA1, al
	   mov al, 0x20
	   out 0xA0, al
	   out 0x20, al
.restart:
	   mov ebx, [next_slot]
	   test ebx, ebx
	   jz @F
	   mov [next_task],0
	   mov esi, [prev_slot]
	   call do_change_task
	   add esp, 4
	   iretd

end if