;Mupen64plus - linkage_x86.asm                                           
;Copyright (C) 2009-2011 Ari64                                         
;                                                                      
;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.,                                       
;51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          

%ifdef ELF_TYPE
    %macro  cglobal 1
      global  %1
    %endmacro
    
    %macro  cextern 1
      extern  %1
    %endmacro
%else
    %macro  cglobal 1
      global  _%1
      %define %1 _%1
    %endmacro

    %macro  cextern 1
      extern  _%1
      %define %1 _%1
    %endmacro
%endif

cglobal dyna_linker
cglobal dyna_linker_ds
cglobal jump_vaddr_eax
cglobal jump_vaddr_ecx
cglobal jump_vaddr_edx
cglobal jump_vaddr_ebx
cglobal jump_vaddr_ebp
cglobal jump_vaddr_edi
cglobal verify_code_ds
cglobal verify_code_vm
cglobal verify_code
cglobal cc_interrupt
cglobal do_interrupt
cglobal fp_exception
cglobal fp_exception_ds
cglobal jump_syscall
cglobal jump_eret
cglobal new_dyna_start
cglobal invalidate_block_eax
cglobal invalidate_block_ecx
cglobal invalidate_block_edx
cglobal invalidate_block_ebx
cglobal invalidate_block_ebp
cglobal invalidate_block_esi
cglobal invalidate_block_edi
cglobal write_rdram_new
cglobal write_rdramb_new
cglobal write_rdramh_new
cglobal write_rdramd_new
cglobal read_nomem_new
cglobal read_nomemb_new
cglobal read_nomemh_new
cglobal read_nomemd_new
cglobal write_nomem_new
cglobal write_nomemb_new
cglobal write_nomemh_new
cglobal write_nomemd_new
cglobal breakpoint

cextern base_addr
cextern tlb_LUT_r
cextern jump_in
cextern add_link
cextern hash_table
cextern jump_dirty
cextern new_recompile_block
cextern g_cp0_regs
cextern get_addr_ht
cextern cycle_count
cextern get_addr
cextern branch_target
cextern memory_map
cextern pending_exception
cextern restore_candidate
cextern gen_interupt
cextern next_interupt
cextern stop
cextern last_count
cextern pcaddr
cextern clean_blocks
cextern reg
cextern hi
cextern lo
cextern invalidate_block
cextern address
cextern g_rdram
cextern cpu_byte
cextern cpu_hword
cextern cpu_word
cextern cpu_dword
cextern invalid_code
cextern readmem_dword
cextern check_interupt
cextern get_addr_32

section .bss
align 4

section .rodata
section .text

dyna_linker:
    ;eax = virtual target address
    ;ebx = instruction to patch
    mov     edi,    eax
    mov     ecx,    eax
    shr     edi,    12
    cmp     eax,    0C0000000h
    cmovge  ecx,    [tlb_LUT_r+edi*4]
    test    ecx,    ecx
    cmovz   ecx,    eax
    xor     ecx,    080000000h
    mov     edx,    2047
    shr     ecx,    12
    and     edx,    ecx
    or      edx,    2048
    cmp     ecx,    edx
    cmova   ecx,    edx
    ;jump_in lookup
    mov     edx,    [jump_in+ecx*4]
_A1:
    test    edx,    edx
    je      _A3
    mov     edi,    [edx]
    xor     edi,    eax
    or      edi,    [4+edx]
    je      _A2
    mov     edx,    DWORD [12+edx]
    jmp     _A1
_A2:
    mov     edi,    [ebx]
    mov     ebp,    esi
    lea     esi,    [4+ebx+edi*1]
    mov     edi,    eax
    pusha
    call    add_link
    popa
    mov     edi,    [8+edx]
    mov     esi,    ebp
    lea     edx,    [-4+edi]
    sub     edx,    ebx
    mov     DWORD [ebx],    edx
    jmp     edi
_A3:
    ;hash_table lookup
    mov     edi,    eax
    mov     edx,    eax
    shr     edi,    16
    shr     edx,    12
    xor     edi,    eax
    and     edx,    2047
    movzx   edi,    di
    shl     edi,    4
    cmp     ecx,    2048
    cmovc   ecx,    edx
    cmp     eax,    [hash_table+edi]
    jne     _A5
