; Code to populate the PowerPC "HTAB"/"Hash Table"

########################################################################

PutPTE ; EA r27 // PTE r30/r31, EQ=Success, GT=Invalid, LT=Fault
; 1. Find which Segment and PMDT cover this Effective Address
    lwz     r29, KDP.CurMap.SegMapPtr(r1)
    rlwinm  r28, r27, 7, 0x0000000F << 3    ; get offset into SegMap based on EA
    lwzx    r29, r29, r28                   ; r29 is now our iterating PMDT ptr
    rlwinm  r28, r27, 20, 0x0000FFFF        ; r27 = page index within Segment
    lhz     r30, 0(r29)                     ; get first PMDT.PageIdx
    b       @find_pmdt
@next_pmdt
    lhzu    r30, 8(r29)                     ; get next PMDT.PageIdx
@find_pmdt
    lhz     r31, PMDT.PageCount(r29)
    subf    r30, r30, r28
    cmplw   cr7, r30, r31
    bgt     cr7, @next_pmdt                 ; Save "found PMDT pointer" in r29, "page index within PMDT" in r30

; 2. Parse the PMDT into a PTE (three major code paths)
    lwz     r28, KDP.HtabSinglePTE(r1)
    lwz     r31, PMDT.Word2(r29)
    cmpwi   cr7, r28, 0                     ; always delete the previous PMDT_PTE_Single entry from the HTAB
    extlwi. r26, r31, 2, 20                 ; use the Cond Reg to branch on Pattr_NotPTE/Pattr_PTE_Single
    bne     cr7, @del_single_pte
    blt     @paged                          ; PMDT_Paged is the probable meaning of Pattr_NotPTE (will return to @parsed_pmdt)
@did_del_single_pte                         ; (optimized return: if LT then @del_single_pte falls thru to @paged)
    bgt     @single_pte                     ; PMDT_PTE_Single is the probable meaning of Pattr_PTE_Single (will return to @parsed_pmdt)
    slwi    r28, r30, 12                    ; PMDT_PTE_Range is likely otherwise, requiring us to add an offset to the PMDT
    add     r31, r31, r28
@parsed_pmdt                                ; Save draft PTE in r31 (points to actual page, has PTE-style flags), and r26 = 
                                            ;   0 (if PMDT_PTE_Range)
                                            ;   0x5A5A (if PMDT_PTE_Single)
                                            ;   PageListEntry ptr (if PMDT_Paged)

; 3. Find free slot in HTAB for new entry
    mfsrin  r30, r27
    rlwinm  r28, r27, 26, 10, 25            ; r28 = (1st arg of XOR) * 64b
    rlwinm  r30, r30, 6, 7, 25              ; r30 = (2nd arg of XOR) * 64b
    xor     r28, r28, r30                   ; r28 = (hash output) * 64b = r28 ^ r30
    lwz     r30, KDP.PTEGMask(r1)
    lwz     r29, KDP.HTABORG(r1)
    and     r28, r28, r30
    or.     r29, r29, r28                   ; r29 = PTEG pointer (clear CR0.EQ here so that @pteg_full knows we are trying primary hash)

@try_secondary_pteg                         ; @pteg_full jumps here to try sec'dary hash
    lwz     r30, 0(r29)                     ; Find "invalid" (bit 0 clear) PTE (optimized!)
    lwz     r28, 8(r29)
    cmpwi   cr6, r30, 0
    lwz     r30, 16(r29)
    cmpwi   cr7, r28, 0
    lwzu    r28, 24(r29)
    bge     cr6, @found_free_pte
    cmpwi   cr6, r30, 0
    lwzu    r30, 8(r29)
    bge     cr7, @found_free_pte
    cmpwi   cr7, r28, 0
    lwzu    r28, 8(r29)
    bge     cr6, @found_free_pte
    cmpwi   cr6, r30, 0
    lwzu    r30, 8(r29)
    bge     cr7, @found_free_pte
    cmpwi   cr7, r28, 0
    lwzu    r28, 8(r29)
    bge     cr6, @found_free_pte
    cmpwi   cr6, r30, 0
    addi    r29, r29, 8
    bge     cr7, @found_free_pte
    cmpwi   cr7, r28, 0
    addi    r29, r29, 8
    bge     cr6, @found_free_pte
    rlwinm  r28, r31, 0, 26, 26             ; wImg bit in PTE???
    addi    r29, r29, 8
    blt     cr7, @pteg_full                 ; @pteg_full *may* return to @try_secondary_pteg
