--- trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:18:51 14 +++ trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:20:10 26 @@ -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.2 2005/09/11 10:37:37 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.37 2006/06/25 02:46:07 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -35,11 +35,11 @@ #include #include -#include "bintrans.h" #include "cop0.h" #include "cpu.h" #include "cpu_mips.h" #include "emul.h" +#include "float_emul.h" #include "machine.h" #include "memory.h" #include "mips_cpu_types.h" @@ -68,14 +68,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(): * @@ -83,41 +75,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 dependant */ - | ((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; @@ -302,52 +315,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); } } @@ -414,11 +384,7 @@ if (coproc_nr == 0) { c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries; - c->tlbs = malloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); - if (c->tlbs == NULL) { - fprintf(stderr, "mips_coproc_new(): out of memory\n"); - exit(1); - } + c->tlbs = zeroed_alloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); /* * Start with nothing in the status register. This makes sure @@ -441,13 +407,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 */ ; @@ -457,7 +427,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) @@ -539,462 +509,33 @@ /* - * old_update_translation_table(): - */ -static void old_update_translation_table(struct cpu *cpu, uint64_t vaddr_page, - unsigned char *host_page, int writeflag, uint64_t paddr_page) -{ - 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; - } - - 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; - } - } - 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; - } - } - - 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; - } - } -} - - -/* - * 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; - } - } - } - } -} - - -/* - * mips_invalidate_translation_caches_paddr(): + * invalidate_asid(): * - * Invalidate based on physical address. - */ -void mips_invalidate_translation_caches_paddr(struct cpu *cpu, - uint64_t paddr, int flags) -{ - paddr &= ~0xfff; - - if (cpu->machine->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; - - 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" - "_paddr(): bad pagemask?\n"); - } - 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<bintrans_data_hostpage[i] = NULL; -} -#endif -} - - -/* - * invalidate_translation_caches(): + * 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. * - * 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. + * Note: In the R3000 case, the asid argument is shifted 6 bits. */ -static void invalidate_translation_caches(struct cpu *cpu, - int all, uint64_t vaddr, int kernelspace, int old_asid_to_invalidate) +static void invalidate_asid(struct cpu *cpu, int asid) { - 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. */ + struct mips_coproc *cp = cpu->cd.mips.coproc[0]; + int i, ntlbs = cp->nr_of_tlbs; + struct mips_tlb *tlb = cp->tlbs; - invalidate_table_entry(cpu, tlb_vaddr); - invalidate_table_entry(cpu, tlb_vaddr | - (1 << psize)); + 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); } - } - } 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 + } else { + /* TODO: Implement support for other. */ + cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); + } } @@ -1016,30 +557,7 @@ if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; 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) { - /* - * 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); - } - - unimpl = 0; - } + if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; @@ -1121,11 +639,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,7 +708,7 @@ 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; @@ -1201,7 +718,7 @@ /* Clear the timer interrupt bit (bit 7): */ cpu->cd.mips.compare_register_set = 1; 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; @@ -1209,14 +726,13 @@ 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 +745,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,58 +804,32 @@ 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); } + +#if 1 + /* + * NOTE: This is not needed for NetBSD, but + * Ultrix and Linux still needs this. They + * shouldn't, though. Something else is buggy. + */ + cpu_create_or_reset_tc(cpu); +#endif } unimpl = 0; break; @@ -1347,10 +838,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 */ @@ -1426,11 +913,22 @@ * * TODO: Move this to some other file? */ -#define FMT_S 16 -#define FMT_D 17 -#define FMT_W 20 -#define FMT_L 21 -#define FMT_PS 22 +static int mips_fmt_to_ieee_fmt[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IEEE_FMT_S, IEEE_FMT_D, 0, 0, + IEEE_FMT_W, IEEE_FMT_L, /* PS (Paired Single) */ 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +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 @@ -1442,150 +940,7 @@ #define FPU_OP_C 8 #define FPU_OP_ABS 9 #define FPU_OP_NEG 10 -/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, - RSQRT */ - - -struct internal_float_value { - double f; - int nan; -}; - - -/* - * fpu_interpret_float_value(): - * - * Interprets a float value from binary IEEE format into an - * internal_float_value struct. - */ -static void fpu_interpret_float_value(uint64_t reg, - struct internal_float_value *fvp, int fmt) -{ - int n_frac = 0, n_exp = 0; - int i, nan, sign = 0, exponent; - double fraction; - - memset(fvp, 0, sizeof(struct internal_float_value)); - - /* n_frac and n_exp: */ - switch (fmt) { - case FMT_S: n_frac = 23; n_exp = 8; break; - case FMT_W: n_frac = 31; n_exp = 0; break; - case FMT_D: n_frac = 52; n_exp = 11; break; - case FMT_L: n_frac = 63; n_exp = 0; break; - default: - fatal("fpu_interpret_float_value(): " - "unimplemented format %i\n", fmt); - } - - /* exponent: */ - exponent = 0; - switch (fmt) { - case FMT_W: - reg &= 0xffffffffULL; - case FMT_L: - break; - case FMT_S: - reg &= 0xffffffffULL; - case FMT_D: - exponent = (reg >> n_frac) & ((1 << n_exp) - 1); - exponent -= (1 << (n_exp-1)) - 1; - break; - default: - fatal("fpu_interpret_float_value(): unimplemented " - "format %i\n", fmt); - } - - /* nan: */ - nan = 0; - switch (fmt) { - case FMT_S: - if (reg == 0x7fffffffULL || reg == 0x7fbfffffULL) - nan = 1; - break; - case FMT_D: - if (reg == 0x7fffffffffffffffULL || - reg == 0x7ff7ffffffffffffULL) - nan = 1; - break; - } - - if (nan) { - fvp->f = 1.0; - goto no_reasonable_result; - } - - /* fraction: */ - fraction = 0.0; - switch (fmt) { - case FMT_W: - { - int32_t r_int = reg; - fraction = r_int; - } - break; - case FMT_L: - { - int64_t r_int = reg; - fraction = r_int; - } - break; - case FMT_S: - case FMT_D: - /* sign: */ - sign = (reg >> 31) & 1; - if (fmt == FMT_D) - sign = (reg >> 63) & 1; - - fraction = 0.0; - for (i=0; i> i) & 1; - fraction /= 2.0; - if (bit) - fraction += 1.0; - } - /* Add implicit bit 0: */ - fraction = (fraction / 2.0) + 1.0; - break; - default: - fatal("fpu_interpret_float_value(): " - "unimplemented format %i\n", fmt); - } - - /* form the value: */ - fvp->f = fraction; - - /* fatal("load reg=%016llx sign=%i exponent=%i fraction=%f ", - (long long)reg, sign, exponent, fraction); */ - - /* TODO: this is awful for exponents of large magnitude. */ - if (exponent > 0) { - /* - * NOTE / TODO: - * - * This is an ulgy workaround on Alpha, where it seems that - * multiplying by 2, 1024 times causes a floating point - * exception. (Triggered by running for example NetBSD/pmax - * 2.0 on an Alpha.) - */ - if (exponent == 1024) - exponent = 1023; - - while (exponent-- > 0) - fvp->f *= 2.0; - } else if (exponent < 0) { - while (exponent++ < 0) - fvp->f /= 2.0; - } - - if (sign) - fvp->f = -fvp->f; - -no_reasonable_result: - fvp->nan = nan; - - /* fatal("f = %f\n", fvp->f); */ -} +/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, RSQRT */ /* @@ -1596,117 +951,14 @@ static void fpu_store_float_value(struct mips_coproc *cp, int fd, double nf, int fmt, int nan) { - int n_frac = 0, n_exp = 0, signofs=0; - int i, exponent; - uint64_t r = 0, r2; - int64_t r3; - - /* n_frac and n_exp: */ - switch (fmt) { - case FMT_S: n_frac = 23; n_exp = 8; signofs = 31; break; - case FMT_W: n_frac = 31; n_exp = 0; signofs = 31; break; - case FMT_D: n_frac = 52; n_exp = 11; signofs = 63; break; - case FMT_L: n_frac = 63; n_exp = 0; signofs = 63; break; - default: - fatal("fpu_store_float_value(): unimplemented format" - " %i\n", fmt); - } - - if ((fmt == FMT_S || fmt == FMT_D) && nan) - goto store_nan; - - /* fraction: */ - switch (fmt) { - case FMT_W: - case FMT_L: - /* - * This causes an implicit conversion of double to integer. - * If nf < 0.0, then r2 will begin with a sequence of binary - * 1's, which is ok. - */ - r3 = nf; - r2 = r3; - r |= r2; - - if (fmt == FMT_W) - r &= 0xffffffffULL; - break; - case FMT_S: - case FMT_D: - /* fatal("store f=%f ", nf); */ - - /* sign bit: */ - if (nf < 0.0) { - r |= ((uint64_t)1 << signofs); - nf = -nf; - } - - /* - * How to convert back from double to exponent + fraction: - * We want fraction to be 1.xxx, that is - * 1.0 <= fraction < 2.0 - * - * This method is very slow but should work: - */ - exponent = 0; - while (nf < 1.0 && exponent > -1023) { - nf *= 2.0; - exponent --; - } - while (nf >= 2.0 && exponent < 1023) { - nf /= 2.0; - exponent ++; - } - - /* Here: 1.0 <= nf < 2.0 */ - /* fatal(" nf=%f", nf); */ - nf -= 1.0; /* remove implicit first bit */ - for (i=n_frac-1; i>=0; i--) { - nf *= 2.0; - if (nf >= 1.0) { - r |= ((uint64_t)1 << i); - nf -= 1.0; - } - /* printf("\n i=%2i r=%016llx\n", i, (long long)r); */ - } - - /* Insert the exponent into the resulting word: */ - /* (First bias, then make sure it's within range) */ - exponent += (((uint64_t)1 << (n_exp-1)) - 1); - if (exponent < 0) - exponent = 0; - if (exponent >= ((int64_t)1 << n_exp)) - exponent = ((int64_t)1 << n_exp) - 1; - r |= (uint64_t)exponent << n_frac; - - /* Special case for 0.0: */ - if (exponent == 0) - r = 0; - - /* fatal(" exp=%i, r = %016llx\n", exponent, (long long)r); */ - - break; - default: - /* TODO */ - fatal("fpu_store_float_value(): unimplemented format " - "%i\n", fmt); - } - -store_nan: - if (nan) { - if (fmt == FMT_S) - r = 0x7fffffffULL; - else if (fmt == FMT_D) - r = 0x7fffffffffffffffULL; - else - r = 0x7fffffffULL; - } + int ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; + uint64_t r = ieee_store_float_value(nf, ieee_fmt, nan); /* - * TODO: this is for 32-bit mode. It has to be updated later - * for 64-bit coprocessor stuff. + * 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; @@ -1726,19 +978,18 @@ /* * fpu_op(): * - * Perform a floating-point operation. For those of fs and ft - * that are >= 0, those numbers are interpreted into local - * variables. + * Perform a floating-point operation. For those of fs and ft that are >= 0, + * those numbers are interpreted into local variables. * - * Only FPU_OP_C (compare) returns anything of interest, 1 for - * true, 0 for false. + * Only FPU_OP_C (compare) returns anything of interest, 1 for true, 0 for + * false. */ static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt, int ft, int fs, int fd, int cond, int output_fmt) { /* Potentially two input registers, fs and ft */ - struct internal_float_value float_value[2]; - int unordered, nan; + struct ieee_float_value float_value[2]; + int unordered, nan, ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; uint64_t fs_v = 0; double nf; @@ -1746,19 +997,19 @@ 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); - fpu_interpret_float_value(fs_v, &float_value[0], fmt); + ieee_interpret_float_value(fs_v, &float_value[0], ieee_fmt); } if (ft >= 0) { 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); - fpu_interpret_float_value(v, &float_value[1], fmt); + ieee_interpret_float_value(v, &float_value[1], ieee_fmt); } switch (op) { @@ -1833,7 +1084,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) @@ -1947,7 +1198,7 @@ if (unassemble_only) return 1; - if (cpu->cd.mips.delay_slot) { + if (cpu->delay_slot) { fatal("%s: jump inside a jump's delay slot, " "or similar. TODO\n", instr_mnem); cpu->running = 0; @@ -1956,17 +1207,18 @@ /* Both the FCCR and FCSR contain condition code bits... */ if (cc == 0) - cond_true = (cp->fcr[FPU_FCSR] >> FCSR_FCC0_SHIFT) & 1; + cond_true = (cp->fcr[MIPS_FPU_FCSR] >> + MIPS_FCSR_FCC0_SHIFT) & 1; else - cond_true = (cp->fcr[FPU_FCSR] >> - (FCSR_FCC1_SHIFT + cc-1)) & 1; + cond_true = (cp->fcr[MIPS_FPU_FCSR] >> + (MIPS_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); + cpu->delay_slot = TO_BE_DELAYED; + cpu->delay_jmpaddr = cpu->pc + (imm << 2); } else { /* "likely": */ if (nd) { @@ -1981,7 +1233,8 @@ /* 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; @@ -1992,7 +1245,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; @@ -2003,7 +1257,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; @@ -2014,7 +1269,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; @@ -2025,7 +1281,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; @@ -2036,7 +1292,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; @@ -2047,7 +1303,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; @@ -2058,7 +1314,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; @@ -2069,26 +1325,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; } @@ -2098,7 +1354,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; @@ -2110,20 +1367,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; @@ -2132,33 +1389,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; } @@ -2288,44 +1545,26 @@ /* * 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; + int index, g_bit, old_asid = -1; 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) + if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { + cp->reg[COP0_RANDOM] = + ((random() % (cp->nr_of_tlbs - 8)) + 8) + << R2K3K_RANDOM_SHIFT; index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) >> R2K3K_RANDOM_SHIFT; - else + } 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) @@ -2341,17 +1580,36 @@ 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: + */ + switch (cpu->cd.mips.cpu_type.mmu_model) { + case MMU3K: oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; oldvaddr &= 0xffffffffULL; @@ -2360,17 +1618,19 @@ old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) >> R2K3K_ENTRYHI_ASID_SHIFT; -/* TODO: Bug? Why does this if need to be commented out? */ - - /* if (cp->tlbs[index].lo0 & ENTRYLO_V) */ - invalidate_translation_caches(cpu, 0, oldvaddr, 0, 0); + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); break; - default: - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + + default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; /* 44 addressable bits: */ if (oldvaddr & 0x80000000000ULL) oldvaddr |= 0xfffff00000000000ULL; + } 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; @@ -2379,15 +1639,17 @@ oldvaddr |= 0xffffff0000000000ULL; } +#if 1 + cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); +#else /* - * 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); + cpu->invalidate_translation_caches(cpu, oldvaddr, + INVALIDATE_VADDR); + cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, + INVALIDATE_VADDR); +#endif } @@ -2402,7 +1664,8 @@ 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; asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; @@ -2432,7 +1695,7 @@ /* 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; @@ -2442,24 +1705,20 @@ 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); + memblock = memory_paddr_to_hostaddr(cpu->mem, paddr, 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); + } + + /* 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) { memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); - - /* - * 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; - cpu->update_translation_table(cpu, vaddr, memblock, wf, paddr); } @@ -2484,17 +1743,50 @@ 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; + /* Invalidate any code translations, if we are writing + Dirty pages to the TLB: */ + if (cp->reg[COP0_PAGEMASK] != 0 && + cp->reg[COP0_PAGEMASK] != 0x1800) { + printf("TODO: MASK = %08"PRIx32"\n", + (uint32_t)cp->reg[COP0_PAGEMASK]); + exit(1); + } + + if (cp->tlbs[index].lo0 & ENTRYLO_D) + cpu->invalidate_code_translation(cpu, + ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << 12, + INVALIDATE_PADDR); + if (cp->tlbs[index].lo1 & ENTRYLO_D) + cpu->invalidate_code_translation(cpu, + ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) + >> ENTRYLO_PFN_SHIFT) << 12, + INVALIDATE_PADDR); + +#if 1 + if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; + /* 44 addressable bits: */ + if (oldvaddr & 0x80000000000ULL) + oldvaddr |= 0xfffff00000000000ULL; + } else if (cpu->is_32bit) { + /* MIPS32 etc.: */ + oldvaddr = (int32_t)oldvaddr; } else { - cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() - % (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); + /* Assume MMU4K */ + oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + /* 40 addressable bits: */ + if (oldvaddr & 0x8000000000ULL) + oldvaddr |= 0xffffff0000000000ULL; } + +cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo0 & +ENTRYLO_PFN_MASK) >> ENTRYLO_PFN_SHIFT) << 12, INVALIDATE_PADDR); +cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo1 & +ENTRYLO_PFN_MASK) >> ENTRYLO_PFN_SHIFT) << 12, INVALIDATE_PADDR); + +#endif } } @@ -2506,19 +1798,9 @@ */ 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); } @@ -2529,45 +1811,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 } @@ -2597,13 +1850,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; @@ -2618,7 +1869,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"); @@ -2644,7 +1895,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"); @@ -2673,9 +1924,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; @@ -2707,20 +1956,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: @@ -2744,48 +1994,28 @@ 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; @@ -2826,6 +2056,7 @@ (long long)cp->reg[COP0_ENTRYLO0]); debug(", lo1=%016llx\n", (long long)cp->reg[COP0_ENTRYLO1]); + return; } coproc_tlbwri(cpu, op == COP0_TLBWR); return; @@ -2852,6 +2083,25 @@ } coproc_eret(cpu); return; + case COP0_DERET: + 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 = cp->reg[COP0_DEPC]; + cpu->delay_slot = 0; + cp->reg[COP0_STATUS] &= ~STATUS_EXL; + return; case COP0_STANDBY: if (unassemble_only) { debug("standby\n"); @@ -2907,14 +2157,11 @@ 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 */