_A4:
    mov     edx,    [hash_table+4+edi]
    jmp     edx
_A5:
    cmp     eax,    [hash_table+8+edi]
    lea     edi,    [8+edi]
    je      _A4
    ;jump_dirty lookup
    mov     edx,    [jump_dirty+ecx*4]
_A6:
    test    edx,    edx
    je      _A8
    mov     ecx,    [edx]
    xor     ecx,    eax
    or      ecx,    [4+edx]
    je      _A7
    mov     edx,    DWORD [12+edx]
    jmp     _A6
_A7:
    mov     edx,    [8+edx]
    ;hash_table insert
    mov     ebx,    [hash_table-8+edi]
    mov     ecx,    [hash_table-4+edi]
    mov     [hash_table-8+edi],    eax
    mov     [hash_table-4+edi],    edx
    mov     [hash_table+edi],      ebx
    mov     [hash_table+4+edi],    ecx
    jmp     edx
_A8:
    mov     edi,    eax
    pusha
    call    new_recompile_block
    test    eax,    eax
    popa
    je      dyna_linker
    ;pagefault
    mov     ebx,    eax
    mov     ecx,    008h

exec_pagefault:
    ;eax = instruction pointer
    ;ebx = fault address
    ;ecx = cause
    mov     edx,    [g_cp0_regs+48]
    add     esp,    -12
    mov     edi,    [g_cp0_regs+16]
    or      edx,    2
    mov     [g_cp0_regs+32],    ebx        ;BadVAddr
    and     edi,    0FF80000Fh
    mov     [g_cp0_regs+48],    edx        ;Status
    mov     [g_cp0_regs+52],    ecx        ;Cause
    mov     [g_cp0_regs+56],    eax        ;EPC
    mov     ecx,    ebx
    shr     ebx,    9
    and     ecx,    0FFFFE000h
    and     ebx,    0007FFFF0h
    mov     [g_cp0_regs+40],    ecx        ;EntryHI
    or      edi,    ebx
    mov     [g_cp0_regs+16],    edi        ;Context
    push     080000000h
    call    get_addr_ht
    add     esp,    16
    jmp     eax

;Special dynamic linker for the case where a page fault
;may occur in a branch delay slot
dyna_linker_ds:
    mov     edi,    eax
    mov     ecx,    eax
    shr     edi,    12
    cmp     eax,    0C0000000h
    cmovge  ecx,    [tlb_LUT_r+edi*4]
    test    ecx,    ecx
    cmovz   ecx,    eax
    xor     ecx,    080000000h
    mov     edx,    2047
    shr     ecx,    12
    and     edx,    ecx
    or      edx,    2048
    cmp     ecx,    edx
    cmova   ecx,    edx
    ;jump_in lookup
    mov     edx,    [jump_in+ecx*4]
_B1:
    test    edx,    edx
    je      _B3
    mov     edi,    [edx]
    xor     edi,    eax
    or      edi,    [4+edx]
    je      _B2
    mov     edx,    DWORD [12+edx]
    jmp     _B1
_B2:
    mov     edi,    [ebx]
    mov     ecx,    esi
    lea     esi,    [4+ebx+edi*1]
    mov     edi,    eax
    pusha
    call    add_link
    popa
    mov     edi,    [8+edx]
    mov     esi,    ecx
    lea     edx,    [-4+edi]
    sub     edx,    ebx
    mov     DWORD [ebx],    edx
    jmp     edi
_B3:
    ;hash_table lookup
    mov     edi,    eax
    mov     edx,    eax
    shr     edi,    16
    shr     edx,    12
    xor     edi,    eax
    and     edx,    2047
    movzx   edi,    di
    shl     edi,    4
    cmp     ecx,    2048
    cmovc   ecx,    edx
    cmp     eax,    [hash_table+edi]
    jne     _B5
_B4:
    mov     edx,    [hash_table+4+edi]
    jmp     edx
_B5:
    cmp     eax,    [hash_table+8+edi]
    lea     edi,    [8+edi]
    je      _B4
    ;jump_dirty lookup
    mov     edx,    [jump_dirty+ecx*4]