@found_free_pte                             ; Save PTE ptr + 24 in r29

; 4. Save the new PTE and return
    cmpwi   r26, 0                          
    mfsrin  r28, r27
    extrwi  r30, r27, 6, 4                  ; PTE[26-31=API] = high 6 bits of offset-within-segment
    stw     r27, KDP.HtabLastEA(r1)
    ori     r31, r31, 0x100                 ; set PTE[R(eference)]
    rlwimi  r30, r31, 27, 25, 25            ; set PTE[H(ash func ID)] to cheeky topmost bit of the phys addr in r31
    rlwinm  r31, r31, 0, 21, 19             ; unset upper reserved bit in PTE[lo]
    insrwi  r30, r28, 24, 1                 ; get PTE[VSID] from segment register
    stw     r31, -20(r29)                   ; PTE[lo] = r31
    oris    r30, r30, 0x8000                ; set PTE[V(alid)]
    sync                                    ; because we just wanged the page table
    stwu    r30, -24(r29)                   ; PTE[hi] = r30

    lwz     r28, KDP.NKInfo.HashTableCreateCount(r1)
    stw     r29, KDP.HtabLastPTE(r1)
    addi    r28, r28, 1
    stw     r28, KDP.NKInfo.HashTableCreateCount(r1)

    beqlr                                   ; PMDT_PTE_Range: return success (EQ)

    cmpwi   r26, 0x5A5A
    bne     @ret_notsingle                  ; PMDT_PTE_Single:
    stw     r29, KDP.HtabSinglePTE(r1)      ; save ptr to this ephemeral PTE
    cmpw    r29, r29                        ; return success (EQ)
    blr
@ret_notsingle

    lwz     r28, 0(r26)                     ; PMDT_PTE_PageList:
    lwz     r30, KDP.HTABORG(r1)
    ori     r28, r28, 0x801                 ; Set PLE RealPgNum field to point to the PTE it now owns
    subf    r30, r30, r29                   ; and set flags 20/31
    cmpw    r29, r29
    rlwimi  r28, r30, 9, 0xFFFFF000
    stw     r28, 0(r26)
    blr                                     ; return success (EQ)

@del_single_pte ; Delete the PTE most recently created from a PMDT_PTE_Single entry.
    lwz     r28, KDP.NKInfo.HashTableDeleteCount(r1)
    lwz     r29, KDP.HtabSinglePTE(r1)
    addi    r28, r28, 1
    stw     r28, KDP.NKInfo.HashTableDeleteCount(r1)
    li      r28, 0
    stw     r28, 0(r29)
    lwz     r29, KDP.HtabSingleEA(r1)
    stw     r28, KDP.HtabSingleEA(r1)
    stw     r28, KDP.HtabSinglePTE(r1)
    sync
    tlbie   r29
    sync
    bge     @did_del_single_pte            ; Optimization: would otherwise branch to a "blt @par"

@paged ; Probably PMDT_Paged
; r30 = page index within area, r31 = RPN
    extlwi. r28, r31, 2, 21                 ; Put remaining two flags into top bits and set Cond Reg
    bge     @not_actually_pagelist          ; Not PMDT_Paged! (e.g. PMDT_InvalidAddress/PMDT_Available)

    rlwinm  r28, r30, 2, 0xFFFFFFFC         ; page index in segment * 4
    rlwinm  r26, r31, 22, 0xFFFFFFFC        ; ptr to first PLE belonging to this segment
    lwzux   r28, r26, r28                   ; r26 = PLE ptr, r28 = PLE

    lwz     r31, KDP.PageAttributeInit(r1)
    andi.   r30, r28, M68pdInHTAB | M68pdSupProtect | M68pdResident
    rlwimi  r31, r28, 0, 0xFFFFF000
    cmplwi  r30, M68pdResident
    cmplwi  cr7, r30, M68pdSupProtect | M68pdResident

    ori     r31, r31, LpteReference
    _mvbit  r31, bLpteChange, r28, bM68pdModified
    _mvbit  r31, bLpteInhibcache, r28, bM68pdCacheinhib
    _mvbit  r31, bLpteWritethru, r28, bM68pdCacheNotIO
    xori    r31, r31, LpteWritethru
    _mvbit  r31, bLpteP1, r28, bM68pdWriteProtect

    beq     @parsed_pmdt                    ; if resident but outside HTAB, put in HTAB
    bltlr   cr7                             ; if no flags, return invalid (GT)
    bl      CrashPageTable                  ; crash hard in any other case

