;----------------------------------------------------------------------------- ; Translate_Address : virtual address in eax; output (physical addr) in eax ;----------------------------------------------------------------------------- align GRAIN, db 0x90 _Virt_To_Phys: bt eax, 31 ; CF := 31st (top) bit of vAddr jc _Above_7FFFFFFF ; If 31st bit = 1, kseg 0/1/2; else: ;; 0x00000000 <= vAddr <= 0x7FFFFFFF (kuseg) : bt CP0_Status, CP0St_ERL ; CF := CP0St_ERL Flag jnc _TLB_Lookup ; If ERL = 0: TLB Lookup required; else: jmp _No_Tlb_Lookup ; pAddr is equal to vAddr, return. _Above_7FFFFFFF: bt eax, 30 ; CF := 30th (2nd from top) bt of vAddr jc _Above_BFFFFFFF ; If 30th bit = 1 : kseg2; else: ;; 0x80000000 <= vAddr <= 0x9FFFFFFF (kseg0) : ;; 0xA0000000 <= vAddr <= 0xBFFFFFFF (kseg1) : and eax, 0x1FFFFFFF ; kseg0 and kseg1: clear top 3 bits, jmp _No_Tlb_Lookup ; i.e. pAddr := bottom 29 bts of vAddr. _Above_BFFFFFFF: ;; 0xC0000000 <= vAddr <= 0xFFFFFFFF (kseg2) : bt CP0_Status, CP0St_UM ; CF := CP0St_UM Flag jnc _TLB_Lookup ; If UM = 0: K. Mode, so do TLB; else: test CP0_Status, (1 << CP0St_EXL) | (1 << CP0St_ERL) ; EXL or ERL jnz _TLB_Lookup ; If EXL && ERL, K. Mode, do TLB ;; Else: vAddr is in kseg2, but we are NOT in Kernel Mode: Flg_Get IsWriting ; Is Writing? jc _V2P_Eggog_Wr ; If so, we want to set AdES; _V2P_Eggog_Rd: ; ... otherwise, set AdEL. SetEXC EXC_AdEL ; Fetch address error. jmp _V2P_Eggog_Fin ; Proceed to abort. _V2P_Eggog_Wr: SetEXC EXC_AdES ; Store address error. _V2P_Eggog_Fin: ;; Will go into exception handler instead of back to _Virt_xxx etc add rsp, 16 ; squelch return to _Virt_xxx and its caller push _Handle_Exception ; 'return' directly to exc handler. _No_Tlb_Lookup: Flg_Off IsWriting ; Consume 'is writing' flag. ret ; Done. _TLB_Lookup: ; TLB Lookup Required: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Save ebx, ecx, edx, AUX, to xmm ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; movd xmm0, ebx movd xmm1, ecx movd xmm2, edx movd xmm3, AUX ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Flg_Off ExcWasTLBNoMatch ; Clear the ExcWasTLBNoMatch Flag ;; Get the active ASID: mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi and edx, 0xFF ; edx := edx & 0xFF (get current ASID) ;; For each slot in table (0 .. 15), attempt lookup xor AUX, AUX ; Start with the 0-th entry in table _Lookup_TLB_E: mov ecx, eax ; ecx := eax (vAddr) and ecx, 0xFFFFF000 ; ecx := ecx & 0xFFFFF000 shr ecx, 13 ; ecx := ecx >> 13 (get vAddr's Tag) mov ebx, TLB_E(AUX) ; ebx := current TLB entry and ebx, TLB_VPN2_Mask ; get VPN2 of this entry cmp ebx, ecx ; cmp(entry.VPN2, vAddr.tag) jne _Lookup_TLB_E_Not_Here ; if entry.VPN2 != vAddr.tag: no match mov ebx, TLB_E(AUX) ; ebx := current TLB entry bt ebx, TLB_G ; is entry.G = 1? jc _Lookup_TLB_E_Match ; then match. shr ebx, TLB_ASID_Shift ; ebx := ebx >> TLB_ASID_Shift and ebx, TLB_ASID_Mask ; ebx := entry.ASID cmp ebx, edx ; entry.ASID = current ASID ? jne _Lookup_TLB_E_Not_Here ; if neither G=1 nor ASID match. mov ebx, TLB_E(AUX) ; ebx := current TLB entry _Lookup_TLB_E_Match: ; TLB Match: bt eax, 12 ; Test odd/even junior bit jc _Lookup_TLB_E_Match_Odd ; If odd: test V1, D1 _Lookup_TLB_E_Match_Even: ; If even: test V0, D0 lea ecx, TLB_PFN_E(AUX) ; prepare to load even PFN entry bt ebx, TLB_V0 ; Is entry.V0=1 ? jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID bt ebx, TLB_D0 ; Is entry.D0=1 ? jc _Lookup_TLB_E_Match_Yes ; If entry.D0=1, then Match Yes jmp _Lookup_TLB_E_Match_Wr ; else, go to 'is writing?' _Lookup_TLB_E_Match_Odd: ; Odd bit: lea ecx, TLB_PFN_O(AUX) ; prepare to load odd PFN entry bt ebx, TLB_V1 ; Is entry.V1=1 ? jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID bt ebx, TLB_D1 ; Is entry.D1=1 ? jc _Lookup_TLB_E_Match_Yes ; If entry.D1=1, then Match Yes _Lookup_TLB_E_Match_Wr: Flg_Get IsWriting ; Is Writing? jnc _Lookup_TLB_E_Match_Yes ; If not writing, go to Match Yes _Lookup_TLB_E_Dirty: ; ... else, Dirty: SetEXC EXC_Mod ; Set the EXC_Mod Exception jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish. _Lookup_TLB_E_Match_Yes: ; This is the 'success' case mov ebx, dword [ecx] ; Actually load the current PFN entry and eax, 0xFFF ; vAddr := vAddr & 0xFFF or eax, ebx ; vAddr := vAddr | entry.PFN[lowbit] jmp _Lookup_TLB_Done ; vAddr is now correct pAddr, done. _Lookup_TLB_E_Not_Here: ; try next one in the table, if any inc AUX ; index := index + 1 cmp AUX, TLB_ENTRIES_COUNT ; see if still in range 0 .. n-1 jb _Lookup_TLB_E ; if in range, go to next entry ;; ... else: Flg_On ExcWasTLBNoMatch ; Set the ExcWasTLBNoMatch Flag ;; ... now drop down into 'invalid' : _Lookup_TLB_E_Invalid: Flg_Get IsWriting ; Was Writing? jc _Lookup_TLB_E_Invalid_W ; If so, we want to set EXC_TLBS _Lookup_TLB_E_Invalid_R: ; Otherwise, set EXC_TLBL exception SetEXC EXC_TLBL ; Set the EXC_TLBL Exception jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish. _Lookup_TLB_E_Invalid_W: SetEXC EXC_TLBS ; Set the EXC_TLBS Exception ;; then drop down to 'write extra' : _Lookup_TLB_E_WriteExtr: ; Write the 'extra data' and finish mov Sr(CP0_BadVAddr), eax ; CP0_BadVAddr := vAddr mov ecx, eax ; ecx := vAddr mov ebx, Sr(CP0_Context) ; ebx := CP0_Context and ebx, ~0x007FFFFF ; ebx := ebx & ~0x007FFFFF shr ecx, 9 ; ecx := ecx >> 9 and ecx, 0x007FFFF0 ; ecx := ecx & 0x007FFFF0 or ebx, ecx ; ebx := ebx | ecx mov Sr(CP0_Context), ebx ; CP0_Context := ebx mov ecx, eax ; ecx := vAddr mov ebx, Sr(CP0_EntryHi) ; ebx := CP0_EntryHi and ebx, 0xFF ; ebx := ebx & 0xFF and ecx, 0xFFFFE000 ; ecx := ecx & 0xFFFFE000 or ebx, ecx ; ebx := ebx | ecx mov Sr(CP0_EntryHi), ebx ; CP0_EntryHi := ebx ;; Will go into exception handler instead of back to _Virt_xxx etc add rsp, 16 ; squelch return to _Virt_xxx and its caller push _Handle_Exception ; 'return' directly to exc handler. ;; and drop into 'done' : _Lookup_TLB_Done: Flg_Off IsWriting ; Consume 'is writing' flag. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Restore ebx, ecx, edx, AUX, from xmm ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; movd ebx, xmm0 movd ecx, xmm1 movd edx, xmm2 movd AUX, xmm3 ret ;-----------------------------------------------------------------------------