_B6:
    test    edx,    edx
    je      _B8
    mov     ecx,    [edx]
    xor     ecx,    eax
    or      ecx,    [4+edx]
    je      _B7
    mov     edx,    DWORD [12+edx]
    jmp     _B6
_B7:
    mov     edx,    [8+edx]
    ;hash_table insert
    mov     ebx,    [hash_table-8+edi]
    mov     ecx,    [hash_table-4+edi]
    mov     [hash_table-8+edi],    eax
    mov     [hash_table-4+edi],    edx
    mov     [hash_table+edi],      ebx
    mov     [hash_table+4+edi],    ecx
    jmp     edx
_B8:
    mov     edi,    eax
    and     edi,    0FFFFFFF8h
    inc     edi
    pusha
    call    new_recompile_block
    test    eax,    eax
    popa
    je      dyna_linker_ds
    ;pagefault
    and     eax,    0FFFFFFF8h
    mov     ecx,    080000008h    ;High bit set indicates pagefault in delay slot 
    mov     ebx,    eax
    sub     eax,    4
    jmp     exec_pagefault

jump_vaddr_eax:
    mov     edi,    eax
    jmp     jump_vaddr_edi

jump_vaddr_ecx:
    mov     edi,    ecx
    jmp     jump_vaddr_edi

jump_vaddr_edx:
    mov     edi,    edx
    jmp     jump_vaddr_edi

jump_vaddr_ebx:
    mov     edi,    ebx
    jmp     jump_vaddr_edi

jump_vaddr_ebp:
    mov     edi,    ebp

jump_vaddr_edi:
    mov     eax,    edi

jump_vaddr:
    ;Check hash table
    shr     eax,    16
    xor     eax,    edi
    movzx   eax,    ax
    shl     eax,    4
    cmp     edi,    [hash_table+eax]
    jne     _C2
_C1:
    mov     edi,    [hash_table+4+eax]
    jmp     edi
_C2:
    cmp     edi,    [hash_table+8+eax]
    lea     eax,    [8+eax]
    je      _C1
    ;No hit on hash table, call compiler
    add     esp,    -12
    push    edi
    mov     [cycle_count],    esi    ;CCREG
    call    get_addr
    mov     esi,    [cycle_count]
    add     esp,    16
    jmp     eax

verify_code_ds:
    mov     [branch_target],    ebp

verify_code_vm:
    ;eax = source (virtual address)
    ;ebx = target
    ;ecx = length
    cmp     eax,    0C0000000h
    jl      verify_code
    mov     edx,    eax
    lea     ebp,    [-1+eax+ecx*1]
    shr     edx,    12
    shr     ebp,    12
    mov     edi,    [memory_map+edx*4]
    test    edi,    edi
    js      _D5
    lea     eax,    [eax+edi*4]
_D1:
    xor     edi,    [memory_map+edx*4]
    shl     edi,    2
    jne     _D5
    mov     edi,    [memory_map+edx*4]
    inc     edx
    cmp     edx,    ebp
    jbe     _D1

verify_code:
    ;eax = source
    ;ebx = target
    ;ecx = length
    mov     edi,    [-4+eax+ecx*1]
    xor     edi,    [-4+ebx+ecx*1]
    jne     _D5
    mov     edx,    ecx
    add     ecx,    -4
    je      _D3
    test    edx,    4
    cmove   ecx,    edx
    mov     [cycle_count],    esi
_D2:
    mov     edx,    [-4+eax+ecx*1]
    mov     ebp,    [-4+ebx+ecx*1]
    mov     esi,    [-8+eax+ecx*1]
    xor     ebp,    edx
    mov     edi,    [-8+ebx+ecx*1]
    jne     _D4
    xor     edi,    esi
    jne     _D4
    add     ecx,    -8
    jne     _D2
    mov     esi,    [cycle_count]
    mov     ebp,    [branch_target]
_D3:
    ret
_D4:
    mov     esi,    [cycle_count]
_D5:
    mov     ebp,    [branch_target]
    push    esi           ;for stack alignment, unused
    push    DWORD [8+esp]
    call    get_addr
    add     esp,    16    ;pop stack
    jmp     eax

