/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Mupen64plus - interpreter_tlb.def                                     *
 *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
 *   Copyright (C) 2002 Hacktarux                                          *
 *                                                                         *
 *   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.          *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <zlib.h> // For adler32()

DECLARE_INSTRUCTION(TLBR)
{
   int index;
   index = g_cp0_regs[CP0_INDEX_REG] & UINT32_C(0x1F);
   g_cp0_regs[CP0_PAGEMASK_REG] = tlb_e[index].mask << 13;
   g_cp0_regs[CP0_ENTRYHI_REG] = ((tlb_e[index].vpn2 << 13) | tlb_e[index].asid);
   g_cp0_regs[CP0_ENTRYLO0_REG] = (tlb_e[index].pfn_even << 6) | (tlb_e[index].c_even << 3)
     | (tlb_e[index].d_even << 2) | (tlb_e[index].v_even << 1)
       | tlb_e[index].g;
   g_cp0_regs[CP0_ENTRYLO1_REG] = (tlb_e[index].pfn_odd << 6) | (tlb_e[index].c_odd << 3)
     | (tlb_e[index].d_odd << 2) | (tlb_e[index].v_odd << 1)
       | tlb_e[index].g;
   ADD_TO_PC(1);
}

static void TLBWrite(unsigned int idx)
{
   if (r4300emu != CORE_PURE_INTERPRETER)
   {
      unsigned int i;
      if (tlb_e[idx].v_even)
      {
         for (i=tlb_e[idx].start_even>>12; i<=tlb_e[idx].end_even>>12; i++)
         {
            if(!invalid_code[i] &&(invalid_code[tlb_LUT_r[i]>>12] ||
               invalid_code[(tlb_LUT_r[i]>>12)+0x20000]))
               invalid_code[i] = 1;
            if (!invalid_code[i])
            {
                /*int j;
                md5_state_t state;
                md5_byte_t digest[16];
                md5_init(&state);
                md5_append(&state, 
                       (const md5_byte_t*)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],
                       0x1000);
                md5_finish(&state, digest);
                for (j=0; j<16; j++) blocks[i]->md5[j] = digest[j];*/
                
                blocks[i]->adler32 = adler32(0, (const unsigned char *)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4], 0x1000);
                
                invalid_code[i] = 1;
            }
            else if (blocks[i])
            {
               /*int j;
                for (j=0; j<16; j++) blocks[i]->md5[j] = 0;*/
               blocks[i]->adler32 = 0;
            }
         }
      }
      if (tlb_e[idx].v_odd)
      {
         for (i=tlb_e[idx].start_odd>>12; i<=tlb_e[idx].end_odd>>12; i++)
         {
            if(!invalid_code[i] &&(invalid_code[tlb_LUT_r[i]>>12] ||
               invalid_code[(tlb_LUT_r[i]>>12)+0x20000]))
               invalid_code[i] = 1;
            if (!invalid_code[i])
            {
               /*int j;
               md5_state_t state;
               md5_byte_t digest[16];
               md5_init(&state);
               md5_append(&state, 
                      (const md5_byte_t*)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],
                      0x1000);
               md5_finish(&state, digest);
               for (j=0; j<16; j++) blocks[i]->md5[j] = digest[j];*/
                
               blocks[i]->adler32 = adler32(0, (const unsigned char *)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4], 0x1000);
                
               invalid_code[i] = 1;
            }
            else if (blocks[i])
            {
               /*int j;
               for (j=0; j<16; j++) blocks[i]->md5[j] = 0;*/
               blocks[i]->adler32 = 0;
            }
         }
      }
   }

   tlb_unmap(&tlb_e[idx]);

   tlb_e[idx].g = (g_cp0_regs[CP0_ENTRYLO0_REG] & g_cp0_regs[CP0_ENTRYLO1_REG] & 1);
   tlb_e[idx].pfn_even = (g_cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x3FFFFFC0)) >> 6;
   tlb_e[idx].pfn_odd = (g_cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x3FFFFFC0)) >> 6;
   tlb_e[idx].c_even = (g_cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x38)) >> 3;
   tlb_e[idx].c_odd = (g_cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x38)) >> 3;
   tlb_e[idx].d_even = (g_cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x4)) >> 2;
   tlb_e[idx].d_odd = (g_cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x4)) >> 2;
   tlb_e[idx].v_even = (g_cp0_regs[CP0_ENTRYLO0_REG] & UINT32_C(0x2)) >> 1;
   tlb_e[idx].v_odd = (g_cp0_regs[CP0_ENTRYLO1_REG] & UINT32_C(0x2)) >> 1;
   tlb_e[idx].asid = (g_cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFF));
   tlb_e[idx].vpn2 = (g_cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFFFFE000)) >> 13;
   //tlb_e[idx].r = (g_cp0_regs[CP0_ENTRYHI_REG] & 0xC000000000000000LL) >> 62;
   tlb_e[idx].mask = (g_cp0_regs[CP0_PAGEMASK_REG] & UINT32_C(0x1FFE000)) >> 13;
   
   tlb_e[idx].start_even = tlb_e[idx].vpn2 << 13;
   tlb_e[idx].end_even = tlb_e[idx].start_even+
     (tlb_e[idx].mask << 12) + UINT32_C(0xFFF);
   tlb_e[idx].phys_even = tlb_e[idx].pfn_even << 12;
   

   tlb_e[idx].start_odd = tlb_e[idx].end_even+1;
   tlb_e[idx].end_odd = tlb_e[idx].start_odd+
     (tlb_e[idx].mask << 12) + UINT32_C(0xFFF);
   tlb_e[idx].phys_odd = tlb_e[idx].pfn_odd << 12;
   
   tlb_map(&tlb_e[idx]);

   if (r4300emu != CORE_PURE_INTERPRETER)
   {
      unsigned int i;
      if (tlb_e[idx].v_even)
      {    
         for (i=tlb_e[idx].start_even>>12; i<=tlb_e[idx].end_even>>12; i++)
         {
            /*if (blocks[i] && (blocks[i]->md5[0] || blocks[i]->md5[1] ||
                  blocks[i]->md5[2] || blocks[i]->md5[3]))
            {
               int j;
               int equal = 1;
               md5_state_t state;
               md5_byte_t digest[16];
               md5_init(&state);
               md5_append(&state, 
                  (const md5_byte_t*)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],
                  0x1000);
               md5_finish(&state, digest);
               for (j=0; j<16; j++)
                 if (digest[j] != blocks[i]->md5[j])
                   equal = 0;
               if (equal) invalid_code[i] = 0;
               }*/
               if(blocks[i] && blocks[i]->adler32)
               {
                  if(blocks[i]->adler32 == adler32(0,(const unsigned char *)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],0x1000))
                     invalid_code[i] = 0;
               }
         }
      }

      if (tlb_e[idx].v_odd)
      {    
         for (i=tlb_e[idx].start_odd>>12; i<=tlb_e[idx].end_odd>>12; i++)
         {
            /*if (blocks[i] && (blocks[i]->md5[0] || blocks[i]->md5[1] ||
                  blocks[i]->md5[2] || blocks[i]->md5[3]))
              {
            int j;
            int equal = 1;
            md5_state_t state;
            md5_byte_t digest[16];
            md5_init(&state);
            md5_append(&state, 
                   (const md5_byte_t*)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],
                   0x1000);
            md5_finish(&state, digest);
            for (j=0; j<16; j++)
              if (digest[j] != blocks[i]->md5[j])
                equal = 0;
            if (equal) invalid_code[i] = 0;
            }*/
            if(blocks[i] && blocks[i]->adler32)
            {
               if(blocks[i]->adler32 == adler32(0,(const unsigned char *)&g_rdram[(tlb_LUT_r[i]&0x7FF000)/4],0x1000))
                  invalid_code[i] = 0;
            }
         }
      }
   }
}