@single_pte ; PMDT_PTE_Single
    ori     r28, r27, 0xfff                 ; r27 = EA, r31 = PMDT (low word, RPN)
    stw     r28, KDP.HtabSingleEA(r1)
    rlwinm  r31, r31, 0, ~(Pattr_NotPTE | Pattr_PTE_Single)  ; clear the flag that got us here, leaving none
    li      r26, 0x5A5A                     ; so that KDP.HtabSinglePTE gets set and we return correctly
    b       @parsed_pmdt                    ; RTS with r26 = 0x5A5A and r31 having flags cleared

@not_actually_pagelist ; Pattr_NotPTE set, but not PMDT_Paged
    bgtlr                                   ; PMDT_InvalidAddress/PMDT_Available: return invalid (GT)
    addi    r29, r1, KDP.SupervisorMap
    b       SetMap                          ; 800 (unknown) -> SetMap returns success (EQ)

@pteg_full ; Try the secondary hashing function, if we haven't already
    cmplw   cr6, r28, r26                   ; r26 is as set by PMDT interpretation, r28 = bit 26 of draft PTE r31
    subi    r29, r29, 64 + 16               ; Make r29 the actual PTEG ptr (PTE search code is very tight)
    ble     cr6, @both_ptegs_full           ; Not sure why r26/r28 could force the sec hash to be skipped...

    crnot   cr0_eq, cr0_eq                  ; Flip everything (CR0.EQ means "secondary")
    lwz     r30, KDP.PTEGMask(r1)
    xori    r31, r31, 0x800                 ; Flip PTE bit 20 to signify sec hash func (or back to primary if failing)
    xor     r29, r29, r30                   ; Flip r29 into the sec PTEG ptr (or back to primary if failing)
    beq     @try_secondary_pteg             ; Go back in with CR0.EQ set this time (or fall through...)
                                            ; On fallthru, r29 = prim PTEG ptr

@both_ptegs_full ; So choose a slot in this PTEG to overflow
    lwz     r26, KDP.HtabLastOverflow(r1)   ; this could be zero
    crclr   cr6_eq                          ; cr6.eq means "hell, we're desperate"
    rlwimi  r26, r29, 0, 0xFFFFFFC0         ; r26 points to the PTE we should try to protext from overflow
    addi    r29, r26, 8                     ; r29 points to the following PTE (gets clobbered straight away)
    b       @first_pte
@redo_search                                
    bne     cr6, @nomr
    mr      r26, r29
@nomr
@next_pte
    cmpw    cr6, r29, r26
    addi    r29, r29, 8