cc_interrupt:
    add     esi,    [last_count]
    add     esp,    -28                 ;Align stack
    mov     [g_cp0_regs+36],    esi    ;Count
    shr     esi,    19
    mov     DWORD [pending_exception],    0
    and     esi,    07fh
    cmp     DWORD [restore_candidate+esi*4],    0
    jne     _E4
_E1:
    call    gen_interupt
    mov     esi,    [g_cp0_regs+36]
    mov     eax,    [next_interupt]
    mov     ebx,    [pending_exception]
    mov     ecx,    [stop]
    add     esp,    28
    mov     [last_count],    eax
    sub     esi,    eax
    test    ecx,    ecx
    jne     _E3
    test    ebx,    ebx
    jne     _E2
    ret
_E2:
    add     esp,    -8
    mov     edi,    [pcaddr]
    mov     [cycle_count],    esi    ;CCREG
    push    edi
    call    get_addr_ht
    mov     esi,    [cycle_count]
    add     esp,    16
    jmp     eax
_E3:
    add     esp,    16     ;pop stack
    pop     edi            ;restore edi
    pop     esi            ;restore esi
    pop     ebx            ;restore ebx
    pop     ebp            ;restore ebp
    ret                    ;exit dynarec
_E4:
    ;Move 'dirty' blocks to the 'clean' list
    mov     ebx,    [restore_candidate+esi*4]
    mov     ebp,    esi
    mov     DWORD [restore_candidate+esi*4],    0
    shl     ebp,    5
_E5:
    shr     ebx,    1
    jnc     _E6
    mov     [esp],    ebp
    call    clean_blocks
_E6:
    inc     ebp
    test    ebp,    31
    jne     _E5
    jmp     _E1

do_interrupt:
    mov     edi,    [pcaddr]
    add     esp,    -12
    push    edi
    call    get_addr_ht
    add     esp,    16
    mov     esi,    [g_cp0_regs+36]
    mov     ebx,    [next_interupt]
    mov     [last_count],    ebx
    sub     esi,    ebx
    add     esi,    2
    jmp     eax

fp_exception:
    mov     edx,    01000002ch
_E7:
    mov     ebx,    [g_cp0_regs+48]
    add     esp,    -12
    or      ebx,    2
    mov     [g_cp0_regs+48],    ebx     ;Status
    mov     [g_cp0_regs+52],    edx     ;Cause
    mov     [g_cp0_regs+56],    eax     ;EPC
    push    080000180h
    call    get_addr_ht
    add     esp,    16
    jmp     eax

fp_exception_ds:
    mov     edx,    09000002ch    ;Set high bit if delay slot
    jmp     _E7

jump_syscall:
    mov     edx,    020h
    mov     ebx,    [g_cp0_regs+48]
    add     esp,    -12
    or      ebx,    2
    mov     [g_cp0_regs+48],    ebx     ;Status
    mov     [g_cp0_regs+52],    edx     ;Cause
    mov     [g_cp0_regs+56],    eax     ;EPC
    push    080000180h
    call    get_addr_ht
    add     esp,    16
    jmp     eax

jump_eret:
    mov     ebx,    [g_cp0_regs+48]        ;Status
    add     esi,    [last_count]
    and     ebx,    0FFFFFFFDh
    mov     [g_cp0_regs+36],    esi        ;Count
    mov     [g_cp0_regs+48],    ebx        ;Status
    call    check_interupt
    mov     eax,    [next_interupt]
    mov     esi,    [g_cp0_regs+36]
    mov     [last_count],    eax
    sub     esi,    eax
    mov     eax,    [g_cp0_regs+56]        ;EPC
    jns     _E11
_E8:
    mov     ebx,    248
    xor     edi,    edi
_E9:
    mov     ecx,    [reg+ebx]
    mov     edx,    [reg+4+ebx]
    sar     ecx,    31
    xor     edx,    ecx
    neg     edx
    adc     edi,    edi
    sub     ebx,    8
    jne     _E9
    mov     ecx,    [hi+ebx]
    mov     edx,    [hi+4+ebx]
    sar     ecx,    31
    xor     edx,    ecx
    jne     _E10
    mov     ecx,    [lo+ebx]
    mov     edx,    [lo+4+ebx]
    sar     ecx,    31
    xor     edx,    ecx