DECLARE_INSTRUCTION(TLBWI)
{
   TLBWrite(g_cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F));
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TLBWR)
{
   update_count();
   g_cp0_regs[CP0_RANDOM_REG] = (g_cp0_regs[CP0_COUNT_REG]/2 % (32 - g_cp0_regs[CP0_WIRED_REG]))
                              + g_cp0_regs[CP0_WIRED_REG];
   TLBWrite(g_cp0_regs[CP0_RANDOM_REG]);
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(TLBP)
{
   int i;
   g_cp0_regs[CP0_INDEX_REG] |= UINT32_C(0x80000000);
   for (i=0; i<32; i++)
   {
      if (((tlb_e[i].vpn2 & (~tlb_e[i].mask)) ==
         (((g_cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFFFFE000)) >> 13) & (~tlb_e[i].mask))) &&
        ((tlb_e[i].g) ||
         (tlb_e[i].asid == (g_cp0_regs[CP0_ENTRYHI_REG] & UINT32_C(0xFF)))))
      {
         g_cp0_regs[CP0_INDEX_REG] = i;
         break;
      }
   }
   ADD_TO_PC(1);
}

DECLARE_INSTRUCTION(ERET)
{
   update_count();
   if (g_cp0_regs[CP0_STATUS_REG] & UINT32_C(0x4))
   {
     DebugMessage(M64MSG_ERROR, "error in ERET");
     stop=1;
   }
   else
   {
     g_cp0_regs[CP0_STATUS_REG] &= ~UINT32_C(0x2);
     generic_jump_to(g_cp0_regs[CP0_EPC_REG]);
   }
   llbit = 0;
   check_interupt();
   last_addr = PCADDR;
   if (next_interupt <= g_cp0_regs[CP0_COUNT_REG]) gen_interupt();
}
