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

$Revision$


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
        push    ebx
        mov     eax, [phis_addr]
        and     eax, not 0xFFF
        or      eax, [flags]
        mov     ebx, [lin_addr]
        shr     ebx, 12
        mov     [page_tabs+ebx*4], eax
        mov     eax, [lin_addr]
        invlpg  [eax]
        pop     ebx
        ret
endp

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_VAR+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_VAR+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_pgdir+(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_pgdir+(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.dir_table+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.dir_table], 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.dir_table]
        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
;           inc ebx
;           add edi, 0x1000
;           mov eax, [esi+ebx*4]
;           test eax, eax
;           jz @f
;          and eax, 0xFFFFF000
;           stdcall map_page, edi, eax

@@:
        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.dir_table]
        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
        cmp     dword [check_idle_semaphore], 20
        jge     .ipc_no_cis

        mov     dword [check_idle_semaphore], 5
.ipc_no_cis:
        push    0
        jmp     .ret
.no_pid:
        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, 25
        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

        mov     edi, edx
        stdcall load_PE, ecx
        mov     esi, eax
        test    eax, eax
        jz      @F

        push    edi
        push    DRV_ENTRY
        call    eax
        add     esp, 8
        test    eax, eax
        jz      @F

        mov     [eax+SRV.entry], esi

@@:
        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

.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


align 4
proc load_pe_driver stdcall, file:dword

        stdcall load_PE, [file]
        test    eax, eax
        jz      .fail

        mov     esi, eax
        stdcall eax, DRV_ENTRY
        test    eax, eax
        jz      .fail

        mov     [eax+SRV.entry], esi
        ret

.fail:
        xor     eax, eax
        ret
endp


align 4
proc init_mtrr

        cmp     [BOOT_VAR+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
@@:
        wrmsr
        inc     ecx
        cmp     ecx, 0x210
        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 stall stdcall, delay:dword
        push    ecx
        push    edx
        push    ebx
        push    eax

        mov     eax, [delay]
        mul     [stall_mcs]
        mov     ebx, eax      ;low
        mov     ecx, edx      ;high
        rdtsc
        add     ebx, eax
        adc     ecx, edx
@@:
        rdtsc
        sub     eax, ebx
        sbb     edx, ecx
        jb      @B

        pop     eax
        pop     ebx
        pop     edx
        pop     ecx
        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