_E10:
    neg     edx
    adc     edi,    edi
    add     esp,    -8
    push    edi
    push    eax
    mov     [cycle_count],    esi
    call    get_addr_32
    mov     esi,    [cycle_count]
    add     esp,    16
    jmp     eax
_E11:
    mov     [pcaddr],    eax
    call    cc_interrupt
    mov     eax,    [pcaddr]
    jmp     _E8

new_dyna_start:
    push    ebp
    push    ebx
    push    esi
    push    edi
    add     esp,    -8    ;align stack
    push    0a4000040h
    call    new_recompile_block
    mov     edi,    DWORD [next_interupt]
    mov     esi,    DWORD [g_cp0_regs+36]
    mov     DWORD [last_count],    edi
    sub     esi,    edi
    jmp     DWORD [base_addr]

invalidate_block_eax:
    push    eax
    push    ecx
    push    edx
    push    eax
    jmp     invalidate_block_call

invalidate_block_ecx:
    push    eax
    push    ecx
    push    edx
    push    ecx
    jmp     invalidate_block_call

invalidate_block_edx:
    push    eax
    push    ecx
    push    edx
    push    edx
    jmp     invalidate_block_call

invalidate_block_ebx:
    push    eax
    push    ecx
    push    edx
    push    ebx
    jmp     invalidate_block_call

invalidate_block_ebp:
    push    eax
    push    ecx
    push    edx
    push    ebp
    jmp     invalidate_block_call

invalidate_block_esi:
    push    eax
    push    ecx
    push    edx
    push    esi
    jmp     invalidate_block_call

invalidate_block_edi:
    push    eax
    push    ecx
    push    edx
    push    edi

invalidate_block_call:
    call    invalidate_block
    pop     eax ;Throw away
    pop     edx
    pop     ecx
    pop     eax
    ret

write_rdram_new:
    mov     edi,    [address]
    mov     ecx,    [cpu_word]
    mov     [g_rdram-0x80000000+edi],    ecx
    jmp     _E12

write_rdramb_new:
    mov     edi,    [address]
    xor     edi,    3
    mov     cl,     BYTE [cpu_byte]
    mov     BYTE [g_rdram-0x80000000+edi],    cl
    jmp     _E12

write_rdramh_new:
    mov     edi,    [address]
    xor     edi,    2
    mov     cx,     WORD [cpu_hword]
    mov     WORD [g_rdram-0x80000000+edi],    cx
    jmp     _E12

write_rdramd_new:
    mov     edi,    [address]
    mov     ecx,    [cpu_dword+4]
    mov     edx,    [cpu_dword+0]
    mov     [g_rdram-0x80000000+edi],      ecx
    mov     [g_rdram-0x80000000+4+edi],    edx
    jmp     _E12


do_invalidate:
    mov     edi,    [address]
    mov     ebx,    edi    ;Return ebx to caller
_E12:
    shr     edi,    12
    cmp     BYTE [invalid_code+edi],    1
    je      _E13
    push    edi
    call    invalidate_block
    pop     edi
_E13:
ret

read_nomem_new:
    mov     edi,    [address]
    mov     ebx,    edi
    shr     edi,    12
    mov     edi,    [memory_map+edi*4]
    mov     eax,    08h
    test    edi,    edi
    js      tlb_exception
    mov     ecx,    [ebx+edi*4]
    mov     [readmem_dword],    ecx
    ret

read_nomemb_new:
    mov     edi,    [address]
    mov     ebx,    edi
    shr     edi,    12
    mov     edi,    [memory_map+edi*4]
    mov     eax,    08h
    test    edi,    edi
    js      tlb_exception
    xor     ebx,    3
    movzx   ecx,    BYTE [ebx+edi*4]
    mov     [readmem_dword],    ecx
    ret