@first_pte
    rlwimi  r29, r26, 0, 0xFFFFFFC0
    lwz     r31, 4(r29)
    lwz     r30, 0(r29)
    beq     cr6, @got_pte
    rlwinm  r28, r31, 30, 25, 25
    andc.   r28, r28, r30                   ; Protect if R && !H (i.e. page has been ref'd and primary-hash)
    bne     @next_pte                       ; But otherwise, or if we're desperate, then kick this PTE out!
@got_pte

; Left side: protect PP2=0, KDP and CB from overflow
    clrlwi  r28, r31, 30
    cmpwi   cr7, r28, 0
    clrrwi  r28, r31, 12
    cmpw    r28, r1
    lwz     r30, KDP.ContextPtr(r1)
    beq     cr7, @redo_search   ; if PP1=0
    addi    r31, r30, CB.Size-1
    beq     @redo_search        ; KDP

    rlwinm  r30, r30, 0, 0xFFFFF000
    cmpwi   cr7, r28, 30
    lwz     r30, 0(r29)
    rlwinm  r31, r31, 0, 0xFFFFF000
    cmpwi   r28, 31
                                                rlwinm  r31, r30, 0, 0x00000040 ; To do with below?
    beq     cr7, @redo_search
    extlwi  r28, r30, 4, 1
    beq     @redo_search

; Okay... now do the dirty job of actually overflowing a PTEG (the one at r29)
; (this may well mean tweaking a PLE or even a PMDT)
    neg     r31, r31                        ; Inscrutable... extracting PMDT offset?
    insrwi  r28, r30, 6, 4
    xor     r31, r31, r29
    rlwimi  r28, r30, 5, 10, 19
    rlwinm  r31, r31, 6, 10, 19
    xor     r28, r28, r31
    lwz     r26, KDP.CurMap.SegMapPtr(r1)
    rlwinm  r30, r28, 7, 0x00000078

    lwzx    r26, r26, r30                   ; Hell, get a segment pointer

@oflow_next_pmdt                            ; find the last non-blank PMDT in the segment
    lhz     r30, PMDT.PageIdx(r26)
    rlwinm  r31, r28, 20, 0x0000FFFF
    subf    r30, r30, r31
    lhz     r31, PMDT.PageCount(r26)
    addi    r26, r26, 8
    cmplw   cr7, r30, r31
    lwz     r31, PMDT.Word2 - 8(r26)
    andi.   r31, r31, EveryPattr
    cmpwi   r31, PMDT_Available
    bgt     cr7, @oflow_next_pmdt           ; addr not in this PMDT -> try other PMDT
    beq     @oflow_next_pmdt                ; not PMDT_Available -> try other PMDT

    lwz     r26, PMDT.Word2 - PMDT.Size(r26)  ; If PMDT_Paged then we must wang the PLE pre-return
    slwi    r30, r30, 2                     ; (r30 = PLE offset relative to first PLE in segment)
    extrwi  r31, r26, 2, 20
    cmpwi   cr7, r31, PMDT_Paged >> 10   ; (save that little tidbit in cr7)

    lwz     r31, KDP.NKInfo.HashTableOverflowCount(r1)
    stw     r29, KDP.HtabLastOverflow(r1)
    addi    r31, r31, 1
    stw     r31, KDP.NKInfo.HashTableOverflowCount(r1)
    lwz     r31, KDP.NKInfo.HashTableDeleteCount(r1)
    stw     r30, 0(r29)                     ; Very clever: save an "invalid" value in PTE then redo search
    addi    r31, r31, 1
    stw     r31, KDP.NKInfo.HashTableDeleteCount(r1)

    sync                                    ; Clear the page from TLB because it could get moved
    tlbie   r28
    sync

    _clrNCBCache scr=r28                    ; Also clobber NCB cache if page could get moved

    bne     cr7, PutPTE                     ; Is this page in a paged area, with a 68k Page Descriptor?
    rlwinm  r26, r26, 22, 0xFFFFFFFC        ; r26 = RPN * 4
    lwzux   r28, r26, r30                   ; (the 68k-PD to edit)
    lwz     r31, 4(r29)                     ; (extract some info from the lower PTE before discarding)
    andi.   r30, r28, M68pdInHTAB           ; Crash if this PLE wasn't marked as HTAB'd!
    rlwinm  r30, r28, 32-9, 0x007FFFF8
    xor     r30, r30, r29                   ; Crash if this 68k-PD's pointer didn't match this PTE!
    beq     CrashPageTable
    andi.   r30, r30, 0xffff
    xori    r28, r28, M68pdInHTAB           ; Edit the 68k-PD's HTAB flag, physical ptr, and "usage" flags
    bne     CrashPageTable
    rlwimi  r28, r31, 0, 0xFFFFF000
    _mvbit  r28, bM68pdModified, r31, bLpteChange
    _mvbit  r28, bM68pdUsed, r31, bLpteReference
    stw     r28, 0(r26)                     ; Save edited 68k-PD

    b       PutPTE                          ; PTEG overflow complete. Redo PutPTE!

########################################################################

SetMap ; MemMap r29
    lwz     r28, MemMap.SegMapPtr(r29)
    stw     r28, KDP.CurMap.SegMapPtr(r1)
    addi    r28, r28, 16*8 + 4
    lis     r31, 0

@next_seg                                       ; SEGMENT REGISTERS
    lwzu    r30, -8(r28)
    subis   r31, r31, 0x1000
    mr.     r31, r31
    mtsrin  r30, r31
    bne     @next_seg

    mfpvr   r31
    lwz     r28, MemMap.BatMap(r29)
    andis.  r31, r31, 0xFFFE
    addi    r29, r1, 0
    stw     r28, KDP.CurMap.BatMap(r1)
    beq     @601

    rlwimi  r29, r28, 7, 0x00000078     ; BATS, non-601
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   ibat0u, r30
    mtspr   ibat0l, r31
    stw     r30, KDP.CurIBAT0.U(r1)
    stw     r31, KDP.CurIBAT0.L(r1)

    rlwimi  r29, r28, 11, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   ibat1u, r30
    mtspr   ibat1l, r31
    stw     r30, KDP.CurIBAT1.U(r1)
    stw     r31, KDP.CurIBAT1.L(r1)

    rlwimi  r29, r28, 15, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   ibat2u, r30
    mtspr   ibat2l, r31
    stw     r30, KDP.CurIBAT2.U(r1)
    stw     r31, KDP.CurIBAT2.L(r1)

    rlwimi  r29, r28, 19, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   ibat3u, r30
    mtspr   ibat3l, r31
    stw     r30, KDP.CurIBAT3.U(r1)
    stw     r31, KDP.CurIBAT3.L(r1)

    rlwimi  r29, r28, 23, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   dbat0u, r30
    mtspr   dbat0l, r31
    stw     r30, KDP.CurDBAT0.U(r1)
    stw     r31, KDP.CurDBAT0.L(r1)

    rlwimi  r29, r28, 27, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   dbat1u, r30
    mtspr   dbat1l, r31
    stw     r30, KDP.CurDBAT1.U(r1)
    stw     r31, KDP.CurDBAT1.L(r1)

    rlwimi  r29, r28, 31, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   dbat2u, r30
    mtspr   dbat2l, r31
    stw     r30, KDP.CurDBAT2.U(r1)
    stw     r31, KDP.CurDBAT2.L(r1)

    rlwimi  r29, r28, 3, 0x00000078
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    mtspr   dbat3u, r30
    mtspr   dbat3l, r31
    stw     r30, KDP.CurDBAT3.U(r1)
    stw     r31, KDP.CurDBAT3.L(r1)

    cmpw    r29, r29
    blr

@601
    rlwimi  r29, r28, 7, 25, 28
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    stw     r30, 0x0300(r1)
    stw     r31, 0x0304(r1)
    stw     r30, 0x0320(r1)
    stw     r31, 0x0324(r1)
    rlwimi  r30, r31, 0, 25, 31
    mtspr   ibat0u, r30
    lwz     r30, KDP.BATs + 0(r29)
    rlwimi  r31, r30, 30, 26, 31
    rlwimi  r31, r30, 6, 25, 25
    mtspr   ibat0l, r31
    rlwimi  r29, r28, 11, 25, 28
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    stw     r30, 0x0308(r1)
    stw     r31, 0x030c(r1)
    stw     r30, 0x0328(r1)
    stw     r31, 0x032c(r1)
    rlwimi  r30, r31, 0, 25, 31
    mtspr   ibat1u, r30
    lwz     r30, KDP.BATs + 0(r29)
    rlwimi  r31, r30, 30, 26, 31
    rlwimi  r31, r30, 6, 25, 25
    mtspr   ibat1l, r31
    rlwimi  r29, r28, 15, 25, 28
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    stw     r30, 0x0310(r1)
    stw     r31, 0x0314(r1)
    stw     r30, 0x0330(r1)
    stw     r31, 0x0334(r1)
    rlwimi  r30, r31, 0, 25, 31
    mtspr   ibat2u, r30
    lwz     r30, KDP.BATs + 0(r29)
    rlwimi  r31, r30, 30, 26, 31
    rlwimi  r31, r30, 6, 25, 25
    mtspr   ibat2l, r31
    rlwimi  r29, r28, 19, 25, 28
    lwz     r30, KDP.BATs + 0(r29)
    lwz     r31, KDP.BATs + 4(r29)
    stw     r30, 0x0318(r1)
    stw     r31, 0x031c(r1)
    stw     r30, 0x0338(r1)
    stw     r31, 0x033c(r1)
    rlwimi  r30, r31, 0, 25, 31
    mtspr   ibat3u, r30
    lwz     r30, KDP.BATs + 0(r29)
    rlwimi  r31, r30, 30, 26, 31
    rlwimi  r31, r30, 6, 25, 25
    mtspr   ibat3l, r31
    cmpw    r29, r29
    blr

########################################################################

GetPhysical ; EA r27, batPtr r29 // PA r31, EQ=Fail
    lwz     r30, 0(r29)
    li      r28, -1
    rlwimi  r28, r30, 15, 0, 14
    xor     r31, r27, r30
    andc.   r31, r31, r28
    beq     @_54
    lwzu    r30, 8(r29)
    rlwimi  r28, r30, 15, 0, 14
    xor     r31, r27, r30
    andc.   r31, r31, r28
    beq     @_54
    lwzu    r30, 8(r29)
    rlwimi  r28, r30, 15, 0, 14
    xor     r31, r27, r30
    andc.   r31, r31, r28
    beq     @_54
    lwzu    r30, 8(r29)
    rlwimi  r28, r30, 15, 0, 14
    xor     r31, r27, r30
    andc.   r31, r31, r28
    bne     GetPhysicalFromHTAB

@_54
    andi.   r31, r30, 1
    rlwinm  r28, r28, 0, 8, 19
    lwzu    r31, 4(r29)
    and     r28, r27, r28
    or      r31, r31, r28
    bnelr

GetPhysicalFromHTAB ; EA r27 // PA r31, EQ=Fail
    mfsrin  r31, r27
    rlwinm  r30, r27, 10, 26, 31
    rlwimi  r30, r31, 7, 1, 24
    rlwinm  r28, r27, 26, 10, 25
    oris    r30, r30, 0x8000
    rlwinm  r31, r31, 6, 7, 25
    xor     r28, r28, r31
    lwz     r31, KDP.PTEGMask(r1)
    lwz     r29, KDP.HTABORG(r1)
    and     r28, r28, r31
    or.     r29, r29, r28

@_2c
    lwz     r31, 0(r29)
    lwz     r28, 8(r29)
    cmpw    cr6, r30, r31
    lwz     r31, 16(r29)
    cmpw    cr7, r30, r28
    lwzu    r28, 24(r29)
    bne     cr6, @_50

@_48
    lwzu    r31, -0x0014(r29)
    blr

@_50
    cmpw    cr6, r30, r31
    lwzu    r31, 8(r29)
    beq     cr7, @_48
    cmpw    cr7, r30, r28
    lwzu    r28, 8(r29)
    beq     cr6, @_48
    cmpw    cr6, r30, r31
    lwzu    r31, 8(r29)
    beq     cr7, @_48
    cmpw    cr7, r30, r28
    lwzu    r28, 8(r29)
    beq     cr6, @_48
    cmpw    cr6, r30, r31
    lwzu    r31, -0x000c(r29)
    beqlr   cr7
    cmpw    cr7, r30, r28
    lwzu    r31, 8(r29)
    beqlr   cr6
    lwzu    r31, 8(r29)
    beqlr   cr7
    lwz     r31, KDP.PTEGMask(r1)
    xori    r30, r30, 0x40
    andi.   r28, r30, 0x40
    addi    r29, r29, -0x3c
    xor     r29, r29, r31
    bne     @_2c
    blr

########################################################################

FlushTLB
    lhz     r29, KDP.ProcInfo.TransCacheTotalSize(r1)
    slwi    r29, r29, 11
@loop
    subi    r29, r29, 4096
    cmpwi   r29, 0
    tlbie   r29
    bgt     @loop
    sync
    blr
