25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_mips_coproc.c,v 1.37 2006/06/25 02:46:07 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.53 2006/08/11 17:43:30 debug Exp $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
533 |
INVALIDATE_VADDR); |
INVALIDATE_VADDR); |
534 |
} |
} |
535 |
} else { |
} else { |
536 |
/* TODO: Implement support for other. */ |
int non4kpages = 0; |
537 |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
538 |
|
|
539 |
|
if (cpu->is_32bit) { |
540 |
|
topbit = 0x80000000; |
541 |
|
fillmask = 0xffffffff00000000ULL; |
542 |
|
} else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
543 |
|
topbit <<= 43; |
544 |
|
fillmask <<= 4; |
545 |
|
} else { |
546 |
|
topbit <<= 39; |
547 |
|
} |
548 |
|
|
549 |
|
for (i=0; i<ntlbs; i++) { |
550 |
|
if (tlb[i].mask != 0 && tlb[i].mask != 0x1800) { |
551 |
|
non4kpages = 1; |
552 |
|
continue; |
553 |
|
} |
554 |
|
|
555 |
|
if ((tlb[i].hi & ENTRYHI_ASID) == asid && |
556 |
|
!(tlb[i].hi & TLB_G)) { |
557 |
|
uint64_t vaddr0, vaddr1; |
558 |
|
vaddr0 = cp->tlbs[i].hi & ~fillmask; |
559 |
|
if (vaddr0 & topbit) |
560 |
|
vaddr0 |= fillmask; |
561 |
|
vaddr1 = vaddr0 | 0x1000; /* TODO: mask */ |
562 |
|
|
563 |
|
if (tlb[i].lo0 & ENTRYLO_V) |
564 |
|
cpu->invalidate_translation_caches(cpu, |
565 |
|
vaddr0, INVALIDATE_VADDR); |
566 |
|
if (tlb[i].lo1 & ENTRYLO_V) |
567 |
|
cpu->invalidate_translation_caches(cpu, |
568 |
|
vaddr1, INVALIDATE_VADDR); |
569 |
|
} |
570 |
|
} |
571 |
|
|
572 |
|
if (non4kpages) { |
573 |
|
cpu->invalidate_translation_caches(cpu, |
574 |
|
0, INVALIDATE_ALL); |
575 |
|
} |
576 |
} |
} |
577 |
} |
} |
578 |
|
|
859 |
cpu->invalidate_translation_caches( |
cpu->invalidate_translation_caches( |
860 |
cpu, 0, INVALIDATE_ALL); |
cpu, 0, INVALIDATE_ALL); |
861 |
} |
} |
|
|
|
|
#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 |
|
862 |
} |
} |
863 |
unimpl = 0; |
unimpl = 0; |
864 |
break; |
break; |
1204 |
|
|
1205 |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
1206 |
if ((function & 0x03e00000) == 0x01000000) { |
if ((function & 0x03e00000) == 0x01000000) { |
1207 |
int nd, tf, imm, cond_true; |
int nd, tf, imm; |
1208 |
char *instr_mnem; |
char *instr_mnem; |
1209 |
|
|
1210 |
/* cc are bits 20..18: */ |
/* cc are bits 20..18: */ |
1227 |
if (unassemble_only) |
if (unassemble_only) |
1228 |
return 1; |
return 1; |
1229 |
|
|
1230 |
if (cpu->delay_slot) { |
fatal("INTERNAL ERROR: MIPS coprocessor branches should not" |
1231 |
fatal("%s: jump inside a jump's delay slot, " |
" be implemented in cpu_mips_coproc.c, but in" |
1232 |
"or similar. TODO\n", instr_mnem); |
" cpu_mips_instr.c!\n"); |
1233 |
cpu->running = 0; |
exit(1); |
|
return 1; |
|
|
} |
|
|
|
|
|
/* Both the FCCR and FCSR contain condition code bits... */ |
|
|
if (cc == 0) |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
MIPS_FCSR_FCC0_SHIFT) & 1; |
|
|
else |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
(MIPS_FCSR_FCC1_SHIFT + cc-1)) & 1; |
|
|
|
|
|
if (!tf) |
|
|
cond_true = !cond_true; |
|
|
|
|
|
if (cond_true) { |
|
|
cpu->delay_slot = TO_BE_DELAYED; |
|
|
cpu->delay_jmpaddr = cpu->pc + (imm << 2); |
|
|
} else { |
|
|
/* "likely": */ |
|
|
if (nd) { |
|
|
/* nullify the delay slot */ |
|
|
cpu->cd.mips.nullify_next = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
return 1; |
|
1234 |
} |
} |
1235 |
|
|
1236 |
/* add.fmt: Floating-point add */ |
/* add.fmt: Floating-point add */ |
1444 |
R2K3K_INDEX_SHIFT; |
R2K3K_INDEX_SHIFT; |
1445 |
if (i >= cp->nr_of_tlbs) { |
if (i >= cp->nr_of_tlbs) { |
1446 |
/* TODO: exception? */ |
/* TODO: exception? */ |
1447 |
fatal("warning: tlbr from index %i (too " |
fatal("[ warning: tlbr from index %i (too " |
1448 |
"high)\n", i); |
"high) ]\n", i); |
1449 |
return; |
return; |
1450 |
} |
} |
1451 |
|
|
1452 |
/* |
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; |
1453 |
* TODO: Hm. Earlier I had an & ~0x3f on the high |
cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0; |
|
* 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; */ |
|
1454 |
} else { |
} else { |
1455 |
/* R4000: */ |
/* R4000: */ |
1456 |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
1547 |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
void coproc_tlbwri(struct cpu *cpu, int randomflag) |
1548 |
{ |
{ |
1549 |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
struct mips_coproc *cp = cpu->cd.mips.coproc[0]; |
1550 |
int index, g_bit, old_asid = -1; |
int index, g_bit; |
1551 |
uint64_t oldvaddr; |
uint64_t oldvaddr; |
1552 |
|
|
1553 |
if (randomflag) { |
if (randomflag) { |
1554 |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
if (cpu->cd.mips.cpu_type.exc_model == EXC3K) { |
1555 |
cp->reg[COP0_RANDOM] = |
index = ((cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
1556 |
((random() % (cp->nr_of_tlbs - 8)) + 8) |
>> R2K3K_RANDOM_SHIFT) - 1; |
1557 |
<< R2K3K_RANDOM_SHIFT; |
/* R3000 always has 8 wired entries: */ |
1558 |
index = (cp->reg[COP0_RANDOM] & R2K3K_RANDOM_MASK) |
if (index < 8) |
1559 |
>> R2K3K_RANDOM_SHIFT; |
index = cp->nr_of_tlbs - 1; |
1560 |
|
cp->reg[COP0_RANDOM] = index << R2K3K_RANDOM_SHIFT; |
1561 |
} else { |
} else { |
1562 |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
cp->reg[COP0_RANDOM] = cp->reg[COP0_WIRED] + (random() |
1563 |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
% (cp->nr_of_tlbs - cp->reg[COP0_WIRED])); |
1604 |
/* |
/* |
1605 |
* Any virtual address translation for the old TLB entry must be |
* Any virtual address translation for the old TLB entry must be |
1606 |
* invalidated first: |
* invalidated first: |
1607 |
|
* |
1608 |
|
* (Only Valid entries need to be invalidated, and only those that |
1609 |
|
* are either Global, or have the same ASID as the new entry will |
1610 |
|
* have. No other address translations should be active anyway.) |
1611 |
*/ |
*/ |
1612 |
|
|
1613 |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
switch (cpu->cd.mips.cpu_type.mmu_model) { |
1614 |
|
|
1615 |
case MMU3K: |
case MMU3K: |
1616 |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
oldvaddr = cp->tlbs[index].hi & R2K3K_ENTRYHI_VPN_MASK; |
1617 |
oldvaddr &= 0xffffffffULL; |
oldvaddr = (int32_t) oldvaddr; |
|
if (oldvaddr & 0x80000000ULL) |
|
|
oldvaddr |= 0xffffffff00000000ULL; |
|
|
old_asid = (cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) |
|
|
>> R2K3K_ENTRYHI_ASID_SHIFT; |
|
1618 |
|
|
1619 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
if (cp->tlbs[index].lo0 & R2K3K_ENTRYLO_V && |
1620 |
INVALIDATE_VADDR); |
(cp->tlbs[index].lo0 & R2K3K_ENTRYLO_G || |
1621 |
|
(cp->tlbs[index].hi & R2K3K_ENTRYHI_ASID_MASK) == |
1622 |
|
(cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) |
1623 |
|
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1624 |
|
INVALIDATE_VADDR); |
1625 |
break; |
break; |
1626 |
|
|
1627 |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1641 |
oldvaddr |= 0xffffff0000000000ULL; |
oldvaddr |= 0xffffff0000000000ULL; |
1642 |
} |
} |
1643 |
|
|
1644 |
#if 1 |
#if 0 |
1645 |
|
/* TODO: FIX THIS! It shouldn't be needed! */ |
1646 |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); |
1647 |
#else |
#else |
1648 |
/* |
/* |
1649 |
* TODO: non-4KB page sizes! |
* TODO: non-4KB page sizes! |
1650 |
*/ |
*/ |
1651 |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
if (cp->tlbs[index].lo0 & ENTRYLO_V) |
1652 |
INVALIDATE_VADDR); |
cpu->invalidate_translation_caches(cpu, oldvaddr, |
1653 |
cpu->invalidate_translation_caches(cpu, oldvaddr | 0x1000, |
INVALIDATE_VADDR); |
1654 |
INVALIDATE_VADDR); |
if (cp->tlbs[index].lo1 & ENTRYLO_V) |
1655 |
|
cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, |
1656 |
|
INVALIDATE_VADDR); |
1657 |
#endif |
#endif |
1658 |
} |
} |
1659 |
|
|
1660 |
|
#if 0 |
1661 |
/* |
/* |
1662 |
* Check for duplicate entries. (There should not be two mappings |
* Check for duplicate entries. (There should not be two mappings |
1663 |
* from one virtual address to physical addresses.) |
* from one virtual address to physical addresses.) |
1695 |
(long long)vaddr1, asid, i); |
(long long)vaddr1, asid, i); |
1696 |
} |
} |
1697 |
} |
} |
1698 |
|
#endif |
1699 |
|
|
1700 |
/* Write the new entry: */ |
/* Write the new entry: */ |
1701 |
|
|
1719 |
INVALIDATE_PADDR); |
INVALIDATE_PADDR); |
1720 |
} |
} |
1721 |
|
|
1722 |
|
if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { |
1723 |
|
fatal("Wow! Interesting case; tlbw* while caches" |
1724 |
|
" are isolated. TODO\n"); |
1725 |
|
/* Don't update the translation table in this |
1726 |
|
case... */ |
1727 |
|
exit(1); |
1728 |
|
} |
1729 |
|
|
1730 |
/* If we have a memblock (host page) for the physical |
/* If we have a memblock (host page) for the physical |
1731 |
page, then add a translation for it immediately: */ |
page, then add a translation for it immediately: */ |
1732 |
if (memblock != NULL && |
if (memblock != NULL && |
1733 |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) { |
cp->reg[COP0_ENTRYLO0] & R2K3K_ENTRYLO_V) |
|
memblock += (paddr & ((1 << BITS_PER_PAGETABLE) - 1)); |
|
1734 |
cpu->update_translation_table(cpu, vaddr, memblock, |
cpu->update_translation_table(cpu, vaddr, memblock, |
1735 |
wf, paddr); |
wf, paddr); |
|
} |
|
1736 |
} else { |
} else { |
1737 |
/* R4000: */ |
/* R4000 etc.: */ |
1738 |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
unsigned char *memblock = NULL; |
1739 |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
int pfn_shift = 12, vpn_shift = 12; |
1740 |
|
int wf0, wf1, mask; |
1741 |
|
uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; |
1742 |
|
|
1743 |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; |
1744 |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; |
1745 |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
cp->tlbs[index].lo1 = cp->reg[COP0_ENTRYLO1]; |
1746 |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
cp->tlbs[index].lo0 = cp->reg[COP0_ENTRYLO0]; |
1747 |
|
|
1748 |
|
wf0 = cp->tlbs[index].lo0 & ENTRYLO_D; |
1749 |
|
wf1 = cp->tlbs[index].lo1 & ENTRYLO_D; |
1750 |
|
|
1751 |
|
mask = cp->reg[COP0_PAGEMASK]; |
1752 |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1753 |
/* NOTE: The VR4131 (and possibly others) don't have |
pfn_shift = 10; |
1754 |
a Global bit in entryhi */ |
mask |= 0x07ff; |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
|
1755 |
} else { |
} else { |
1756 |
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
mask |= 0x1fff; |
|
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
|
|
|
|
|
cp->tlbs[index].hi &= ~TLB_G; |
|
|
if (g_bit) |
|
|
cp->tlbs[index].hi |= TLB_G; |
|
1757 |
} |
} |
1758 |
|
switch (mask) { |
1759 |
/* Invalidate any code translations, if we are writing |
case 0x00007ff: |
1760 |
Dirty pages to the TLB: */ |
if (cp->tlbs[index].lo0 & ENTRYLO_V || |
1761 |
if (cp->reg[COP0_PAGEMASK] != 0 && |
cp->tlbs[index].lo1 & ENTRYLO_V) { |
1762 |
cp->reg[COP0_PAGEMASK] != 0x1800) { |
fatal("1KB pages don't work with dyntrans.\n"); |
1763 |
printf("TODO: MASK = %08"PRIx32"\n", |
exit(1); |
1764 |
(uint32_t)cp->reg[COP0_PAGEMASK]); |
} |
1765 |
|
vpn_shift = 10; |
1766 |
|
break; |
1767 |
|
case 0x0001fff: break; |
1768 |
|
case 0x0007fff: vpn_shift = 14; break; |
1769 |
|
case 0x001ffff: vpn_shift = 16; break; |
1770 |
|
case 0x007ffff: vpn_shift = 18; break; |
1771 |
|
case 0x01fffff: vpn_shift = 20; break; |
1772 |
|
case 0x07fffff: vpn_shift = 22; break; |
1773 |
|
case 0x1ffffff: vpn_shift = 24; break; |
1774 |
|
case 0x7ffffff: vpn_shift = 26; break; |
1775 |
|
default:fatal("Unimplemented MASK = 0x%016x\n", mask); |
1776 |
exit(1); |
exit(1); |
1777 |
} |
} |
1778 |
|
|
1779 |
if (cp->tlbs[index].lo0 & ENTRYLO_D) |
paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
1780 |
cpu->invalidate_code_translation(cpu, |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
1781 |
((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) |
paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) |
1782 |
>> ENTRYLO_PFN_SHIFT) << 12, |
>> ENTRYLO_PFN_SHIFT) << pfn_shift; |
|
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); |
|
1783 |
|
|
1784 |
#if 1 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
1785 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
|
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; |
|
1786 |
/* 44 addressable bits: */ |
/* 44 addressable bits: */ |
1787 |
if (oldvaddr & 0x80000000000ULL) |
if (vaddr0 & 0x80000000000ULL) |
1788 |
oldvaddr |= 0xfffff00000000000ULL; |
vaddr0 |= 0xfffff00000000000ULL; |
1789 |
} else if (cpu->is_32bit) { |
} else if (cpu->is_32bit) { |
1790 |
/* MIPS32 etc.: */ |
/* MIPS32 etc.: */ |
1791 |
oldvaddr = (int32_t)oldvaddr; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1792 |
|
vaddr0 = (int32_t)vaddr0; |
1793 |
} else { |
} else { |
1794 |
/* Assume MMU4K */ |
/* Assume MMU4K */ |
1795 |
oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; |
1796 |
/* 40 addressable bits: */ |
/* 40 addressable bits: */ |
1797 |
if (oldvaddr & 0x8000000000ULL) |
if (vaddr0 & 0x8000000000ULL) |
1798 |
oldvaddr |= 0xffffff0000000000ULL; |
vaddr0 |= 0xffffff0000000000ULL; |
1799 |
} |
} |
1800 |
|
|
1801 |
cpu->invalidate_translation_caches(cpu, ((cp->tlbs[index].lo0 & |
vaddr1 = vaddr0 | (1 << vpn_shift); |
|
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); |
|
1802 |
|
|
1803 |
#endif |
g_bit = (cp->reg[COP0_ENTRYLO0] & |
1804 |
} |
cp->reg[COP0_ENTRYLO1]) & ENTRYLO_G; |
|
} |
|
1805 |
|
|
1806 |
|
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
1807 |
|
/* NOTE: The VR4131 (and possibly others) don't have |
1808 |
|
a Global bit in entryhi */ |
1809 |
|
cp->tlbs[index].hi &= ~cp->reg[COP0_PAGEMASK]; |
1810 |
|
} else { |
1811 |
|
cp->tlbs[index].lo0 &= ~ENTRYLO_G; |
1812 |
|
cp->tlbs[index].lo1 &= ~ENTRYLO_G; |
1813 |
|
|
1814 |
/* |
cp->tlbs[index].hi &= ~TLB_G; |
1815 |
* coproc_rfe(): |
if (g_bit) |
1816 |
* |
cp->tlbs[index].hi |= TLB_G; |
1817 |
* Return from exception. (R3000 etc.) |
} |
1818 |
*/ |
|
1819 |
void coproc_rfe(struct cpu *cpu) |
/* |
1820 |
{ |
* Invalidate any code translations, if we are writing Dirty |
1821 |
cpu->cd.mips.coproc[0]->reg[COP0_STATUS] = |
* pages to the TLB: (TODO: 4KB hardcoded... ugly) |
1822 |
(cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & ~0x3f) | |
*/ |
1823 |
((cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 0x3c) >> 2); |
for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { |
1824 |
|
if (wf0) |
1825 |
|
cpu->invalidate_code_translation(cpu, |
1826 |
|
paddr0 + ptmp, INVALIDATE_PADDR); |
1827 |
|
if (wf1) |
1828 |
|
cpu->invalidate_code_translation(cpu, |
1829 |
|
paddr1 + ptmp, INVALIDATE_PADDR); |
1830 |
|
} |
1831 |
|
|
1832 |
|
/* |
1833 |
|
* If we have a memblock (host page) for the physical page, |
1834 |
|
* then add a translation for it immediately, to save some |
1835 |
|
* time. (It would otherwise be added later on anyway, |
1836 |
|
* because of a translation miss.) |
1837 |
|
* |
1838 |
|
* NOTE/TODO: This is only for 4KB pages so far. It would |
1839 |
|
* be too expensive to add e.g. 16MB pages like |
1840 |
|
* this. |
1841 |
|
*/ |
1842 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr0, 0); |
1843 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO0] & ENTRYLO_V) |
1844 |
|
cpu->update_translation_table(cpu, vaddr0, memblock, |
1845 |
|
wf0, paddr0); |
1846 |
|
memblock = memory_paddr_to_hostaddr(cpu->mem, paddr1, 0); |
1847 |
|
if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) |
1848 |
|
cpu->update_translation_table(cpu, vaddr1, memblock, |
1849 |
|
wf1, paddr1); |
1850 |
|
} |
1851 |
} |
} |
1852 |
|
|
1853 |
|
|
2069 |
|
|
2070 |
/* TLB operations and other things: */ |
/* TLB operations and other things: */ |
2071 |
if (cp->coproc_nr == 0) { |
if (cp->coproc_nr == 0) { |
2072 |
|
if (!unassemble_only) { |
2073 |
|
fatal("FATAL INTERNAL ERROR: Should be implemented" |
2074 |
|
" with dyntrans instead.\n"); |
2075 |
|
exit(1); |
2076 |
|
} |
2077 |
|
|
2078 |
op = (function) & 0xff; |
op = (function) & 0xff; |
2079 |
switch (co_bit) { |
switch (co_bit) { |
2080 |
case 1: |
case 1: |
2081 |
switch (op) { |
switch (op) { |
2082 |
case COP0_TLBR: /* Read indexed TLB entry */ |
case COP0_TLBR: /* Read indexed TLB entry */ |
2083 |
if (unassemble_only) { |
debug("tlbr\n"); |
|
debug("tlbr\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 1); |
|
2084 |
return; |
return; |
2085 |
case COP0_TLBWI: /* Write indexed */ |
case COP0_TLBWI: /* Write indexed */ |
2086 |
case COP0_TLBWR: /* Write random */ |
case COP0_TLBWR: /* Write random */ |
2087 |
if (unassemble_only) { |
if (op == COP0_TLBWI) |
2088 |
if (op == COP0_TLBWI) |
debug("tlbwi"); |
2089 |
debug("tlbwi"); |
else |
2090 |
else |
debug("tlbwr"); |
2091 |
debug("tlbwr"); |
if (!running) { |
2092 |
if (!running) { |
debug("\n"); |
|
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]); |
|
2093 |
return; |
return; |
2094 |
} |
} |
2095 |
coproc_tlbwri(cpu, op == COP0_TLBWR); |
debug("\tindex=%08llx", |
2096 |
|
(long long)cp->reg[COP0_INDEX]); |
2097 |
|
debug(", random=%08llx", |
2098 |
|
(long long)cp->reg[COP0_RANDOM]); |
2099 |
|
debug(", mask=%016llx", |
2100 |
|
(long long)cp->reg[COP0_PAGEMASK]); |
2101 |
|
debug(", hi=%016llx", |
2102 |
|
(long long)cp->reg[COP0_ENTRYHI]); |
2103 |
|
debug(", lo0=%016llx", |
2104 |
|
(long long)cp->reg[COP0_ENTRYLO0]); |
2105 |
|
debug(", lo1=%016llx\n", |
2106 |
|
(long long)cp->reg[COP0_ENTRYLO1]); |
2107 |
return; |
return; |
2108 |
case COP0_TLBP: /* Probe TLB for |
case COP0_TLBP: /* Probe TLB for |
2109 |
matching entry */ |
matching entry */ |
2110 |
if (unassemble_only) { |
debug("tlbp\n"); |
|
debug("tlbp\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 0); |
|
2111 |
return; |
return; |
2112 |
case COP0_RFE: /* R2000/R3000 only: |
case COP0_RFE: /* R2000/R3000 only: |
2113 |
Return from Exception */ |
Return from Exception */ |
2114 |
if (unassemble_only) { |
debug("rfe\n"); |
|
debug("rfe\n"); |
|
|
return; |
|
|
} |
|
|
coproc_rfe(cpu); |
|
2115 |
return; |
return; |
2116 |
case COP0_ERET: /* R4000: Return from exception */ |
case COP0_ERET: /* R4000: Return from exception */ |
2117 |
if (unassemble_only) { |
debug("eret\n"); |
|
debug("eret\n"); |
|
|
return; |
|
|
} |
|
|
coproc_eret(cpu); |
|
2118 |
return; |
return; |
2119 |
case COP0_DERET: |
case COP0_DERET: |
2120 |
if (unassemble_only) { |
debug("deret\n"); |
2121 |
debug("deret\n"); |
return; |
2122 |
return; |
case COP0_WAIT: |
2123 |
|
{ |
2124 |
|
int code = (function >> 6) & 0x7ffff; |
2125 |
|
debug("wait"); |
2126 |
|
if (code > 0) |
2127 |
|
debug("\t0x%x", code); |
2128 |
|
debug("\n"); |
2129 |
} |
} |
|
/* |
|
|
* 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; |
|
2130 |
return; |
return; |
2131 |
case COP0_STANDBY: |
case COP0_STANDBY: |
2132 |
if (unassemble_only) { |
debug("standby\n"); |
|
debug("standby\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2133 |
return; |
return; |
2134 |
case COP0_SUSPEND: |
case COP0_SUSPEND: |
2135 |
if (unassemble_only) { |
debug("suspend\n"); |
|
debug("suspend\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2136 |
return; |
return; |
2137 |
case COP0_HIBERNATE: |
case COP0_HIBERNATE: |
2138 |
if (unassemble_only) { |
debug("hibernate\n"); |
|
debug("hibernate\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2139 |
return; |
return; |
2140 |
default: |
default: |
2141 |
; |
; |
2155 |
return; |
return; |
2156 |
} |
} |
2157 |
|
|
|
/* 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; |
|
|
} |
|
|
|
|
2158 |
if (unassemble_only) { |
if (unassemble_only) { |
2159 |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
2160 |
return; |
return; |