read_nomemh_new:
    mov     edi,    [address]
    mov     ebx,    edi
    shr     edi,    12
    mov     edi,    [memory_map+edi*4]
    mov     eax,    08h
    test    edi,    edi
    js      tlb_exception
    xor     ebx,    2
    movzx   ecx,    WORD [ebx+edi*4]
    mov     [readmem_dword],    ecx
    ret

read_nomemd_new:
    mov     edi,    [address]
    mov     ebx,    edi
    shr     edi,    12
    mov     edi,    [memory_map+edi*4]
    mov     eax,    08h
    test    edi,    edi
    js      tlb_exception
    mov     ecx,    [4+ebx+edi*4]
    mov     edx,    [ebx+edi*4]
    mov     [readmem_dword],      ecx
    mov     [readmem_dword+4],    edx
    ret

write_nomem_new:
    call    do_invalidate
    mov     edi,    [memory_map+edi*4]
    mov     ecx,    [cpu_word]
    mov     eax,    0ch
    shl     edi,    2
    jc      tlb_exception
    mov     [ebx+edi],    ecx
    ret

write_nomemb_new:
    call    do_invalidate
    mov     edi,    [memory_map+edi*4]
    mov     cl,     BYTE [cpu_byte]
    mov     eax,    0ch
    shl     edi,    2
    jc      tlb_exception
    xor     ebx,    3
    mov     BYTE [ebx+edi],    cl
    ret

write_nomemh_new:
    call    do_invalidate
    mov     edi,    [memory_map+edi*4]
    mov     cx,     WORD [cpu_hword]
    mov     eax,    0ch
    shl     edi,    2
    jc      tlb_exception
    xor     ebx,    2
    mov     WORD [ebx+edi],    cx
    ret

write_nomemd_new:
    call    do_invalidate
    mov     edi,    [memory_map+edi*4]
    mov     edx,    [cpu_dword+4]
    mov     ecx,    [cpu_dword+0]
    mov     eax,    0ch
    shl     edi,    2
    jc      tlb_exception
    mov     [ebx+edi],    edx
    mov     [4+ebx+edi],    ecx
    ret


tlb_exception:
    ;eax = cause
    ;ebx = address
    ;ebp = instr addr + flags
    mov     ebp,    [024h+esp]
;Debug: 
    ;push    ebp
    ;push    ebx
    ;push    eax
    ;call    tlb_debug
    ;pop     eax
    ;pop     ebx
    ;pop     ebp
;end debug
    mov     esi,    [g_cp0_regs+48]
    mov     ecx,    ebp
    mov     edx,    ebp
    mov     edi,    ebp
    shl     ebp,    31
    shr     ecx,    12
    or      eax,    ebp
    sar     ebp,    29
    and     edx,    0FFFFFFFCh
    mov     ecx,    [memory_map+ecx*4]
    or      esi,    2
    mov     ecx,    [edx+ecx*4]
    add     edx,    ebp
    mov     [g_cp0_regs+48],    esi    ;Status
    mov     [g_cp0_regs+52],    eax    ;Cause
    mov     [g_cp0_regs+56],    edx    ;EPC
    add     esp,    024h
    mov     edx,    06000022h
    mov     ebp,    ecx
    movsx   eax,    cx
    shr     ecx,    26
    shr     ebp,    21
    sub     ebx,    eax
    and     ebp,    01fh
    ror     edx,    cl
    mov     esi,    [g_cp0_regs+16]
    cmovc   ebx,    [reg+ebp*8]
    and     esi,    0FF80000Fh
    mov     [reg+ebp*8],    ebx
    add     eax,    ebx
    sar     ebx,    31
    mov     [g_cp0_regs+32],    eax    ;BadVAddr
    shr     eax,    9
    test    edi,    2
    cmove   ebx,    [reg+4+ebp*8]
    add     esp,    -12
    and     eax,    0007FFFF0h
    mov     [reg+4+ebp*8],    ebx
    push    080000180h
    or      esi,    eax
    mov     [g_cp0_regs+16],    esi    ;Context
    call    get_addr_ht
    add     esp,    16
    mov     edi,    DWORD [next_interupt]
    mov     esi,    DWORD [g_cp0_regs+36]    ;Count
    mov     DWORD [last_count],    edi
    sub     esi,    edi
    jmp     eax

breakpoint: