--- trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:19:23 20 +++ trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:20:58 32 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2005 Anders Gavare. All rights reserved. + * Copyright (C) 2003-2006 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_mips_coproc.c,v 1.8 2005/11/23 02:17:41 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.60 2006/10/14 23:47:37 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -35,7 +35,6 @@ #include #include -#include "bintrans.h" #include "cop0.h" #include "cpu.h" #include "cpu_mips.h" @@ -46,6 +45,7 @@ #include "mips_cpu_types.h" #include "misc.h" #include "opcodes_mips.h" +#include "timer.h" #ifndef ENABLE_MIPS @@ -69,14 +69,6 @@ static char *regnames[] = MIPS_REGISTER_NAMES; -/* FPU control registers: */ -#define FPU_FCIR 0 -#define FPU_FCCR 25 -#define FPU_FCSR 31 -#define FCSR_FCC0_SHIFT 23 -#define FCSR_FCC1_SHIFT 25 - - /* * initialize_cop0_config(): * @@ -84,41 +76,62 @@ */ static void initialize_cop0_config(struct cpu *cpu, struct mips_coproc *c) { -#ifdef ENABLE_MIPS16 - const int m16 = 1; -#else - const int m16 = 0; -#endif - int cpu_type, IB, DB, SB, IC, DC, SC, IA, DA; + const int m16 = 0; /* TODO: MIPS16 support */ + int IB, DB, SB, IC, DC, SC, IA, DA; + + /* Generic case for MIPS32/64: */ + if (cpu->cd.mips.cpu_type.isa_level == 32 || + cpu->cd.mips.cpu_type.isa_level == 64) { + /* According to the MIPS64 (5K) User's Manual: */ + c->reg[COP0_CONFIG] = + ( (uint32_t)1 << 31)/* Config 1 present bit */ + | ( 0 << 20) /* ISD: instruction scheduling + disable (=1) */ + | ( 0 << 17) /* DID: dual issue disable */ + | ( 0 << 16) /* BM: burst mode */ + | ((cpu->byte_order == EMUL_BIG_ENDIAN? 1 : 0) << 15) + /* endian mode */ + | ((cpu->cd.mips.cpu_type.isa_level == 64? 2 : 0) << 13) + /* 0=MIPS32, 1=64S, 2=64 */ + | ( 0 << 10) /* Architecture revision */ + | ( 1 << 7) /* MMU type: 1=TLB, 3=FMT */ + | ( 2 << 0) /* kseg0 cache coherency algorithm */ + ; + /* Config select 1: caches etc. TODO: Don't use + cpu->machine for this stuff! */ + IB = cpu->machine->cache_picache_linesize - 1; + IB = IB < 0? 0 : (IB > 7? 7 : IB); + DB = cpu->machine->cache_pdcache_linesize - 1; + DB = DB < 0? 0 : (DB > 7? 7 : DB); + IC = cpu->machine->cache_picache - + cpu->machine->cache_picache_linesize - 7; + DC = cpu->machine->cache_pdcache - + cpu->machine->cache_pdcache_linesize - 7; + IA = cpu->cd.mips.cpu_type.piways - 1; + DA = cpu->cd.mips.cpu_type.pdways - 1; + cpu->cd.mips.cop0_config_select1 = + ((cpu->cd.mips.cpu_type.nr_of_tlb_entries - 1) << 25) + | (IC << 22) /* IS: I-cache sets per way */ + | (IB << 19) /* IL: I-cache line-size */ + | (IA << 16) /* IA: I-cache assoc. (ways-1) */ + | (DC << 13) /* DS: D-cache sets per way */ + | (DB << 10) /* DL: D-cache line-size */ + | (DA << 7) /* DA: D-cache assoc. (ways-1) */ + | (16 * 0) /* Existance of PerformanceCounters */ + | ( 8 * 0) /* Existance of Watch Registers */ + | ( 4 * m16) /* Existance of MIPS16 */ + | ( 2 * 0) /* Existance of EJTAG */ + | ( 1 * 1) /* Existance of FPU */ + ; - /* Default values: */ - c->reg[COP0_CONFIG] = - ( 0 << 31) /* config1 present */ - | (0x00 << 16) /* implementation dependent */ - | ((cpu->byte_order==EMUL_BIG_ENDIAN? 1 : 0) << 15) - /* endian mode */ - | ( 2 << 13) /* 0 = MIPS32, - 1 = MIPS64 with 32-bit segments, - 2 = MIPS64 with all segments, - 3 = reserved */ - | ( 0 << 10) /* architecture revision level, - 0 = "Revision 1", other - values are reserved */ - | ( 1 << 7) /* MMU type: 0 = none, - 1 = Standard TLB, - 2 = Standard BAT, - 3 = fixed mapping, 4-7=reserved */ - | ( 0 << 0) /* kseg0 coherency algorithm - (TODO) */ - ; - - cpu_type = cpu->cd.mips.cpu_type.rev & 0xff; - - /* AU1x00 are treated as 4Kc (MIPS32 cores): */ - if ((cpu->cd.mips.cpu_type.rev & 0xffff) == 0x0301) - cpu_type = MIPS_4Kc; + return; + } - switch (cpu_type) { + switch (cpu->cd.mips.cpu_type.rev) { + case MIPS_R2000: + case MIPS_R3000: + /* No config register. */ + break; case MIPS_R4000: /* according to the R4000 manual */ case MIPS_R4600: IB = cpu->machine->cache_picache_linesize - 4; @@ -303,52 +316,9 @@ (TODO) */ ; break; - case MIPS_4Kc: - case MIPS_5Kc: - /* According to the MIPS64 (5K) User's Manual: */ - c->reg[COP0_CONFIG] = - ( (uint32_t)1 << 31)/* Config 1 present bit */ - | ( 0 << 20) /* ISD: instruction scheduling - disable (=1) */ - | ( 0 << 17) /* DID: dual issue disable */ - | ( 0 << 16) /* BM: burst mode */ - | ((cpu->byte_order == EMUL_BIG_ENDIAN? 1 : 0) << 15) - /* endian mode */ - | ((cpu_type == MIPS_5Kc? 2 : 0) << 13) - /* 0=MIPS32, 1=64S, 2=64 */ - | ( 0 << 10) /* Architecture revision */ - | ( 1 << 7) /* MMU type: 1=TLB, 3=FMT */ - | ( 2 << 0) /* kseg0 cache coherency algorithm */ - ; - /* Config select 1: caches etc. TODO: Don't use - cpu->machine for this stuff! */ - IB = cpu->machine->cache_picache_linesize - 1; - IB = IB < 0? 0 : (IB > 7? 7 : IB); - DB = cpu->machine->cache_pdcache_linesize - 1; - DB = DB < 0? 0 : (DB > 7? 7 : DB); - IC = cpu->machine->cache_picache - - cpu->machine->cache_picache_linesize - 7; - DC = cpu->machine->cache_pdcache - - cpu->machine->cache_pdcache_linesize - 7; - IA = cpu->cd.mips.cpu_type.piways - 1; - DA = cpu->cd.mips.cpu_type.pdways - 1; - cpu->cd.mips.cop0_config_select1 = - ((cpu->cd.mips.cpu_type.nr_of_tlb_entries - 1) << 25) - | (IC << 22) /* IS: I-cache sets per way */ - | (IB << 19) /* IL: I-cache line-size */ - | (IA << 16) /* IA: I-cache assoc. (ways-1) */ - | (DC << 13) /* DS: D-cache sets per way */ - | (DB << 10) /* DL: D-cache line-size */ - | (DA << 7) /* DA: D-cache assoc. (ways-1) */ - | (16 * 0) /* Existance of PerformanceCounters */ - | ( 8 * 0) /* Existance of Watch Registers */ - | ( 4 * m16) /* Existance of MIPS16 */ - | ( 2 * 0) /* Existance of EJTAG */ - | ( 1 * 1) /* Existance of FPU */ - ; - break; - default: - ; + default:fatal("Internal error: No initialization code for" + " config0? cpu rev = 0x%x", cpu->cd.mips.cpu_type.rev); + exit(1); } } @@ -438,13 +408,17 @@ if (!cpu->machine->prom_emulation) c->reg[COP0_STATUS] |= STATUS_BEV; - /* Default pagesize = 4 KB. */ + /* Ugly hack for R5900/TX79/C790: */ + if (cpu->cd.mips.cpu_type.rev == MIPS_R5900) + c->reg[COP0_STATUS] |= R5900_STATUS_EIE; + + /* Default pagesize = 4 KB (i.e. dualpage = 8KB) */ c->reg[COP0_PAGEMASK] = 0x1fff; /* Note: .rev may contain the company ID as well! */ c->reg[COP0_PRID] = - (0x00 << 24) /* Company Options */ - | (0x00 << 16) /* Company ID */ + (0x00 << 24) /* Company Options */ + | (0x00 << 16) /* Company ID */ | (cpu->cd.mips.cpu_type.rev << 8) /* Processor ID */ | (cpu->cd.mips.cpu_type.sub) /* Revision */ ; @@ -454,7 +428,7 @@ initialize_cop0_config(cpu, c); /* Make sure the status register is sign-extended nicely: */ - c->reg[COP0_STATUS] = (int64_t)(int32_t)c->reg[COP0_STATUS]; + c->reg[COP0_STATUS] = (int32_t)c->reg[COP0_STATUS]; } if (coproc_nr == 1) @@ -465,6 +439,23 @@ /* + * mips_timer_tick(): + */ +static void mips_timer_tick(struct timer *timer, void *extra) +{ + struct cpu *cpu = (struct cpu *) extra; + + cpu->cd.mips.compare_interrupts_pending ++; + + if ((int32_t) (cpu->cd.mips.coproc[0]->reg[COP0_COUNT] - + cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]) < 0) { + cpu->cd.mips.coproc[0]->reg[COP0_COUNT] = + cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]; + } +} + + +/* * mips_coproc_tlb_set_entry(): * * Used by machine setup code, if a specific machine emulation starts up @@ -536,464 +527,71 @@ /* - * old_update_translation_table(): + * invalidate_asid(): + * + * Go through all entries in the TLB. If an entry has a matching asid, is + * valid, and is not global (i.e. the ASID matters), then its virtual address + * translation is invalidated. + * + * Note: In the R3000 case, the asid argument is shifted 6 bits. */ -static void old_update_translation_table(struct cpu *cpu, uint64_t vaddr_page, - unsigned char *host_page, int writeflag, uint64_t paddr_page) +static void invalidate_asid(struct cpu *cpu, int asid) { - int a, b, index; - struct vth32_table *tbl1; - void *p_r, *p_w; - uint32_t p_paddr; - - /* This table stuff only works for 32-bit mode: */ - if (vaddr_page & 0x80000000ULL) { - if ((vaddr_page >> 32) != 0xffffffffULL) - return; - } else { - if ((vaddr_page >> 32) != 0) - return; - } + struct mips_coproc *cp = cpu->cd.mips.coproc[0]; + int i, ntlbs = cp->nr_of_tlbs; + struct mips_tlb *tlb = cp->tlbs; - a = (vaddr_page >> 22) & 0x3ff; - b = (vaddr_page >> 12) & 0x3ff; - index = (vaddr_page >> 12) & 0xfffff; - - /* printf("vaddr = %08x, a = %03x, b = %03x\n", - (int)vaddr_page,a, b); */ - - tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; - /* printf("tbl1 = %p\n", tbl1); */ - if (tbl1 == cpu->cd.mips.vaddr_to_hostaddr_nulltable) { - /* Allocate a new table1: */ - /* printf("ALLOCATING a new table1, 0x%08x - " - "0x%08x\n", a << 22, (a << 22) + 0x3fffff); */ - if (cpu->cd.mips.next_free_vth_table == NULL) { - tbl1 = malloc(sizeof(struct vth32_table)); - if (tbl1 == NULL) { - fprintf(stderr, "out of mem\n"); - exit(1); - } - memset(tbl1, 0, sizeof(struct vth32_table)); - } else { - tbl1 = cpu->cd.mips.next_free_vth_table; - cpu->cd.mips.next_free_vth_table = tbl1->next_free; - tbl1->next_free = NULL; - } - cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a] = tbl1; - if (tbl1->refcount != 0) { - printf("INTERNAL ERROR in coproc.c\n"); - exit(1); - } - } - p_r = tbl1->haddr_entry[b*2]; - p_w = tbl1->haddr_entry[b*2+1]; - p_paddr = tbl1->paddr_entry[b]; - /* printf(" p_r=%p p_w=%p\n", p_r, p_w); */ - if (p_r == NULL && p_paddr == 0 && - (host_page != NULL || paddr_page != 0)) { - tbl1->refcount ++; - /* printf("ADDING %08x -> %p wf=%i (refcount is " - "now %i)\n", (int)vaddr_page, host_page, - writeflag, tbl1->refcount); */ - } - if (writeflag == -1) { - /* Forced downgrade to read-only: */ - tbl1->haddr_entry[b*2 + 1] = NULL; - if (cpu->cd.mips.host_store == - cpu->cd.mips.host_store_orig) - cpu->cd.mips.host_store[index] = NULL; - } else if (writeflag==0 && p_w != NULL && host_page != NULL) { - /* Don't degrade a page from writable to readonly. */ - } else { - if (host_page != NULL) { - tbl1->haddr_entry[b*2] = host_page; - if (cpu->cd.mips.host_load == - cpu->cd.mips.host_load_orig) - cpu->cd.mips.host_load[index] = host_page; - if (writeflag) { - tbl1->haddr_entry[b*2+1] = host_page; - if (cpu->cd.mips.host_store == - cpu->cd.mips.host_store_orig) - cpu->cd.mips.host_store[index] = - host_page; - } else { - tbl1->haddr_entry[b*2+1] = NULL; - if (cpu->cd.mips.host_store == - cpu->cd.mips.host_store_orig) - cpu->cd.mips.host_store[index] = NULL; - } - } else { - tbl1->haddr_entry[b*2] = NULL; - tbl1->haddr_entry[b*2+1] = NULL; - if (cpu->cd.mips.host_store == - cpu->cd.mips.host_store_orig) { - cpu->cd.mips.host_load[index] = NULL; - cpu->cd.mips.host_store[index] = NULL; + if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { + for (i=0; iinvalidate_translation_caches(cpu, + tlb[i].hi & R2K3K_ENTRYHI_VPN_MASK, + INVALIDATE_VADDR); } - } - tbl1->paddr_entry[b] = paddr_page; - } - tbl1->bintrans_chunks[b] = NULL; -} - - -/* - * mips_update_translation_table(): - */ -void mips_update_translation_table(struct cpu *cpu, uint64_t vaddr_page, - unsigned char *host_page, int writeflag, uint64_t paddr_page) -{ - if (!cpu->machine->bintrans_enable) - return; - - if (writeflag > 0) - bintrans_invalidate(cpu, paddr_page); - - if (cpu->machine->old_bintrans_enable) { - old_update_translation_table(cpu, vaddr_page, host_page, - writeflag, paddr_page); - return; - } - - /* TODO */ - /* printf("update_translation_table(): TODO\n"); */ -} - - -/* - * invalidate_table_entry(): - */ -static void invalidate_table_entry(struct cpu *cpu, uint64_t vaddr) -{ - int a, b, index; - struct vth32_table *tbl1; - void *p_r, *p_w; - uint32_t p_paddr; - - if (!cpu->machine->old_bintrans_enable) { - /* printf("invalidate_table_entry(): New: TODO\n"); */ - return; - } - - /* This table stuff only works for 32-bit mode: */ - if (vaddr & 0x80000000ULL) { - if ((vaddr >> 32) != 0xffffffffULL) { - fatal("invalidate_table_entry(): vaddr = 0x%016llx\n", - (long long)vaddr); - return; - } } else { - if ((vaddr >> 32) != 0) { - fatal("invalidate_table_entry(): vaddr = 0x%016llx\n", - (long long)vaddr); - return; - } - } + int non4kpages = 0; + uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; - a = (vaddr >> 22) & 0x3ff; - b = (vaddr >> 12) & 0x3ff; - index = (vaddr >> 12) & 0xfffff; - - /* printf("vaddr = %08x, a = %03x, b = %03x\n", (int)vaddr,a, b); */ - - tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; - /* printf("tbl1 = %p\n", tbl1); */ - p_r = tbl1->haddr_entry[b*2]; - p_w = tbl1->haddr_entry[b*2+1]; - p_paddr = tbl1->paddr_entry[b]; - tbl1->bintrans_chunks[b] = NULL; - /* printf("B: p_r=%p p_w=%p\n", p_r,p_w); */ - cpu->cd.mips.host_load_orig[index] = NULL; - cpu->cd.mips.host_store_orig[index] = NULL; - if (p_r != NULL || p_paddr != 0) { - /* printf("Found a mapping, " - "vaddr = %08x, a = %03x, b = %03x\n", (int)vaddr,a, b); */ - tbl1->haddr_entry[b*2] = NULL; - tbl1->haddr_entry[b*2+1] = NULL; - tbl1->paddr_entry[b] = 0; - tbl1->refcount --; - if (tbl1->refcount == 0) { - cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a] = - cpu->cd.mips.vaddr_to_hostaddr_nulltable; - /* "free" tbl1: */ - tbl1->next_free = cpu->cd.mips.next_free_vth_table; - cpu->cd.mips.next_free_vth_table = tbl1; + if (cpu->is_32bit) { + topbit = 0x80000000; + fillmask = 0xffffffff00000000ULL; + } else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + topbit <<= 43; + fillmask <<= 4; + } else { + topbit <<= 39; } - } -} - -/* - * clear_all_chunks_from_all_tables(): - */ -void clear_all_chunks_from_all_tables(struct cpu *cpu) -{ - int a, b; - struct vth32_table *tbl1; - - if (!cpu->machine->old_bintrans_enable) { - printf("clear_all_chunks_from_all_tables(): New: TODO\n"); - return; - } - - for (a=0; a<0x400; a++) { - tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; - if (tbl1 != cpu->cd.mips.vaddr_to_hostaddr_nulltable) { - for (b=0; b<0x400; b++) { - int index; - - tbl1->haddr_entry[b*2] = NULL; - tbl1->haddr_entry[b*2+1] = NULL; - tbl1->paddr_entry[b] = 0; - tbl1->bintrans_chunks[b] = NULL; - - if (cpu->cd.mips.host_store == - cpu->cd.mips.host_store_orig) { - index = (a << 10) + b; - cpu->cd.mips.host_load[index] = NULL; - cpu->cd.mips.host_store[index] = NULL; - } + for (i=0; imachine->bintrans_enable) { -#if 1 - int i; - uint64_t tlb_paddr0, tlb_paddr1; - uint64_t tlb_vaddr; - uint64_t p, p2; - - switch (cpu->cd.mips.cpu_type.mmu_model) { - case MMU3K: - for (i=0; i<64; i++) { - tlb_paddr0 = cpu->cd.mips.coproc[0]-> - tlbs[i].lo0 & R2K3K_ENTRYLO_PFN_MASK; - tlb_vaddr = cpu->cd.mips.coproc[0]-> - tlbs[i].hi & R2K3K_ENTRYHI_VPN_MASK; - tlb_vaddr = (int64_t)(int32_t)tlb_vaddr; - if ((cpu->cd.mips.coproc[0]->tlbs[i].lo0 & - R2K3K_ENTRYLO_V) && tlb_paddr0 == paddr) - invalidate_table_entry(cpu, tlb_vaddr); - } - break; - default: - for (i=0; icd.mips.coproc[0]->nr_of_tlbs; i++) { - int psize = 12; - int or_pmask = 0x1fff; - int phys_shift = 12; - int tmp_pmask = cpu->cd.mips.coproc[0]-> - tlbs[i].mask | or_pmask; - - if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { - or_pmask = 0x7ff; - phys_shift = 10; - } - switch (tmp_pmask) { - case 0x000007ff: psize = 10; break; - case 0x00001fff: psize = 12; break; - case 0x00007fff: psize = 14; break; - case 0x0001ffff: psize = 16; break; - case 0x0007ffff: psize = 18; break; - case 0x001fffff: psize = 20; break; - case 0x007fffff: psize = 22; break; - case 0x01ffffff: psize = 24; break; - case 0x07ffffff: psize = 26; break; - default: - printf("invalidate_translation_caches" - "_paddr(): bad pagemask: 0x%x\n", - (int)tmp_pmask); - } - tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i]. - lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT; - tlb_paddr1 = (cpu->cd.mips.coproc[0]->tlbs[i]. - lo1 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT; - tlb_paddr0 <<= phys_shift; - tlb_paddr1 <<= phys_shift; - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - tlb_vaddr = cpu->cd.mips.coproc[0]-> - tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; - if (tlb_vaddr & ((int64_t)1 << 43)) - tlb_vaddr |= - 0xfffff00000000000ULL; - } else { - tlb_vaddr = cpu->cd.mips.coproc[0]-> - tlbs[i].hi & ENTRYHI_VPN2_MASK; - if (tlb_vaddr & ((int64_t)1 << 39)) - tlb_vaddr |= - 0xffffff0000000000ULL; - } - if ((cpu->cd.mips.coproc[0]->tlbs[i].lo0 & - ENTRYLO_V) && paddr >= tlb_paddr0 && - paddr < tlb_paddr0 + (1<cd.mips.coproc[0]->tlbs[i].lo1 & - ENTRYLO_V) && paddr >= tlb_paddr1 && - paddr < tlb_paddr1 + (1<tlbs[i].hi & ~fillmask; + if (vaddr0 & topbit) + vaddr0 |= fillmask; + vaddr1 = vaddr0 | 0x1000; /* TODO: mask */ + + if (tlb[i].lo0 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, + vaddr0, INVALIDATE_VADDR); + if (tlb[i].lo1 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, + vaddr1, INVALIDATE_VADDR); } } -#endif - if (paddr < 0x20000000) { - invalidate_table_entry(cpu, 0xffffffff80000000ULL - + paddr); - invalidate_table_entry(cpu, 0xffffffffa0000000ULL - + paddr); + if (non4kpages) { + cpu->invalidate_translation_caches(cpu, + 0, INVALIDATE_ALL); } } - -#if 0 -{ - int i; - - /* TODO: Don't invalidate everything. */ - for (i=0; ibintrans_data_hostpage[i] = NULL; -} -#endif -} - - -/* - * invalidate_translation_caches(): - * - * This is necessary for every change to the TLB, and when the ASID is changed, - * so that for example user-space addresses are not cached when they should - * not be. - */ -static void invalidate_translation_caches(struct cpu *cpu, - int all, uint64_t vaddr, int kernelspace, int old_asid_to_invalidate) -{ - int i; - - /* printf("inval(all=%i, kernel=%i, addr=%016llx)\n", - all, kernelspace, (long long)vaddr); */ - - if (!cpu->machine->bintrans_enable) - goto nobintrans; - - if (all) { - int i; - uint64_t tlb_vaddr; - switch (cpu->cd.mips.cpu_type.mmu_model) { - case MMU3K: - for (i=0; i<64; i++) { - tlb_vaddr = cpu->cd.mips.coproc[0]->tlbs[i].hi - & R2K3K_ENTRYHI_VPN_MASK; - tlb_vaddr = (int64_t)(int32_t)tlb_vaddr; - if ((cpu->cd.mips.coproc[0]->tlbs[i].lo0 & - R2K3K_ENTRYLO_V) && (tlb_vaddr & - 0xc0000000ULL) != 0x80000000ULL) { - int asid = (cpu->cd.mips.coproc[0]-> - tlbs[i].hi & R2K3K_ENTRYHI_ASID_MASK - ) >> R2K3K_ENTRYHI_ASID_SHIFT; - if (old_asid_to_invalidate < 0 || - old_asid_to_invalidate == asid) - invalidate_table_entry(cpu, - tlb_vaddr); - } - } - break; - default: - for (i=0; icd.mips.coproc[0]->nr_of_tlbs; i++) { - int psize = 10, or_pmask = 0x1fff; - int phys_shift = 12; - - if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { - or_pmask = 0x7ff; - phys_shift = 10; - } - - switch (cpu->cd.mips.coproc[0]->tlbs[i].mask - | or_pmask) { - case 0x000007ff: psize = 10; break; - case 0x00001fff: psize = 12; break; - case 0x00007fff: psize = 14; break; - case 0x0001ffff: psize = 16; break; - case 0x0007ffff: psize = 18; break; - case 0x001fffff: psize = 20; break; - case 0x007fffff: psize = 22; break; - case 0x01ffffff: psize = 24; break; - case 0x07ffffff: psize = 26; break; - default: - printf("invalidate_translation_caches" - "(): bad pagemask?\n"); - } - - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - tlb_vaddr = cpu->cd.mips.coproc[0]-> - tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; - if (tlb_vaddr & ((int64_t)1 << 43)) - tlb_vaddr |= - 0xfffff00000000000ULL; - } else { - tlb_vaddr = cpu->cd.mips.coproc[0]-> - tlbs[i].hi & ENTRYHI_VPN2_MASK; - if (tlb_vaddr & ((int64_t)1 << 39)) - tlb_vaddr |= - 0xffffff0000000000ULL; - } - - /* TODO: Check the ASID etc. */ - - invalidate_table_entry(cpu, tlb_vaddr); - invalidate_table_entry(cpu, tlb_vaddr | - (1 << psize)); - } - } - } else - invalidate_table_entry(cpu, vaddr); - -nobintrans: - - /* TODO: Don't invalidate everything. */ - for (i=0; icd.mips.bintrans_data_hostpage[i] = NULL; - - if (kernelspace) - all = 1; - -#ifdef USE_TINY_CACHE -{ - vaddr >>= 12; - - /* Invalidate the tiny translation cache... */ - if (!cpu->machine->bintrans_enable) - for (i=0; icd.mips. - translation_cache_instr[i].vaddr_pfn)) - cpu->cd.mips.translation_cache_instr[i].wf = 0; - - if (!cpu->machine->bintrans_enable) - for (i=0; icd.mips. - translation_cache_data[i].vaddr_pfn)) - cpu->cd.mips.translation_cache_data[i].wf = 0; -} -#endif } @@ -1016,28 +614,8 @@ if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { -#if 0 - /* - * This speeds up delay-loops that just read the count - * register until it has reached a certain value. (Only for - * R4000 etc.) - * - * TODO: Maybe this should be optional? - */ - if (cpu->cd.mips.cpu_type.exc_model != EXC3K) { - int increase = 500; - int32_t x = cp->reg[COP0_COUNT]; - int32_t y = cp->reg[COP0_COMPARE]; - int32_t diff = x - y; - if (diff < 0 && diff + increase >= 0 - && cpu->cd.mips.compare_register_set) { - mips_cpu_interrupt(cpu, 7); - cpu->cd.mips.compare_register_set = 0; - } - cp->reg[COP0_COUNT] = (int64_t) - (int32_t)(cp->reg[COP0_COUNT] + increase); - } -#endif + /* TODO: Increase count in a more meaningful way! */ + cp->reg[COP0_COUNT] = (int32_t) (cp->reg[COP0_COUNT] + 1); unimpl = 0; } if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; @@ -1121,11 +699,10 @@ (tmp & 0xff)!=0) { /* char *symbol; uint64_t offset; - symbol = get_symbol_name( - cpu->cd.mips.pc_last, &offset); + symbol = get_symbol_name(cpu->pc, &offset); fatal("YO! pc = 0x%08llx <%s> " "lo=%016llx\n", (long long) - cpu->cd.mips.pc_last, symbol? symbol : + cpu->pc, symbol? symbol : "no symbol", (long long)tmp); */ tmp &= (R2K3K_ENTRYLO_PFN_MASK | R2K3K_ENTRYLO_N | R2K3K_ENTRYLO_D | @@ -1191,32 +768,67 @@ unimpl = 0; break; case COP0_COUNT: - if (tmp != (int64_t)(int32_t)tmp) + if (tmp != (uint64_t)(int64_t)(int32_t)tmp) fatal("WARNING: trying to write a 64-bit value" " to the COUNT register!\n"); tmp = (int64_t)(int32_t)tmp; unimpl = 0; break; case COP0_COMPARE: - /* Clear the timer interrupt bit (bit 7): */ - cpu->cd.mips.compare_register_set = 1; + if (cpu->machine->emulated_hz > 0) { + int32_t compare_diff = tmp - + cp->reg[COP0_COMPARE]; + double hz; + + if (compare_diff < 0) + hz = tmp - cp->reg[COP0_COUNT]; + + if (compare_diff == 0) + hz = 0; + else + hz = (double)cpu->machine->emulated_hz + / (double)compare_diff; +/* + * TODO: DON'T HARDCODE THIS! + */ +hz = 100.0; + + /* Initialize or re-set the periodic timer: */ + if (hz > 0) { + if (cpu->cd.mips.timer == NULL) + cpu->cd.mips.timer = timer_add( + hz, mips_timer_tick, cpu); + else + timer_update_frequency( + cpu->cd.mips.timer, hz); + } + } + + /* Ack the periodic timer, if it was asserted: */ + if (cp->reg[COP0_CAUSE] & 0x8000 && + cpu->cd.mips.compare_interrupts_pending > 0) + cpu->cd.mips.compare_interrupts_pending --; + + /* Clear the timer interrupt assertion (bit 7): */ mips_cpu_interrupt_ack(cpu, 7); - if (tmp != (int64_t)(int32_t)tmp) + + if (tmp != (uint64_t)(int64_t)(int32_t)tmp) fatal("WARNING: trying to write a 64-bit value" " to the COMPARE register!\n"); + tmp = (int64_t)(int32_t)tmp; + cpu->cd.mips.compare_register_set = 1; unimpl = 0; break; case COP0_ENTRYHI: /* - * Translation caches must be invalidated, because the - * address space might change (if the ASID changes). + * Translation caches must be invalidated if the + * ASID changes: */ switch (cpu->cd.mips.cpu_type.mmu_model) { case MMU3K: - old_asid = (cp->reg[COP0_ENTRYHI] & - R2K3K_ENTRYHI_ASID_MASK) >> - R2K3K_ENTRYHI_ASID_SHIFT; + old_asid = cp->reg[COP0_ENTRYHI] & + R2K3K_ENTRYHI_ASID_MASK; if ((cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) != (tmp & R2K3K_ENTRYHI_ASID_MASK)) @@ -1229,19 +841,20 @@ inval = 1; break; } + if (inval) - invalidate_translation_caches(cpu, 1, 0, 0, - old_asid); + invalidate_asid(cpu, old_asid); + unimpl = 0; if (cpu->cd.mips.cpu_type.mmu_model == MMU3K && (tmp & 0x3f)!=0) { /* char *symbol; uint64_t offset; - symbol = get_symbol_name(cpu-> - cd.mips.pc_last, &offset); + symbol = get_symbol_name(cpu->pc, + &offset); fatal("YO! pc = 0x%08llx <%s> " - "hi=%016llx\n", (long long)cpu-> - cd.mips.pc_last, symbol? symbol : + "hi=%016llx\n", (long long)cpu->pc, + symbol? symbol : "no symbol", (long long)tmp); */ tmp &= ~0x3f; } @@ -1287,57 +900,22 @@ case COP0_STATUS: oldmode = cp->reg[COP0_STATUS]; tmp &= ~(1 << 21); /* bit 21 is read-only */ -#if 0 -/* Why was this here? It should not be necessary. */ - - /* Changing from kernel to user mode? Then - invalidate some translation caches: */ - if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { - if (!(oldmode & MIPS1_SR_KU_CUR) - && (tmp & MIPS1_SR_KU_CUR)) - invalidate_translation_caches(cpu, - 0, 0, 1, 0); - } else { - /* TODO: don't hardcode */ - if ((oldmode & 0xff) != (tmp & 0xff)) - invalidate_translation_caches( - cpu, 0, 0, 1, 0); - } -#endif + /* + * When isolating caches, invalidate all translations. + * During the isolation, a special hack in memory_rw.c + * prevents translation tables from being updated, so + * the translation caches don't have to be invalidated + * when switching back to normal mode. + */ if (cpu->cd.mips.cpu_type.mmu_model == MMU3K && (oldmode & MIPS1_ISOL_CACHES) != (tmp & MIPS1_ISOL_CACHES)) { - /* R3000-style caches when isolated are - treated in bintrans mode by changing - the vaddr_to_hostaddr_table0 pointer: */ + /* Invalidate everything if we are switching + to isolated mode: */ if (tmp & MIPS1_ISOL_CACHES) { - /* 2-level table: */ - cpu->cd.mips.vaddr_to_hostaddr_table0 = - tmp & MIPS1_SWAP_CACHES? - cpu->cd.mips. - vaddr_to_hostaddr_table0_cacheisol_i - : cpu->cd.mips. - vaddr_to_hostaddr_table0_cacheisol_d; - - /* 1M-entry table: */ - cpu->cd.mips.host_load = - cpu->cd.mips.host_store = - cpu->cd.mips.huge_r2k3k_cache_table; - } else { - /* 2-level table: */ - cpu->cd.mips.vaddr_to_hostaddr_table0 = - cpu->cd.mips. - vaddr_to_hostaddr_table0_kernel; - - /* TODO: cpu->cd.mips. - vaddr_to_hostaddr_table0_user; */ - - /* 1M-entry table: */ - cpu->cd.mips.host_load = - cpu->cd.mips.host_load_orig; - cpu->cd.mips.host_store = - cpu->cd.mips.host_store_orig; + cpu->invalidate_translation_caches( + cpu, 0, INVALIDATE_ALL); } } unimpl = 0; @@ -1347,10 +925,6 @@ affects IM bits 0 and 1: */ cp->reg[reg_nr] &= ~(0x3 << STATUS_IM_SHIFT); cp->reg[reg_nr] |= (tmp & (0x3 << STATUS_IM_SHIFT)); - if (!(cp->reg[COP0_CAUSE] & STATUS_IM_MASK)) - cpu->cd.mips.cached_interrupt_is_possible = 0; - else - cpu->cd.mips.cached_interrupt_is_possible = 1; return; case COP0_FRAMEMASK: /* TODO: R10000 */ @@ -1433,12 +1007,15 @@ IEEE_FMT_W, IEEE_FMT_L, /* PS (Paired Single) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -/* MIPS floating point types: */ -#define FMT_S 16 -#define FMT_D 17 -#define FMT_W 20 -#define FMT_L 21 -#define FMT_PS 22 +static char *fmtname[32] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15", + "s", "d", "18", "19", "w", "l", "ps", "23", + "24", "25", "26", "27", "28", "29", "30", "31" }; + +static char *ccname[16] = { + "f", "un", "eq", "ueq", "olt", "ult", "ole", "ule", + "sf", "ngle", "seq", "ngl", "lt", "nge", "le", "ngt" }; #define FPU_OP_ADD 1 #define FPU_OP_SUB 2 @@ -1468,7 +1045,7 @@ * TODO: This is for 32-bit mode. It has to be updated later * for 64-bit coprocessor functionality! */ - if (fmt == FMT_D || fmt == FMT_L) { + if (fmt == COP1_FMT_D || fmt == COP1_FMT_L) { cp->reg[fd] = r & 0xffffffffULL; cp->reg[(fd+1) & 31] = (r >> 32) & 0xffffffffULL; @@ -1507,7 +1084,7 @@ fs_v = cp->reg[fs]; /* TODO: register-pair mode and plain register mode? "FR" bit? */ - if (fmt == FMT_D || fmt == FMT_L) + if (fmt == COP1_FMT_D || fmt == COP1_FMT_L) fs_v = (fs_v & 0xffffffffULL) + (cp->reg[(fs + 1) & 31] << 32); ieee_interpret_float_value(fs_v, &float_value[0], ieee_fmt); @@ -1516,7 +1093,7 @@ uint64_t v = cp->reg[ft]; /* TODO: register-pair mode and plain register mode? "FR" bit? */ - if (fmt == FMT_D || fmt == FMT_L) + if (fmt == COP1_FMT_D || fmt == COP1_FMT_L) v = (v & 0xffffffffULL) + (cp->reg[(ft + 1) & 31] << 32); ieee_interpret_float_value(v, &float_value[1], ieee_fmt); @@ -1594,7 +1171,7 @@ * TODO: this is for 32-bit mode. It has to be updated later * for 64-bit coprocessor stuff. */ - if (output_fmt == FMT_D || output_fmt == FMT_L) { + if (output_fmt == COP1_FMT_D || output_fmt == COP1_FMT_L) { cp->reg[fd] = fs_v & 0xffffffffULL; cp->reg[(fd+1) & 31] = (fs_v >> 32) & 0xffffffffULL; if (cp->reg[fd] & 0x80000000ULL) @@ -1685,7 +1262,7 @@ /* bc1f, bc1t, bc1fl, bc1tl: */ if ((function & 0x03e00000) == 0x01000000) { - int nd, tf, imm, cond_true; + int nd, tf, imm; char *instr_mnem; /* cc are bits 20..18: */ @@ -1708,41 +1285,17 @@ if (unassemble_only) return 1; - if (cpu->cd.mips.delay_slot) { - fatal("%s: jump inside a jump's delay slot, " - "or similar. TODO\n", instr_mnem); - cpu->running = 0; - return 1; - } - - /* Both the FCCR and FCSR contain condition code bits... */ - if (cc == 0) - cond_true = (cp->fcr[FPU_FCSR] >> FCSR_FCC0_SHIFT) & 1; - else - cond_true = (cp->fcr[FPU_FCSR] >> - (FCSR_FCC1_SHIFT + cc-1)) & 1; - - if (!tf) - cond_true = !cond_true; - - if (cond_true) { - cpu->cd.mips.delay_slot = TO_BE_DELAYED; - cpu->cd.mips.delay_jmpaddr = cpu->pc + (imm << 2); - } else { - /* "likely": */ - if (nd) { - /* nullify the delay slot */ - cpu->cd.mips.nullify_next = 1; - } - } - - return 1; + fatal("INTERNAL ERROR: MIPS coprocessor branches should not" + " be implemented in cpu_mips_coproc.c, but in" + " cpu_mips_instr.c!\n"); + exit(1); } /* add.fmt: Floating-point add */ if ((function & 0x0000003f) == 0x00000000) { if (cpu->machine->instruction_trace || unassemble_only) - debug("add.%i\tr%i,r%i,r%i\n", fmt, fd, fs, ft); + debug("add.%s\tr%i,r%i,r%i\n", + fmtname[fmt], fd, fs, ft); if (unassemble_only) return 1; @@ -1753,7 +1306,8 @@ /* sub.fmt: Floating-point subtract */ if ((function & 0x0000003f) == 0x00000001) { if (cpu->machine->instruction_trace || unassemble_only) - debug("sub.%i\tr%i,r%i,r%i\n", fmt, fd, fs, ft); + debug("sub.%s\tr%i,r%i,r%i\n", + fmtname[fmt], fd, fs, ft); if (unassemble_only) return 1; @@ -1764,7 +1318,8 @@ /* mul.fmt: Floating-point multiply */ if ((function & 0x0000003f) == 0x00000002) { if (cpu->machine->instruction_trace || unassemble_only) - debug("mul.%i\tr%i,r%i,r%i\n", fmt, fd, fs, ft); + debug("mul.%s\tr%i,r%i,r%i\n", + fmtname[fmt], fd, fs, ft); if (unassemble_only) return 1; @@ -1775,7 +1330,8 @@ /* div.fmt: Floating-point divide */ if ((function & 0x0000003f) == 0x00000003) { if (cpu->machine->instruction_trace || unassemble_only) - debug("div.%i\tr%i,r%i,r%i\n", fmt, fd, fs, ft); + debug("div.%s\tr%i,r%i,r%i\n", + fmtname[fmt], fd, fs, ft); if (unassemble_only) return 1; @@ -1786,7 +1342,7 @@ /* sqrt.fmt: Floating-point square-root */ if ((function & 0x001f003f) == 0x00000004) { if (cpu->machine->instruction_trace || unassemble_only) - debug("sqrt.%i\tr%i,r%i\n", fmt, fd, fs); + debug("sqrt.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; @@ -1797,7 +1353,7 @@ /* abs.fmt: Floating-point absolute value */ if ((function & 0x001f003f) == 0x00000005) { if (cpu->machine->instruction_trace || unassemble_only) - debug("abs.%i\tr%i,r%i\n", fmt, fd, fs); + debug("abs.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; @@ -1808,7 +1364,7 @@ /* mov.fmt: Floating-point (non-arithmetic) move */ if ((function & 0x0000003f) == 0x00000006) { if (cpu->machine->instruction_trace || unassemble_only) - debug("mov.%i\tr%i,r%i\n", fmt, fd, fs); + debug("mov.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; @@ -1819,7 +1375,7 @@ /* neg.fmt: Floating-point negate */ if ((function & 0x001f003f) == 0x00000007) { if (cpu->machine->instruction_trace || unassemble_only) - debug("neg.%i\tr%i,r%i\n", fmt, fd, fs); + debug("neg.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; @@ -1830,26 +1386,26 @@ /* trunc.l.fmt: Truncate */ if ((function & 0x001f003f) == 0x00000009) { if (cpu->machine->instruction_trace || unassemble_only) - debug("trunc.l.%i\tr%i,r%i\n", fmt, fd, fs); + debug("trunc.l.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; /* TODO: not CVT? */ - fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, FMT_L); + fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, COP1_FMT_L); return 1; } /* trunc.w.fmt: Truncate */ if ((function & 0x001f003f) == 0x0000000d) { if (cpu->machine->instruction_trace || unassemble_only) - debug("trunc.w.%i\tr%i,r%i\n", fmt, fd, fs); + debug("trunc.w.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; /* TODO: not CVT? */ - fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, FMT_W); + fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, COP1_FMT_W); return 1; } @@ -1859,7 +1415,8 @@ int bit; if (cpu->machine->instruction_trace || unassemble_only) - debug("c.%i.%i\tr%i,r%i,r%i\n", cond, fmt, cc, fs, ft); + debug("c.%s.%s\tcc%i,r%i,r%i\n", ccname[cond], + fmtname[fmt], cc, fs, ft); if (unassemble_only) return 1; @@ -1871,20 +1428,20 @@ * FCCR: bits 7..0 * FCSR: bits 31..25 and 23 */ - cp->fcr[FPU_FCCR] &= ~(1 << cc); + cp->fcr[MIPS_FPU_FCCR] &= ~(1 << cc); if (cond_true) - cp->fcr[FPU_FCCR] |= (1 << cc); + cp->fcr[MIPS_FPU_FCCR] |= (1 << cc); if (cc == 0) { - bit = 1 << FCSR_FCC0_SHIFT; - cp->fcr[FPU_FCSR] &= ~bit; + bit = 1 << MIPS_FCSR_FCC0_SHIFT; + cp->fcr[MIPS_FPU_FCSR] &= ~bit; if (cond_true) - cp->fcr[FPU_FCSR] |= bit; + cp->fcr[MIPS_FPU_FCSR] |= bit; } else { - bit = 1 << (FCSR_FCC1_SHIFT + cc-1); - cp->fcr[FPU_FCSR] &= ~bit; + bit = 1 << (MIPS_FCSR_FCC1_SHIFT + cc-1); + cp->fcr[MIPS_FPU_FCSR] &= ~bit; if (cond_true) - cp->fcr[FPU_FCSR] |= bit; + cp->fcr[MIPS_FPU_FCSR] |= bit; } return 1; @@ -1893,33 +1450,33 @@ /* cvt.s.fmt: Convert to single floating-point */ if ((function & 0x001f003f) == 0x00000020) { if (cpu->machine->instruction_trace || unassemble_only) - debug("cvt.s.%i\tr%i,r%i\n", fmt, fd, fs); + debug("cvt.s.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; - fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, FMT_S); + fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, COP1_FMT_S); return 1; } /* cvt.d.fmt: Convert to double floating-point */ if ((function & 0x001f003f) == 0x00000021) { if (cpu->machine->instruction_trace || unassemble_only) - debug("cvt.d.%i\tr%i,r%i\n", fmt, fd, fs); + debug("cvt.d.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; - fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, FMT_D); + fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, COP1_FMT_D); return 1; } /* cvt.w.fmt: Convert to word fixed-point */ if ((function & 0x001f003f) == 0x00000024) { if (cpu->machine->instruction_trace || unassemble_only) - debug("cvt.w.%i\tr%i,r%i\n", fmt, fd, fs); + debug("cvt.w.%s\tr%i,r%i\n", fmtname[fmt], fd, fs); if (unassemble_only) return 1; - fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, FMT_W); + fpu_op(cpu, cp, FPU_OP_CVT, fmt, -1, fs, fd, -1, COP1_FMT_W); return 1; } @@ -1945,19 +1502,13 @@ R2K3K_INDEX_SHIFT; if (i >= cp->nr_of_tlbs) { /* TODO: exception? */ - fatal("warning: tlbr from index %i (too " - "high)\n", i); + fatal("[ warning: tlbr from index %i (too " + "high) ]\n", i); return; } - /* - * TODO: Hm. Earlier I had an & ~0x3f on the high - * assignment and an & ~0xff on the lo0 assignment. - * I wonder why. - */ - - cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; /* & ~0x3f; */ - cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0;/* & ~0xff; */ + cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; + cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0; } else { /* R4000: */ i = cp->reg[COP0_INDEX] & INDEX_MASK; @@ -2049,44 +1600,27 @@ /* * coproc_tlbwri(): * - * 'tlbwr' and 'tlbwi' + * MIPS TLB write random (tlbwr) and write indexed (tlbwi) instructions. */ void coproc_tlbwri(struct cpu *cpu, int randomflag) { struct mips_coproc *cp = cpu->cd.mips.coproc[0]; int index, g_bit; uint64_t oldvaddr; - int old_asid = -1; - - /* - * ... and the last instruction page: - * - * Some thoughts about this: Code running in - * the kernel's physical address space has the - * same vaddr->paddr translation, so the last - * virtual page invalidation only needs to - * happen if we are for some extremely weird - * reason NOT running in the kernel's physical - * address space. - * - * (An even insaner (but probably useless) - * optimization would be to only invalidate - * the last virtual page stuff if the TLB - * update actually affects the vaddr in - * question.) - */ - - if (cpu->pc < (uint64_t)0xffffffff80000000ULL || - cpu->pc >= (uint64_t)0xffffffffc0000000ULL) - cpu->cd.mips.pc_last_virtual_page = - PC_LAST_PAGE_IMPOSSIBLE_VALUE; if (randomflag) { - if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) - index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) - >> R2K3K_RANDOM_SHIFT; - else + if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { + index = ((cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) + >> R2K3K_RANDOM_SHIFT) - 1; + /* R3000 always has 8 wired entries: */ + if (index < 8) + index = cp->nr_of_tlbs - 1; + cp->reg[COP0_RANDOM] = index << R2K3K_RANDOM_SHIFT; + } else { + cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() + % (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); index = cp->reg[COP0_RANDOM] & RANDOM_MASK; + } } else { if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) index = (cp->reg[COP0_INDEX] & R2K3K_INDEX_MASK) @@ -2102,56 +1636,84 @@ return; } + #if 0 /* Debug dump of the previous entry at that index: */ - debug(" old entry at index = %04x", index); - debug(" mask = %016llx", (long long) cp->tlbs[index].mask); - debug(" hi = %016llx", (long long) cp->tlbs[index].hi); - debug(" lo0 = %016llx", (long long) cp->tlbs[index].lo0); - debug(" lo1 = %016llx\n", (long long) cp->tlbs[index].lo1); + fatal("{ old TLB entry at index %02x:", index); + if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { + fatal(" hi=%08"PRIx32, (uint32_t)cp->tlbs[index].hi); + fatal(" lo=%08"PRIx32, (uint32_t)cp->tlbs[index].lo0); + } else { + if (cpu->is_32bit) { + fatal(" mask=%08"PRIx32,(uint32_t)cp->tlbs[index].mask); + fatal(" hi=%08"PRIx32, (uint32_t)cp->tlbs[index].hi); + fatal(" lo0=%08"PRIx32, (uint32_t)cp->tlbs[index].lo0); + fatal(" lo1=%08"PRIx32, (uint32_t)cp->tlbs[index].lo1); + } else { + fatal(" mask=%016"PRIx64, cp->tlbs[index].mask); + fatal(" hi=%016"PRIx64, cp->tlbs[index].hi); + fatal(" lo0=%016"PRIx64, cp->tlbs[index].lo0); + fatal(" lo1=%016"PRIx64, cp->tlbs[index].lo1); + } + } + fatal(" }\n"); #endif - /* Translation caches must be invalidated: */ + /* + * Any virtual address translation for the old TLB entry must be + * invalidated first: + * + * (Only Valid entries need to be invalidated, and only those that + * are either Global, or have the same ASID as the new entry will + * have. No other address translations should be active anyway.) + */ + switch (cpu->cd.mips.cpu_type.mmu_model) { + case MMU3K: oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; - oldvaddr &= 0xffffffffULL; - if (oldvaddr & 0x80000000ULL) - oldvaddr |= 0xffffffff00000000ULL; - old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) - >> R2K3K_ENTRYHI_ASID_SHIFT; + oldvaddr = (int32_t) oldvaddr; -/* TODO: Bug? Why does this if need to be commented out? */ + if (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_V && + (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_G || + (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) == + (cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); - /* if (cp->tlbs[index].lo0 & ENTRYLO_V) */ - invalidate_translation_caches(cpu, 0, oldvaddr, 0, 0); break; - default: - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; + + default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + oldvaddr = cp->tlbs[index].hi & + (ENTRYHI_VPN2_MASK_R10K | ENTRYHI_R_MASK); /* 44 addressable bits: */ if (oldvaddr & 0x80000000000ULL) - oldvaddr |= 0xfffff00000000000ULL; + oldvaddr |= 0x3ffff00000000000ULL; + } else if (cpu->is_32bit) { + /* MIPS32 etc.: */ + oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + oldvaddr = (int32_t)oldvaddr; } else { /* Assume MMU4K */ - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + oldvaddr = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); /* 40 addressable bits: */ if (oldvaddr & 0x8000000000ULL) - oldvaddr |= 0xffffff0000000000ULL; + oldvaddr |= 0x3fffff0000000000ULL; } /* - * Both pages: - * * TODO: non-4KB page sizes! */ - invalidate_translation_caches( - cpu, 0, oldvaddr & ~0x1fff, 0, 0); - invalidate_translation_caches( - cpu, 0, (oldvaddr & ~0x1fff) | 0x1000, 0, 0); + if (cp->tlbs[index].lo0 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); + if (cp->tlbs[index].lo1 & ENTRYLO_V) + cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, + INVALIDATE_VADDR); } - +#if 0 /* * Check for duplicate entries. (There should not be two mappings * from one virtual address to physical addresses.) @@ -2163,9 +1725,11 @@ if (cpu->cd.mips.cpu_type.mmu_model != MMU3K && cpu->cd.mips.cpu_type.rev != MIPS_R4100) { uint64_t vaddr1, vaddr2; - int i, asid; + int i; + unsigned int asid; - vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; + vaddr1 = cp->reg[COP0_ENTRYHI] & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; /* Since this is just a warning, it's probably not necessary to use R4000 masks etc. */ @@ -2178,7 +1742,8 @@ (cp->tlbs[i].hi & ENTRYHI_ASID) != asid) continue; - vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; + vaddr2 = cp->tlbs[i].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) fatal("\n[ WARNING! tlbw%s to index 0x%02x " @@ -2188,12 +1753,12 @@ (long long)vaddr1, asid, i); } } - +#endif /* Write the new entry: */ if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { - uint64_t vaddr, paddr; + uint32_t vaddr, paddr; int wf = cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_D? 1 : 0; unsigned char *memblock = NULL; @@ -2203,36 +1768,104 @@ vaddr = cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_VPN_MASK; paddr = cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_PFN_MASK; - /* TODO: This is ugly. */ - if (paddr < 0x10000000) - memblock = memory_paddr_to_hostaddr( - cpu->mem, paddr, 1); - - if (memblock != NULL && - cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) { - memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr, 0); - /* - * TODO: Hahaha, this is even uglier than the thing - * above. Some OSes seem to map code pages read/write, - * which causes the bintrans cache to be invalidated - * even when it doesn't have to be. - */ -/* if (vaddr < 0x10000000) */ - wf = 0; + /* Invalidate any code translation, if we are writing + a Dirty page to the TLB: */ + if (wf) { + cpu->invalidate_code_translation(cpu, paddr, + INVALIDATE_PADDR); + } + + /* Set new last_written_tlb_index hint: */ + cpu->cd.mips.last_written_tlb_index = index; + + if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { + fatal("Wow! Interesting case; tlbw* while caches" + " are isolated. TODO\n"); + /* Don't update the translation table in this + case... */ + exit(1); + } + /* If we have a memblock (host page) for the physical + page, then add a translation for it immediately: */ + if (memblock != NULL && + cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) cpu->update_translation_table(cpu, vaddr, memblock, wf, paddr); - } } else { - /* R4000: */ - g_bit = (cp->reg[COP0_ENTRYLO0] & - cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; + /* R4000 etc.: */ + unsigned char *memblock = NULL; + int pfn_shift = 12, vpn_shift = 12; + int wf0, wf1, mask; + uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; + cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; + wf0 = cp->tlbs[index].lo0 & ENTRYLO_D; + wf1 = cp->tlbs[index].lo1 & ENTRYLO_D; + + mask = cp->reg[COP0_PAGEMASK]; + if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { + pfn_shift = 10; + mask |= 0x07ff; + } else { + mask |= 0x1fff; + } + switch (mask) { + case 0x00007ff: + if (cp->tlbs[index].lo0 & ENTRYLO_V || + cp->tlbs[index].lo1 & ENTRYLO_V) { + fatal("1KB pages don't work with dyntrans.\n"); + exit(1); + } + vpn_shift = 10; + break; + case 0x0001fff: break; + case 0x0007fff: vpn_shift = 14; break; + case 0x001ffff: vpn_shift = 16; break; + case 0x007ffff: vpn_shift = 18; break; + case 0x01fffff: vpn_shift = 20; break; + case 0x07fffff: vpn_shift = 22; break; + case 0x1ffffff: vpn_shift = 24; break; + case 0x7ffffff: vpn_shift = 26; break; + default:fatal("Unimplemented MASK = 0x%016x\n", mask); + exit(1); + } + + paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << pfn_shift; + paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << pfn_shift; + + if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + vaddr0 = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); + /* 44 addressable bits: */ + if (vaddr0 & 0x80000000000ULL) + vaddr0 |= 0x3ffff00000000000ULL; + } else if (cpu->is_32bit) { + /* MIPS32 etc.: */ + vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + vaddr0 = (int32_t)vaddr0; + } else { + /* Assume MMU4K */ + vaddr0 = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); + /* 40 addressable bits: */ + if (vaddr0 & 0x8000000000ULL) + vaddr0 |= 0x3fffff0000000000ULL; + } + + vaddr1 = vaddr0 | (1 << vpn_shift); + + g_bit = (cp->reg[COP0_ENTRYLO0] & + cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; + if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { /* NOTE: The VR4131 (and possibly others) don't have a Global bit in entryhi */ @@ -2245,41 +1878,42 @@ if (g_bit) cp->tlbs[index].hi |= TLB_G; } - } - if (randomflag) { - if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { - cp->reg[COP0_RANDOM] = - ((random() % (cp->nr_of_tlbs - 8)) + 8) - << R2K3K_RANDOM_SHIFT; - } else { - cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() - % (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); + /* + * Invalidate any code translations, if we are writing Dirty + * pages to the TLB: (TODO: 4KB hardcoded... ugly) + */ + for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { + if (wf0) + cpu->invalidate_code_translation(cpu, + paddr0 + ptmp, INVALIDATE_PADDR); + if (wf1) + cpu->invalidate_code_translation(cpu, + paddr1 + ptmp, INVALIDATE_PADDR); } - } -} + /* + * If we have a memblock (host page) for the physical page, + * then add a translation for it immediately, to save some + * time. (It would otherwise be added later on anyway, + * because of a translation miss.) + * + * NOTE/TODO: This is only for 4KB pages so far. It would + * be too expensive to add e.g. 16MB pages like + * this. + */ + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr0, 0); + if (memblock != NULL && cp->reg[COP0_ENTRYLO0] & ENTRYLO_V) + cpu->update_translation_table(cpu, vaddr0, memblock, + wf0, paddr0); + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr1, 0); + if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) + cpu->update_translation_table(cpu, vaddr1, memblock, + wf1, paddr1); -/* - * coproc_rfe(): - * - * Return from exception. (R3000 etc.) - */ -void coproc_rfe(struct cpu *cpu) -{ - int oldmode; - - oldmode = cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS1_SR_KU_CUR; - - cpu->cd.mips.coproc[0]->reg[COP0_STATUS] = - (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & ~0x3f) | - ((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 0x3c) >> 2); - - /* Changing from kernel to user mode? Then this is necessary: */ - if (!oldmode && - (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & - MIPS1_SR_KU_CUR)) - invalidate_translation_caches(cpu, 0, 0, 1, 0); + /* Set new last_written_tlb_index hint: */ + cpu->cd.mips.last_written_tlb_index = index; + } } @@ -2290,45 +1924,16 @@ */ void coproc_eret(struct cpu *cpu) { - int oldmode, newmode; - - /* Kernel mode flag: */ - oldmode = 0; - if ((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS3_SR_KSU_MASK) - != MIPS3_SR_KSU_USER - || (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & (STATUS_EXL | - STATUS_ERL)) || - (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 1) == 0) - oldmode = 1; - if (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & STATUS_ERL) { - cpu->pc = cpu->cd.mips.pc_last = - cpu->cd.mips.coproc[0]->reg[COP0_ERROREPC]; + cpu->pc = cpu->cd.mips.coproc[0]->reg[COP0_ERROREPC]; cpu->cd.mips.coproc[0]->reg[COP0_STATUS] &= ~STATUS_ERL; } else { - cpu->pc = cpu->cd.mips.pc_last = - cpu->cd.mips.coproc[0]->reg[COP0_EPC]; - cpu->cd.mips.delay_slot = 0; + cpu->pc = cpu->cd.mips.coproc[0]->reg[COP0_EPC]; + cpu->delay_slot = 0; cpu->cd.mips.coproc[0]->reg[COP0_STATUS] &= ~STATUS_EXL; } cpu->cd.mips.rmw = 0; /* the "LL bit" */ - - /* New kernel mode flag: */ - newmode = 0; - if ((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS3_SR_KSU_MASK) - != MIPS3_SR_KSU_USER - || (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & (STATUS_EXL | - STATUS_ERL)) || - (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 1) == 0) - newmode = 1; - -#if 0 - /* Changing from kernel to user mode? - Then this is necessary: TODO */ - if (oldmode && !newmode) - invalidate_translation_caches(cpu, 0, 0, 1, 0); -#endif } @@ -2358,13 +1963,11 @@ return; } -#if 0 /* No FPU? */ if (cpnr == 1 && (cpu->cd.mips.cpu_type.flags & NOFPU)) { mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, cpnr, 0, 0, 0); return; } -#endif /* For quick reference: */ copz = (function >> 21) & 31; @@ -2379,7 +1982,7 @@ if (cpnr == 0) debug("%s", cop0_names[rd]); else - debug("cpreg%i", rd); + debug("r%i", rd); if (function & 7) debug(",%i", (int)(function & 7)); debug("\n"); @@ -2405,7 +2008,7 @@ if (cpnr == 0) debug("%s", cop0_names[rd]); else - debug("cpreg%i", rd); + debug("r%i", rd); if (function & 7) debug(",%i", (int)(function & 7)); debug("\n"); @@ -2434,9 +2037,7 @@ regnames[rt], fs); return; } - cpu->cd.mips.gpr[rt] = cp->fcr[fs] & 0xffffffffULL; - if (cpu->cd.mips.gpr[rt] & 0x80000000ULL) - cpu->cd.mips.gpr[rt] |= 0xffffffff00000000ULL; + cpu->cd.mips.gpr[rt] = (int32_t)cp->fcr[fs]; /* TODO: implement delay for gpr[rt] (for MIPS I,II,III only) */ return; @@ -2468,20 +2069,21 @@ on status bits! */ switch (fs) { - case FPU_FCCR: - cp->fcr[FPU_FCSR] = - (cp->fcr[FPU_FCSR] & + case MIPS_FPU_FCCR: + cp->fcr[MIPS_FPU_FCSR] = + (cp->fcr[MIPS_FPU_FCSR] & 0x017fffffULL) | ((tmp & 1) - << FCSR_FCC0_SHIFT) + << MIPS_FCSR_FCC0_SHIFT) | (((tmp & 0xfe) >> 1) << - FCSR_FCC1_SHIFT); + MIPS_FCSR_FCC1_SHIFT); break; - case FPU_FCSR: - cp->fcr[FPU_FCCR] = - (cp->fcr[FPU_FCCR] & + case MIPS_FPU_FCSR: + cp->fcr[MIPS_FPU_FCCR] = + (cp->fcr[MIPS_FPU_FCCR] & 0xffffff00ULL) | ((tmp >> - FCSR_FCC0_SHIFT) & 1) | - (((tmp >> FCSR_FCC1_SHIFT) + MIPS_FCSR_FCC0_SHIFT) & 1) | + (((tmp >> + MIPS_FCSR_FCC1_SHIFT) & 0x7f) << 1); break; default: @@ -2505,134 +2107,101 @@ return; } - /* For AU1500 and probably others: deret */ - if (function == 0x0200001f) { - if (unassemble_only) { - debug("deret\n"); - return; - } - - /* - * According to the MIPS64 manual, deret loads PC from the - * DEPC cop0 register, and jumps there immediately. No - * delay slot. - * - * TODO: This instruction is only available if the processor - * is in debug mode. (What does that mean?) - * TODO: This instruction is undefined in a delay slot. - */ - - cpu->pc = cpu->cd.mips.pc_last = cp->reg[COP0_DEPC]; - cpu->cd.mips.delay_slot = 0; - cp->reg[COP0_STATUS] &= ~STATUS_EXL; - - return; - } - /* Ugly R5900 hacks: */ - if ((function & 0xfffff) == 0x38) { /* ei */ - if (unassemble_only) { - debug("ei\n"); + if (cpu->cd.mips.cpu_type.rev == MIPS_R5900) { + if ((function & 0xfffff) == COP0_EI) { + if (unassemble_only) { + debug("ei\n"); + return; + } + cpu->cd.mips.coproc[0]->reg[COP0_STATUS] |= + R5900_STATUS_EIE; return; } - cpu->cd.mips.coproc[0]->reg[COP0_STATUS] |= R5900_STATUS_EIE; - return; - } - if ((function & 0xfffff) == 0x39) { /* di */ - if (unassemble_only) { - debug("di\n"); + if ((function & 0xfffff) == COP0_DI) { + if (unassemble_only) { + debug("di\n"); + return; + } + cpu->cd.mips.coproc[0]->reg[COP0_STATUS] &= + ~R5900_STATUS_EIE; return; } - cpu->cd.mips.coproc[0]->reg[COP0_STATUS] &= ~R5900_STATUS_EIE; - return; } co_bit = (function >> 25) & 1; /* TLB operations and other things: */ if (cp->coproc_nr == 0) { + if (!unassemble_only) { + fatal("FATAL INTERNAL ERROR: Should be implemented" + " with dyntrans instead.\n"); + exit(1); + } + op = (function) & 0xff; switch (co_bit) { case 1: switch (op) { case COP0_TLBR: /* Read indexed TLB entry */ - if (unassemble_only) { - debug("tlbr\n"); - return; - } - coproc_tlbpr(cpu, 1); + debug("tlbr\n"); return; case COP0_TLBWI: /* Write indexed */ case COP0_TLBWR: /* Write random */ - if (unassemble_only) { - if (op == COP0_TLBWI) - debug("tlbwi"); - else - debug("tlbwr"); - if (!running) { - debug("\n"); - return; - } - debug("\tindex=%08llx", - (long long)cp->reg[COP0_INDEX]); - debug(", random=%08llx", - (long long)cp->reg[COP0_RANDOM]); - debug(", mask=%016llx", - (long long)cp->reg[COP0_PAGEMASK]); - debug(", hi=%016llx", - (long long)cp->reg[COP0_ENTRYHI]); - debug(", lo0=%016llx", - (long long)cp->reg[COP0_ENTRYLO0]); - debug(", lo1=%016llx\n", - (long long)cp->reg[COP0_ENTRYLO1]); + if (op == COP0_TLBWI) + debug("tlbwi"); + else + debug("tlbwr"); + if (!running) { + debug("\n"); + return; } - coproc_tlbwri(cpu, op == COP0_TLBWR); + debug("\tindex=%08llx", + (long long)cp->reg[COP0_INDEX]); + debug(", random=%08llx", + (long long)cp->reg[COP0_RANDOM]); + debug(", mask=%016llx", + (long long)cp->reg[COP0_PAGEMASK]); + debug(", hi=%016llx", + (long long)cp->reg[COP0_ENTRYHI]); + debug(", lo0=%016llx", + (long long)cp->reg[COP0_ENTRYLO0]); + debug(", lo1=%016llx\n", + (long long)cp->reg[COP0_ENTRYLO1]); return; case COP0_TLBP: /* Probe TLB for matching entry */ - if (unassemble_only) { - debug("tlbp\n"); - return; - } - coproc_tlbpr(cpu, 0); + debug("tlbp\n"); return; case COP0_RFE: /* R2000/R3000 only: Return from Exception */ - if (unassemble_only) { - debug("rfe\n"); - return; - } - coproc_rfe(cpu); + debug("rfe\n"); return; case COP0_ERET: /* R4000: Return from exception */ - if (unassemble_only) { - debug("eret\n"); - return; + debug("eret\n"); + return; + case COP0_DERET: + debug("deret\n"); + return; + case COP0_WAIT: + { + int code = (function >> 6) & 0x7ffff; + debug("wait"); + if (code > 0) + debug("\t0x%x", code); + debug("\n"); } - coproc_eret(cpu); return; case COP0_STANDBY: - if (unassemble_only) { - debug("standby\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("standby\n"); return; case COP0_SUSPEND: - if (unassemble_only) { - debug("suspend\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("suspend\n"); return; case COP0_HIBERNATE: - if (unassemble_only) { - debug("hibernate\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("hibernate\n"); return; default: ; @@ -2652,30 +2221,16 @@ return; } - /* TODO: RM5200 idle (?) */ - if ((cp->coproc_nr==0 || cp->coproc_nr==3) && function == 0x02000020) { - if (unassemble_only) { - debug("idle(?)\n"); /* TODO */ - return; - } - - /* Idle? TODO */ - return; - } - if (unassemble_only) { debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); return; } - fatal("cpu%i: UNIMPLEMENTED coproc%i function %08lx " - "(pc = %016llx)\n", cpu->cpu_id, cp->coproc_nr, function, - (long long)cpu->cd.mips.pc_last); -#if 1 - single_step = 1; -#else + fatal("cpu%i: UNIMPLEMENTED coproc%i function %08"PRIx32" " + "(pc = %016"PRIx64")\n", cpu->cpu_id, cp->coproc_nr, + (uint32_t)function, cpu->pc); + mips_cpu_exception(cpu, EXCEPTION_CPU, 0, 0, cp->coproc_nr, 0, 0, 0); -#endif } #endif /* ENABLE_MIPS */