--- hatari/src/cpu/cpummu.c 2019/04/09 08:49:24 1.1 +++ hatari/src/cpu/cpummu.c 2019/04/09 08:57:57 1.1.1.7 @@ -1,1075 +1,1766 @@ -/* - * cpummu.cpp - MMU emulation - * - * Copyright (c) 2001-2004 Milan Jurik of ARAnyM dev team (see AUTHORS) - * - * Inspired by UAE MMU patch - * - * This file is part of the ARAnyM project which builds a new and powerful - * TOS/FreeMiNT compatible virtual machine running on almost any hardware. - * - * ARAnyM is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * ARAnyM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ARAnyM; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define DEBUG 0 -#define USETAG 0 - -#include "sysconfig.h" -#include "sysdeps.h" - -#include "options_cpu.h" -#include "memory.h" -#include "newcpu.h" -//#include "debug.h" -#include "main.h" -#include "cpummu.h" - -#define DBG_MMU_VERBOSE 1 -#define DBG_MMU_SANITY 1 -#define write_log printf - -#ifdef FULLMMU - -mmu_atc_l1_array atc_l1[2]; -mmu_atc_l1_array *current_atc; -struct mmu_atc_line atc_l2[2][ATC_L2_SIZE]; - -# ifdef ATC_STATS -static unsigned int mmu_atc_hits[ATC_L2_SIZE]; -# endif - - -static void mmu_dump_ttr(const TCHAR * label, uae_u32 ttr) -{ - DUNUSED(label); - uae_u32 from_addr, to_addr; - - from_addr = ttr & MMU_TTR_LOGICAL_BASE; - to_addr = (ttr & MMU_TTR_LOGICAL_MASK) << 8; - - - fprintf(stderr, "%s: [%08lx] %08lx - %08lx enabled=%d supervisor=%d wp=%d cm=%02d\n", - label, ttr, - from_addr, to_addr, - ttr & MMU_TTR_BIT_ENABLED ? 1 : 0, - (ttr & (MMU_TTR_BIT_SFIELD_ENABLED | MMU_TTR_BIT_SFIELD_SUPER)) >> MMU_TTR_SFIELD_SHIFT, - ttr & MMU_TTR_BIT_WRITE_PROTECT ? 1 : 0, - (ttr & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT - ); -} - -void mmu_make_transparent_region(uaecptr baseaddr, uae_u32 size, int datamode) -{ - uae_u32 * ttr; - uae_u32 * ttr0 = datamode ? ®s.dtt0 : ®s.itt0; - uae_u32 * ttr1 = datamode ? ®s.dtt1 : ®s.itt1; - - if ((*ttr1 & MMU_TTR_BIT_ENABLED) == 0) - ttr = ttr1; - else if ((*ttr0 & MMU_TTR_BIT_ENABLED) == 0) - ttr = ttr0; - else - return; - - *ttr = baseaddr & MMU_TTR_LOGICAL_BASE; - *ttr |= ((baseaddr + size - 1) & MMU_TTR_LOGICAL_BASE) >> 8; - *ttr |= MMU_TTR_BIT_ENABLED; - - fprintf(stderr, "MMU: map transparent mapping of %08x\n", *ttr); -} - -/* check if an address matches a ttr */ -static int mmu_do_match_ttr(uae_u32 ttr, uaecptr addr, bool super) -{ - if (ttr & MMU_TTR_BIT_ENABLED) { /* TTR enabled */ - uae_u8 msb, mask; - - msb = ((addr ^ ttr) & MMU_TTR_LOGICAL_BASE) >> 24; - mask = (ttr & MMU_TTR_LOGICAL_MASK) >> 16; - - if (!(msb & ~mask)) { - - if ((ttr & MMU_TTR_BIT_SFIELD_ENABLED) == 0) { - if (((ttr & MMU_TTR_BIT_SFIELD_SUPER) == 0) != (super == 0)) { - return TTR_NO_MATCH; - } - } - - return (ttr & MMU_TTR_BIT_WRITE_PROTECT) ? TTR_NO_WRITE : TTR_OK_MATCH; - } - } - return TTR_NO_MATCH; -} - -static inline int mmu_match_ttr(uaecptr addr, bool super, bool data) -{ - int res; - - if (data) { - res = mmu_do_match_ttr(regs.dtt0, addr, super); - if (res == TTR_NO_MATCH) - res = mmu_do_match_ttr(regs.dtt1, addr, super); - } else { - res = mmu_do_match_ttr(regs.itt0, addr, super); - if (res == TTR_NO_MATCH) - res = mmu_do_match_ttr(regs.itt1, addr, super); - } - return res; -} - -#if DEBUG -/* {{{ mmu_dump_table */ -static void mmu_dump_table(const char * label, uaecptr root_ptr) -{ - DUNUSED(label); - const int ROOT_TABLE_SIZE = 128, - PTR_TABLE_SIZE = 128, - PAGE_TABLE_SIZE = 64, - ROOT_INDEX_SHIFT = 25, - PTR_INDEX_SHIFT = 18; - // const int PAGE_INDEX_SHIFT = 12; - int root_idx, ptr_idx, page_idx; - uae_u32 root_des, ptr_des, page_des; - uaecptr ptr_des_addr, page_addr, - root_log, ptr_log, page_log; - - fprintf(stderr, "%s: root=%lx\n", label, root_ptr); - - for (root_idx = 0; root_idx < ROOT_TABLE_SIZE; root_idx++) { - root_des = phys_get_long(root_ptr + root_idx); - - if ((root_des & 2) == 0) - continue; /* invalid */ - - fprintf(stderr, "ROOT: %03d U=%d W=%d UDT=%02d\n", root_idx, - root_des & 8 ? 1 : 0, - root_des & 4 ? 1 : 0, - root_des & 3 - ); - - root_log = root_idx << ROOT_INDEX_SHIFT; - - ptr_des_addr = root_des & MMU_ROOT_PTR_ADDR_MASK; - - for (ptr_idx = 0; ptr_idx < PTR_TABLE_SIZE; ptr_idx++) { - struct { - uaecptr log, phys; - int start_idx, n_pages; /* number of pages covered by this entry */ - uae_u32 match; - } page_info[PAGE_TABLE_SIZE]; - int n_pages_used; - - ptr_des = phys_get_long(ptr_des_addr + ptr_idx); - ptr_log = root_log | (ptr_idx << PTR_INDEX_SHIFT); - - if ((ptr_des & 2) == 0) - continue; /* invalid */ - - page_addr = ptr_des & (regs.mmu_pagesize_8k ? MMU_PTR_PAGE_ADDR_MASK_8 : MMU_PTR_PAGE_ADDR_MASK_4); - - n_pages_used = -1; - for (page_idx = 0; page_idx < PAGE_TABLE_SIZE; page_idx++) { - - page_des = phys_get_long(page_addr + page_idx); - page_log = ptr_log | (page_idx << 2); // ??? PAGE_INDEX_SHIFT - - switch (page_des & 3) { - case 0: /* invalid */ - continue; - case 1: case 3: /* resident */ - case 2: /* indirect */ - if (n_pages_used == -1 || page_info[n_pages_used].match != page_des) { - /* use the next entry */ - n_pages_used++; - - page_info[n_pages_used].match = page_des; - page_info[n_pages_used].n_pages = 1; - page_info[n_pages_used].start_idx = page_idx; - page_info[n_pages_used].log = page_log; - } else { - page_info[n_pages_used].n_pages++; - } - break; - } - } - - if (n_pages_used == -1) - continue; - - fprintf(stderr, " PTR: %03d U=%d W=%d UDT=%02d\n", ptr_idx, - ptr_des & 8 ? 1 : 0, - ptr_des & 4 ? 1 : 0, - ptr_des & 3 - ); - - - for (page_idx = 0; page_idx <= n_pages_used; page_idx++) { - page_des = page_info[page_idx].match; - - if ((page_des & MMU_PDT_MASK) == 2) { - fprintf(stderr, " PAGE: %03d-%03d log=%08lx INDIRECT --> addr=%08lx\n", - page_info[page_idx].start_idx, - page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1, - page_info[page_idx].log, - page_des & MMU_PAGE_INDIRECT_MASK - ); - - } else { - fprintf(stderr, " PAGE: %03d-%03d log=%08lx addr=%08lx UR=%02d G=%d U1/0=%d S=%d CM=%d M=%d U=%d W=%d\n", - page_info[page_idx].start_idx, - page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1, - page_info[page_idx].log, - page_des & (regs.mmu_pagesize_8k ? MMU_PAGE_ADDR_MASK_8 : MMU_PAGE_ADDR_MASK_4), - (page_des & (regs.mmu_pagesize_8k ? MMU_PAGE_UR_MASK_8 : MMU_PAGE_UR_MASK_4)) >> MMU_PAGE_UR_SHIFT, - page_des & MMU_DES_GLOBAL ? 1 : 0, - (page_des & MMU_TTR_UX_MASK) >> MMU_TTR_UX_SHIFT, - page_des & MMU_DES_SUPER ? 1 : 0, - (page_des & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT, - page_des & MMU_DES_MODIFIED ? 1 : 0, - page_des & MMU_DES_USED ? 1 : 0, - page_des & MMU_DES_WP ? 1 : 0 - ); - } - } - } - - } -} -/* }}} */ -#endif - -/* {{{ mmu_dump_atc */ -void mmu_dump_atc(void) -{ - int i, j; - for (i = 0; i < 2; i++) { - for (j = 0; j < ATC_L2_SIZE; j++) { - if (atc_l2[i][j].tag == 0x8000) - continue; - fprintf(stderr, "ATC[%02d] G=%d TT=%d M=%d WP=%d VD=%d VI=%d tag=%08x --> phys=%08x\n", - j, atc_l2[i][j].global, atc_l2[i][j].tt, atc_l2[i][j].modified, - atc_l2[i][j].write_protect, atc_l2[i][j].valid_data, atc_l2[i][j].valid_inst, - atc_l2[i][j].tag, atc_l2[i][j].phys); - } - } -} -/* }}} */ - -/* {{{ mmu_dump_tables */ -void mmu_dump_tables(void) -{ - fprintf(stderr, "URP: %08x SRP: %08x MMUSR: %x TC: %x\n", regs.urp, regs.srp, regs.mmusr, regs.tcr); - mmu_dump_ttr(L"DTT0", regs.dtt0); - mmu_dump_ttr(L"DTT1", regs.dtt1); - mmu_dump_ttr(L"ITT0", regs.itt0); - mmu_dump_ttr(L"ITT1", regs.itt1); - mmu_dump_atc(); -#if DEBUG - mmu_dump_table("SRP", regs.srp); -#endif -} -/* }}} */ - -static uaecptr REGPARAM2 mmu_lookup_pagetable(uaecptr addr, bool super, bool write); - -static ALWAYS_INLINE int mmu_get_fc(bool super, bool data) -{ - return (super ? 4 : 0) | (data ? 1 : 2); -} - -static void mmu_bus_error(uaecptr addr, int fc, bool write, int size) -{ - uae_u16 ssw = 0; - - ssw |= fc & MMU_SSW_TM; /* Copy TM */ - switch (size) { - case sz_byte: - ssw |= MMU_SSW_SIZE_B; - break; - case sz_word: - ssw |= MMU_SSW_SIZE_W; - break; - case sz_long: - ssw |= MMU_SSW_SIZE_L; - break; - } - - regs.wb3_status = write ? 0x80 | ssw : 0; - if (!write) - ssw |= MMU_SSW_RW; - - regs.mmu_fault_addr = addr; - regs.mmu_ssw = ssw | MMU_SSW_ATC; - - fprintf(stderr, "BUS ERROR: fc=%d w=%d log=%08x ssw=%04x PC=%08x\n", fc, write, addr, ssw, m68k_getpc()); - - //write_log(L"BUS ERROR: fc=%d w=%d log=%08x ssw=%04x PC=%08x\n", fc, write, addr, ssw, m68k_getpc()); - - except=2; -} - -/* - * Update the atc line for a given address by doing a mmu lookup. - */ -static uaecptr mmu_fill_atc_l2(uaecptr addr, bool super, bool data, bool write, struct mmu_atc_line *l) -{ - int res; - uae_u32 desc; - - l->tag = ATC_TAG(addr); - l->hw = l->bus_fault = 0; - - /* check ttr0 */ - res = mmu_match_ttr(addr, super, data); - if (res != TTR_NO_MATCH) { - l->tt = 1; - if (data) { - l->valid_data = 1; - l->valid_inst = mmu_match_ttr(addr, super, 0) == res; - } else { - l->valid_inst = 1; - l->valid_data = mmu_match_ttr(addr, super, 1) == res; - } - l->global = 1; - l->modified = 1; - l->write_protect = (res == TTR_NO_WRITE); - l->phys = 0; - - return 0; - } - - l->tt = 0; - if (!regs.mmu_enabled) { - l->valid_data = l->valid_inst = 1; - l->global = 1; - l->modified = 1; - l->write_protect = 0; - l->phys = 0; - return 0; - } - - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - desc = mmu_lookup_pagetable(addr, super, write); -#if DEBUG > 2 - fprintf(stderr, "translate: %x,%u,%u,%u -> %x\n", addr, super, write, data, desc); -#endif - RESTORE_EXCEPTION; - CATCH(prb) { - RESTORE_EXCEPTION; - /* bus error during table search */ - desc = 0; - goto fail; - } - - if ((desc & 1) == 0 || (!super && desc & MMU_MMUSR_S)) { - fail: - l->valid_data = l->valid_inst = 0; - l->global = 0; - } else { - l->valid_data = l->valid_inst = 1; - if (regs.mmu_pagesize_8k) - l->phys = (desc & ~0x1fff) - (addr & ~0x1fff); - else - l->phys = (desc & ~0xfff) - (addr & ~0xfff); - l->global = (desc & MMU_MMUSR_G) != 0; - l->modified = (desc & MMU_MMUSR_M) != 0; - l->write_protect = (desc & MMU_MMUSR_W) != 0; - } - - return desc; -} - -static ALWAYS_INLINE bool mmu_fill_atc_l1(uaecptr addr, bool super, bool data, bool write, struct mmu_atc_line *l1) -{ - int idx = ATC_L2_INDEX(addr); - int tag = ATC_TAG(addr); - struct mmu_atc_line *l = &atc_l2[super ? 1 : 0][idx]; - - if (l->tag != tag) { - restart: - mmu_fill_atc_l2(addr, super, data, write, l); - } - if (!(data ? l->valid_data : l->valid_inst)) { - fprintf(stderr, "MMU: non-resident page (%x,%x,%x)!\n", addr, regs.pc, regs.fault_pc); - goto fail; - } - if (write) { - if (l->write_protect) { - fprintf(stderr, "MMU: write protected (via %s) %lx\n", l->tt ? "ttr" : "atc", addr); - goto fail; - } - if (!l->modified) - goto restart; - } - *l1 = *l; -#if 0 - uaecptr phys_addr = addr + l1->phys; - if ((phys_addr & 0xfff00000) == 0x00f00000) { - l1->hw = 1; - goto fail; - } - if ((phys_addr & 0xfff00000) == 0xfff00000) { - l1->hw = 1; - l1->phys -= 0xff000000; - goto fail; - } - - if (!test_ram_boundary(phys_addr, 1, super, write)) { - l1->bus_fault = 1; - goto fail; - } -#endif - return true; - -fail: - l1->tag = ~l1->tag; - return false; -} - -uaecptr REGPARAM2 mmu_translate(uaecptr addr, bool super, bool data, bool write) -{ - struct mmu_atc_line *l; - - l = &atc_l2[super ? 1 : 0][ATC_L2_INDEX(addr)]; - mmu_fill_atc_l2(addr, super, data, write, l); - if (!(data ? l->valid_data : l->valid_inst)) - except=2; - - return addr + l->phys; -} - -/* - * Lookup the address by walking the page table and updating - * the page descriptors accordingly. Returns the found descriptor - * or produces a bus error. - */ -static uaecptr REGPARAM2 mmu_lookup_pagetable(uaecptr addr, bool super, bool write) -{ - uae_u32 desc, desc_addr, wp; - int i; - - wp = 0; - desc = super ? regs.srp : regs.urp; - - /* fetch root table descriptor */ - i = (addr >> 23) & 0x1fc; - desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i; - desc = phys_get_long(desc_addr); - if ((desc & 2) == 0) { - fprintf(stderr, "MMU: invalid root descriptor for %lx\n", addr); - return 0; - } - - wp |= desc; - if ((desc & MMU_DES_USED) == 0) - phys_put_long(desc_addr, desc | MMU_DES_USED); - - /* fetch pointer table descriptor */ - i = (addr >> 16) & 0x1fc; - desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i; - desc = phys_get_long(desc_addr); - if ((desc & 2) == 0) { - fprintf(stderr, "MMU: invalid ptr descriptor for %lx\n", addr); - return 0; - } - wp |= desc; - if ((desc & MMU_DES_USED) == 0) - phys_put_long(desc_addr, desc | MMU_DES_USED); - - /* fetch page table descriptor */ - if (regs.mmu_pagesize_8k) { - i = (addr >> 11) & 0x7c; - desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_8) | i; - } else { - i = (addr >> 10) & 0xfc; - desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_4) | i; - } - - desc = phys_get_long(desc_addr); - if ((desc & 3) == 2) { - /* indirect */ - desc_addr = desc & MMU_PAGE_INDIRECT_MASK; - desc = phys_get_long(desc_addr); - } - if ((desc & 1) == 0) { - fprintf(stderr, "MMU: invalid page descriptor log=%08lx desc=%08lx @%08lx\n", addr, desc, desc_addr); - return desc; - } - - desc |= wp & MMU_DES_WP; - if (write) { - if (desc & MMU_DES_WP) { - if ((desc & MMU_DES_USED) == 0) { - desc |= MMU_DES_USED; - phys_put_long(desc_addr, desc); - } - } else if ((desc & (MMU_DES_USED|MMU_DES_MODIFIED)) != - (MMU_DES_USED|MMU_DES_MODIFIED)) { - desc |= MMU_DES_USED|MMU_DES_MODIFIED; - phys_put_long(desc_addr, desc); - } - } else { - if ((desc & MMU_DES_USED) == 0) { - desc |= MMU_DES_USED; - phys_put_long(desc_addr, desc); - } - } - return desc; -} - -uae_u16 REGPARAM2 mmu_get_word_unaligned(uaecptr addr, bool data) -{ - uae_u16 res; - - res = (uae_u16)mmu_get_byte(addr, data, sz_word) << 8; - SAVE_EXCEPTION; - -// TRY(prb) { - except = 0; - res |= mmu_get_byte(addr + 1, data, sz_word); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - return res; -} - -uae_u32 REGPARAM2 mmu_get_long_unaligned(uaecptr addr, bool data) -{ - uae_u32 res; - - if (likely(!(addr & 1))) { - res = (uae_u32)mmu_get_word(addr, data, sz_long) << 16; - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - res |= mmu_get_word(addr + 2, data, sz_long); - RESTORE_EXCEPTION; - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - } else { - res = (uae_u32)mmu_get_byte(addr, data, sz_long) << 8; - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - res = (res | mmu_get_byte(addr + 1, data, sz_long)) << 8; - res = (res | mmu_get_byte(addr + 2, data, sz_long)) << 8; - res |= mmu_get_byte(addr + 3, data, sz_long); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - } - return res; -} - -uae_u8 REGPARAM2 mmu_get_byte_slow(uaecptr addr, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) - return HWget_b(cl->phys + addr); - mmu_bus_error(addr, mmu_get_fc(super, data), 0, size); - return 0; - } - - if (!mmu_fill_atc_l1(addr, super, data, 0, cl)) - goto redo; - - return phys_get_byte(mmu_get_real_address(addr, cl)); -} - -uae_u16 REGPARAM2 mmu_get_word_slow(uaecptr addr, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) - return HWget_w(cl->phys + addr); - mmu_bus_error(addr, mmu_get_fc(super, data), 0, size); - return 0; - } - - if (!mmu_fill_atc_l1(addr, super, data, 0, cl)) - goto redo; - - return phys_get_word(mmu_get_real_address(addr, cl)); -} - -uae_u32 REGPARAM2 mmu_get_long_slow(uaecptr addr, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) - return HWget_l(cl->phys + addr); - mmu_bus_error(addr, mmu_get_fc(super, data), 0, size); - return 0; - } - - if (!mmu_fill_atc_l1(addr, super, data, 0, cl)) - goto redo; - - return phys_get_long(mmu_get_real_address(addr, cl)); -} - -void REGPARAM2 mmu_put_long_unaligned(uaecptr addr, uae_u32 val, bool data) -{ - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - if (likely(!(addr & 1))) { - mmu_put_word(addr, val >> 16, data, sz_long); - mmu_put_word(addr + 2, val, data, sz_long); - } else { - mmu_put_byte(addr, val >> 24, data, sz_long); - mmu_put_byte(addr + 1, val >> 16, data, sz_long); - mmu_put_byte(addr + 2, val >> 8, data, sz_long); - mmu_put_byte(addr + 3, val, data, sz_long); - } - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } - except=2; - } -} - -void REGPARAM2 mmu_put_word_unaligned(uaecptr addr, uae_u16 val, bool data) -{ - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - mmu_put_byte(addr, val >> 8, data, sz_word); - mmu_put_byte(addr + 1, val, data, sz_word); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } - except=2; - } -} - -void REGPARAM2 mmu_put_byte_slow(uaecptr addr, uae_u8 val, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) { - HWput_b(cl->phys + addr, val); - return; - } - regs.wb3_data = val; - mmu_bus_error(addr, mmu_get_fc(super, data), 1, size); - return; - } - - if (!mmu_fill_atc_l1(addr, super, data, 1, cl)) - goto redo; - - phys_put_byte(mmu_get_real_address(addr, cl), val); -} - -void REGPARAM2 mmu_put_word_slow(uaecptr addr, uae_u16 val, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) { - HWput_w(cl->phys + addr, val); - return; - } - regs.wb3_data = val; - mmu_bus_error(addr, mmu_get_fc(super, data), 1, size); - return; - } - - if (!mmu_fill_atc_l1(addr, super, data, 1, cl)) - goto redo; - - phys_put_word(mmu_get_real_address(addr, cl), val); -} - -void REGPARAM2 mmu_put_long_slow(uaecptr addr, uae_u32 val, bool super, bool data, - int size, struct mmu_atc_line *cl) -{ - uae_u32 tag = ATC_TAG(addr); - - if (USETAG && cl->tag == (uae_u16)~tag) { - redo: - if (cl->hw) { - HWput_l(cl->phys + addr, val); - return; - } - regs.wb3_data = val; - mmu_bus_error(addr, mmu_get_fc(super, data), 1, size); - return; - } - - if (!mmu_fill_atc_l1(addr, super, data, 1, cl)) - goto redo; - - phys_put_long(mmu_get_real_address(addr, cl), val); -} - -uae_u32 REGPARAM2 sfc_get_long(uaecptr addr) -{ - bool super = (regs.sfc & 4) != 0; - bool data = (regs.sfc & 3) != 2; - uae_u32 res; - - if (likely(!is_unaligned(addr, 4))) - return mmu_get_user_long(addr, super, data, sz_long); - - if (likely(!(addr & 1))) { - res = (uae_u32)mmu_get_user_word(addr, super, data, sz_long) << 16; - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - res |= mmu_get_user_word(addr + 2, super, data, sz_long); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - } else { - res = (uae_u32)mmu_get_user_byte(addr, super, data, sz_long) << 8; - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - res = (res | mmu_get_user_byte(addr + 1, super, data, sz_long)) << 8; - res = (res | mmu_get_user_byte(addr + 2, super, data, sz_long)) << 8; - res |= mmu_get_user_byte(addr + 3, super, data, sz_long); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - } - return res; -} - -uae_u16 REGPARAM2 sfc_get_word(uaecptr addr) -{ - bool super = (regs.sfc & 4) != 0; - bool data = (regs.sfc & 3) != 2; - uae_u16 res; - - if (likely(!is_unaligned(addr, 2))) - return mmu_get_user_word(addr, super, data, sz_word); - - res = (uae_u16)mmu_get_user_byte(addr, super, data, sz_word) << 8; - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - res |= mmu_get_user_byte(addr + 1, super, data, sz_word); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - except=2; - } - return res; -} - -uae_u8 REGPARAM2 sfc_get_byte(uaecptr addr) -{ - bool super = (regs.sfc & 4) != 0; - bool data = (regs.sfc & 3) != 2; - - return mmu_get_user_byte(addr, super, data, sz_byte); -} - -void REGPARAM2 dfc_put_long(uaecptr addr, uae_u32 val) -{ - bool super = (regs.dfc & 4) != 0; - bool data = (regs.dfc & 3) != 2; - - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - if (likely(!is_unaligned(addr, 4))) - mmu_put_user_long(addr, val, super, data, sz_long); - else if (likely(!(addr & 1))) { - mmu_put_user_word(addr, val >> 16, super, data, sz_long); - mmu_put_user_word(addr + 2, val, super, data, sz_long); - } else { - mmu_put_user_byte(addr, val >> 24, super, data, sz_long); - mmu_put_user_byte(addr + 1, val >> 16, super, data, sz_long); - mmu_put_user_byte(addr + 2, val >> 8, super, data, sz_long); - mmu_put_user_byte(addr + 3, val, super, data, sz_long); - } - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } - except=2; - } -} - -void REGPARAM2 dfc_put_word(uaecptr addr, uae_u16 val) -{ - bool super = (regs.dfc & 4) != 0; - bool data = (regs.dfc & 3) != 2; - - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - if (likely(!is_unaligned(addr, 2))) - mmu_put_user_word(addr, val, super, data, sz_word); - else { - mmu_put_user_byte(addr, val >> 8, super, data, sz_word); - mmu_put_user_byte(addr + 1, val, super, data, sz_word); - } - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { - regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } - except=2; - } -} - -void REGPARAM2 dfc_put_byte(uaecptr addr, uae_u8 val) -{ - bool super = (regs.dfc & 4) != 0; - bool data = (regs.dfc & 3) != 2; - - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - mmu_put_user_byte(addr, val, super, data, sz_byte); - RESTORE_EXCEPTION; - - CATCH(prb) { - RESTORE_EXCEPTION; - regs.wb3_data = val; - except=2; - } -} - -void REGPARAM2 mmu_op_real(uae_u32 opcode, uae_u16 extra) -{ - bool super = (regs.dfc & 4) != 0; - DUNUSED(extra); - if ((opcode & 0xFE0) == 0x0500) { - bool glob; - int regno; - //D(didflush = 0); - uae_u32 addr; - /* PFLUSH */ - regno = opcode & 7; - glob = (opcode & 8) != 0; - - if (opcode & 16) { - fprintf(stderr, "pflusha(%u,%u)\n", glob, regs.dfc); - mmu_flush_atc_all(glob); - } else { - addr = m68k_areg(regs, regno); - fprintf(stderr, "pflush(%u,%u,%x)\n", glob, regs.dfc, addr); - mmu_flush_atc(addr, super, glob); - } - flush_internals(); -#ifdef USE_JIT - flush_icache(0); -#endif - } else if ((opcode & 0x0FD8) == 0x548) { - bool write; - int regno; - uae_u32 addr; - - regno = opcode & 7; - write = (opcode & 32) == 0; - addr = m68k_areg(regs, regno); - fprintf(stderr, "PTEST%c (A%d) %08x DFC=%d\n", write ? 'W' : 'R', regno, addr, regs.dfc); - mmu_flush_atc(addr, super, true); - SAVE_EXCEPTION; -// TRY(prb) { - except = 0; - struct mmu_atc_line *l; - uae_u32 desc; - bool data = (regs.dfc & 3) != 2; - - l = &atc_l2[super ? 1 : 0][ATC_L2_INDEX(addr)]; - desc = mmu_fill_atc_l2(addr, super, data, write, l); - if (!(data ? l->valid_data : l->valid_inst)) - regs.mmusr = MMU_MMUSR_B; - else if (l->tt) - regs.mmusr = MMU_MMUSR_T | MMU_MMUSR_R; - else { - regs.mmusr = desc & (~0xfff|MMU_MMUSR_G|MMU_MMUSR_Ux|MMU_MMUSR_S| - MMU_MMUSR_CM|MMU_MMUSR_M|MMU_MMUSR_W); - regs.mmusr |= MMU_MMUSR_R; - } - - CATCH(prb) { - regs.mmusr = MMU_MMUSR_B; - } - RESTORE_EXCEPTION; - fprintf(stderr, "PTEST result: mmusr %08x\n", regs.mmusr); - } else - op_illg (opcode); -} - -void REGPARAM2 mmu_flush_atc(uaecptr addr, bool super, bool global) -{ - struct mmu_atc_line *l; - int i, j; - - l = atc_l1[super ? 1 : 0][0][0]; - i = ATC_L1_INDEX(addr); - for (j = 0; j < 4; j++) { - if (global || !l[i].global) - l[i].tag = 0x8000; - l += ATC_L1_SIZE; - } - if (regs.mmu_pagesize_8k) { - i = ATC_L1_INDEX(addr) ^ 1; - for (j = 0; j < 4; j++) { - if (global || !l[i].global) - l[i].tag = 0x8000; - l += ATC_L1_SIZE; - } - } - l = atc_l2[super ? 1 : 0]; - i = ATC_L2_INDEX(addr); - if (global || !l[i].global) - l[i].tag = 0x8000; - if (regs.mmu_pagesize_8k) { - i ^= 1; - if (global || !l[i].global) - l[i].tag = 0x8000; - } -} - -void REGPARAM2 mmu_flush_atc_all(bool global) -{ - struct mmu_atc_line *l; - unsigned int i; - - l = atc_l1[0][0][0]; - for (i = 0; i < sizeof(atc_l1) / sizeof(*l); l++, i++) { - if (global || !l->global) - l->tag = 0x8000; - } - - l = atc_l2[0]; - for (i = 0; i < sizeof(atc_l2) / sizeof(*l); l++, i++) { - if (global || !l->global) - l->tag = 0x8000; - } -} - -void REGPARAM2 mmu_reset(void) -{ - mmu_flush_atc_all(true); -#if 0 - regs.urp = regs.srp = 0; - regs.itt0 = regs.itt0 = 0; - regs.dtt0 = regs.dtt0 = 0; - regs.mmusr = 0; -#endif -} - - -void REGPARAM2 mmu_set_tc(uae_u16 tc) -{ -#if 0 - if (regs.tcr == tc) - return; - regs.tcr = tc; -#endif - regs.mmu_enabled = tc & 0x8000 ? 1 : 0; - regs.mmu_pagesize_8k = tc & 0x4000 ? 1 : 0; - mmu_flush_atc_all(true); - - write_log("MMU: enabled=%d page8k=%d\n", regs.mmu_enabled, regs.mmu_pagesize_8k); -} - -void REGPARAM2 mmu_set_super(bool super) -{ - current_atc = &atc_l1[super ? 1 : 0]; -} - -#else - -void mmu_op(uae_u32 opcode, uae_u16 /*extra*/) -{ - if ((opcode & 0xFE0) == 0x0500) { - /* PFLUSH instruction */ - flush_internals(); - } else if ((opcode & 0x0FD8) == 0x548) { - /* PTEST instruction */ - } else - op_illg(opcode); -} - -#endif - -/* -vim:ts=4:sw=4: -*/ +/* + * cpummu.cpp - MMU emulation + * + * Copyright (c) 2001-2004 Milan Jurik of ARAnyM dev team (see AUTHORS) + * + * Inspired by UAE MMU patch + * + * This file is part of the ARAnyM project which builds a new and powerful + * TOS/FreeMiNT compatible virtual machine running on almost any hardware. + * + * ARAnyM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * ARAnyM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ARAnyM; if not, write to the Free Software Foundation, + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + */ + + +#include "sysconfig.h" +#include "sysdeps.h" + +#include "main.h" +#include "hatari-glue.h" + + +#include "options_cpu.h" +#include "memory.h" +#include "newcpu.h" +#include "cpummu.h" +#include "debug.h" + +#define MMUDUMP 1 + +#define DBG_MMU_VERBOSE 1 +#define DBG_MMU_SANITY 1 +#if 0 +#define write_log printf +#endif + +#ifdef FULLMMU + +uae_u32 mmu_is_super; +uae_u32 mmu_tagmask, mmu_pagemask, mmu_pagemaski; +struct mmu_atc_line mmu_atc_array[ATC_TYPE][ATC_SLOTS][ATC_WAYS]; +bool mmu_pagesize_8k; +int mmu_pageshift, mmu_pageshift1m; +uae_u8 mmu_cache_state; +uae_u8 cache_default_ins, cache_default_data; + +int mmu060_state; +uae_u16 mmu_opcode; +bool mmu_restart; +static bool locked_rmw_cycle; +bool rmw_cycle; +static bool ismoves; +bool mmu_ttr_enabled, mmu_ttr_enabled_ins, mmu_ttr_enabled_data; +int mmu_atc_ways[2]; +int way_random; + +int mmu040_movem; +uaecptr mmu040_movem_ea; +uae_u32 mmu040_move16[4]; + +#if MMU_ICACHE +struct mmu_icache mmu_icache_data[MMU_ICACHE_SZ]; +#endif +#if MMU_IPAGECACHE +uae_u32 atc_last_ins_laddr, atc_last_ins_paddr; +uae_u8 atc_last_ins_cache; +#endif +#if MMU_DPAGECACHE +struct mmufastcache atc_data_cache_read[MMUFASTCACHE_ENTRIES]; +struct mmufastcache atc_data_cache_write[MMUFASTCACHE_ENTRIES]; +#endif + +#if CACHE_HIT_COUNT +int mmu_ins_hit, mmu_ins_miss; +int mmu_data_read_hit, mmu_data_read_miss; +int mmu_data_write_hit, mmu_data_write_miss; +#endif + +static void mmu_dump_ttr(const TCHAR * label, uae_u32 ttr) +{ + DUNUSED(label); +#if MMUDEBUG > 0 + uae_u32 from_addr, to_addr; + + from_addr = ttr & MMU_TTR_LOGICAL_BASE; + to_addr = (ttr & MMU_TTR_LOGICAL_MASK) << 8; + + console_out_f(_T("%s: [%08x] %08x - %08x enabled=%d supervisor=%d wp=%d cm=%02d\n"), + label, ttr, + from_addr, to_addr, + ttr & MMU_TTR_BIT_ENABLED ? 1 : 0, + (ttr & (MMU_TTR_BIT_SFIELD_ENABLED | MMU_TTR_BIT_SFIELD_SUPER)) >> MMU_TTR_SFIELD_SHIFT, + ttr & MMU_TTR_BIT_WRITE_PROTECT ? 1 : 0, + (ttr & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT + ); +#endif +} + +void mmu_make_transparent_region(uaecptr baseaddr, uae_u32 size, int datamode) +{ + uae_u32 * ttr; + uae_u32 * ttr0 = datamode ? ®s.dtt0 : ®s.itt0; + uae_u32 * ttr1 = datamode ? ®s.dtt1 : ®s.itt1; + + if ((*ttr1 & MMU_TTR_BIT_ENABLED) == 0) + ttr = ttr1; + else if ((*ttr0 & MMU_TTR_BIT_ENABLED) == 0) + ttr = ttr0; + else + return; + + *ttr = baseaddr & MMU_TTR_LOGICAL_BASE; + *ttr |= ((baseaddr + size - 1) & MMU_TTR_LOGICAL_BASE) >> 8; + *ttr |= MMU_TTR_BIT_ENABLED; + +#if MMUDEBUG > 0 + write_log(_T("MMU: map transparent mapping of %08x\n"), *ttr); +#endif +} + +void mmu_tt_modified (void) +{ + mmu_ttr_enabled_ins = ((regs.itt0 | regs.itt1) & MMU_TTR_BIT_ENABLED) != 0; + mmu_ttr_enabled_data = ((regs.dtt0 | regs.dtt1) & MMU_TTR_BIT_ENABLED) != 0; + mmu_ttr_enabled = mmu_ttr_enabled_ins || mmu_ttr_enabled_data; +} + + +#if MMUDUMP + +/* This dump output makes much more sense than old one */ + +#ifdef WINUAE_FOR_HATARI +#define ULONG Uint32 +#endif + +#define LEVELA_SIZE 7 +#define LEVELB_SIZE 7 +#define LEVELC_SIZE 6 +#define PAGE_SIZE 12 // = 1 << 12 = 4096 + +#define LEVELA_VAL(x) ((((uae_u32)(x)) >> (32 - (LEVELA_SIZE ))) & ((1 << LEVELA_SIZE) - 1)) +#define LEVELB_VAL(x) ((((uae_u32)(x)) >> (32 - (LEVELA_SIZE + LEVELB_SIZE ))) & ((1 << LEVELB_SIZE) - 1)) +#define LEVELC_VAL(x) ((((uae_u32)(x)) >> (32 - (LEVELA_SIZE + LEVELB_SIZE + LEVELC_SIZE))) & ((1 << LEVELC_SIZE) - 1)) + +#define LEVELA(root, x) (get_long(root + LEVELA_VAL(x) * 4)) +#define LEVELB(a, x) (get_long((((uae_u32)a) & ~((1 << (LEVELB_SIZE + 2)) - 1)) + LEVELB_VAL(x) * 4)) +#define LEVELC(b, x) (get_long((((uae_u32)b) & ~((1 << (LEVELC_SIZE + 2)) - 1)) + LEVELC_VAL(x) * 4)) + +#define ISINVALID(x) ((((ULONG)x) & 3) == 0) + +static uae_u32 getdesc(uae_u32 root, uae_u32 addr) +{ + ULONG desc; + + desc = LEVELA(root, addr); + if (ISINVALID(desc)) + return desc; + desc = LEVELB(desc, addr); + if (ISINVALID(desc)) + return desc; + desc = LEVELC(desc, addr); + return desc; +} +static void mmu_dump_table(const char * label, uaecptr root_ptr) +{ + ULONG i; + ULONG startaddr; + ULONG odesc; + ULONG totalpages; + ULONG pagemask = (1 << PAGE_SIZE) - 1; + + root_ptr &= 0xfffffe00; + console_out_f(_T("MMU dump start. Root = %08x\n"), root_ptr); + totalpages = 1 << (32 - PAGE_SIZE); + startaddr = 0; + odesc = getdesc(root_ptr, startaddr); + for (i = 0; i <= totalpages; i++) { + ULONG addr = i << PAGE_SIZE; + ULONG desc = 0; + if (i < totalpages) + desc = getdesc(root_ptr, addr); + if ((desc & pagemask) != (odesc & pagemask) || i == totalpages) { + uae_u8 cm, sp; + cm = (odesc >> 5) & 3; + sp = (odesc >> 7) & 1; + console_out_f(_T("%08x - %08x: %08x WP=%d S=%d CM=%d (%08x)\n"), + startaddr, addr - 1, odesc & ~((1 << PAGE_SIZE) - 1), + (odesc & 4) ? 1 : 0, sp, cm, odesc); + startaddr = addr; + odesc = desc; + } + } + console_out_f(_T("MMU dump end\n")); +} + +#else +/* {{{ mmu_dump_table */ +static void mmu_dump_table(const char * label, uaecptr root_ptr) +{ + DUNUSED(label); + const int ROOT_TABLE_SIZE = 128, + PTR_TABLE_SIZE = 128, + PAGE_TABLE_SIZE = 64, + ROOT_INDEX_SHIFT = 25, + PTR_INDEX_SHIFT = 18; + // const int PAGE_INDEX_SHIFT = 12; + int root_idx, ptr_idx, page_idx; + uae_u32 root_des, ptr_des, page_des; + uaecptr ptr_des_addr, page_addr, + root_log, ptr_log, page_log; + + console_out_f(_T("%s: root=%x\n"), label, root_ptr); + + for (root_idx = 0; root_idx < ROOT_TABLE_SIZE; root_idx++) { + root_des = phys_get_long(root_ptr + root_idx); + + if ((root_des & 2) == 0) + continue; /* invalid */ + + console_out_f(_T("ROOT: %03d U=%d W=%d UDT=%02d\n"), root_idx, + root_des & 8 ? 1 : 0, + root_des & 4 ? 1 : 0, + root_des & 3 + ); + + root_log = root_idx << ROOT_INDEX_SHIFT; + + ptr_des_addr = root_des & MMU_ROOT_PTR_ADDR_MASK; + + for (ptr_idx = 0; ptr_idx < PTR_TABLE_SIZE; ptr_idx++) { + struct { + uaecptr log, phys; + int start_idx, n_pages; /* number of pages covered by this entry */ + uae_u32 match; + } page_info[PAGE_TABLE_SIZE]; + int n_pages_used; + + ptr_des = phys_get_long(ptr_des_addr + ptr_idx); + ptr_log = root_log | (ptr_idx << PTR_INDEX_SHIFT); + + if ((ptr_des & 2) == 0) + continue; /* invalid */ + + page_addr = ptr_des & (mmu_pagesize_8k ? MMU_PTR_PAGE_ADDR_MASK_8 : MMU_PTR_PAGE_ADDR_MASK_4); + + n_pages_used = -1; + for (page_idx = 0; page_idx < PAGE_TABLE_SIZE; page_idx++) { + + page_des = phys_get_long(page_addr + page_idx); + page_log = ptr_log | (page_idx << 2); // ??? PAGE_INDEX_SHIFT + + switch (page_des & 3) { + case 0: /* invalid */ + continue; + case 1: case 3: /* resident */ + case 2: /* indirect */ + if (n_pages_used == -1 || page_info[n_pages_used].match != page_des) { + /* use the next entry */ + n_pages_used++; + + page_info[n_pages_used].match = page_des; + page_info[n_pages_used].n_pages = 1; + page_info[n_pages_used].start_idx = page_idx; + page_info[n_pages_used].log = page_log; + } else { + page_info[n_pages_used].n_pages++; + } + break; + } + } + + if (n_pages_used == -1) + continue; + + console_out_f(_T(" PTR: %03d U=%d W=%d UDT=%02d\n"), ptr_idx, + ptr_des & 8 ? 1 : 0, + ptr_des & 4 ? 1 : 0, + ptr_des & 3 + ); + + + for (page_idx = 0; page_idx <= n_pages_used; page_idx++) { + page_des = page_info[page_idx].match; + + if ((page_des & MMU_PDT_MASK) == 2) { + console_out_f(_T(" PAGE: %03d-%03d log=%08x INDIRECT --> addr=%08x\n"), + page_info[page_idx].start_idx, + page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1, + page_info[page_idx].log, + page_des & MMU_PAGE_INDIRECT_MASK + ); + + } else { + console_out_f(_T(" PAGE: %03d-%03d log=%08x addr=%08x UR=%02d G=%d U1/0=%d S=%d CM=%d M=%d U=%d W=%d\n"), + page_info[page_idx].start_idx, + page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1, + page_info[page_idx].log, + page_des & (mmu_pagesize_8k ? MMU_PAGE_ADDR_MASK_8 : MMU_PAGE_ADDR_MASK_4), + (page_des & (mmu_pagesize_8k ? MMU_PAGE_UR_MASK_8 : MMU_PAGE_UR_MASK_4)) >> MMU_PAGE_UR_SHIFT, + page_des & MMU_DES_GLOBAL ? 1 : 0, + (page_des & MMU_TTR_UX_MASK) >> MMU_TTR_UX_SHIFT, + page_des & MMU_DES_SUPER ? 1 : 0, + (page_des & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT, + page_des & MMU_DES_MODIFIED ? 1 : 0, + page_des & MMU_DES_USED ? 1 : 0, + page_des & MMU_DES_WP ? 1 : 0 + ); + } + } + } + + } +} +/* }}} */ +#endif + +/* {{{ mmu_dump_atc */ +static void mmu_dump_atc(void) +{ + +} +/* }}} */ + +/* {{{ mmu_dump_tables */ +void mmu_dump_tables(void) +{ + console_out_f(_T("URP: %08x SRP: %08x MMUSR: %x TC: %x\n"), regs.urp, regs.srp, regs.mmusr, regs.tcr); + mmu_dump_ttr(_T("DTT0"), regs.dtt0); + mmu_dump_ttr(_T("DTT1"), regs.dtt1); + mmu_dump_ttr(_T("ITT0"), regs.itt0); + mmu_dump_ttr(_T("ITT1"), regs.itt1); + mmu_dump_atc(); +#if MMUDUMP + mmu_dump_table("SRP", regs.srp); + if (regs.urp != regs.srp) + mmu_dump_table("URP", regs.urp); +#endif +} +/* }}} */ + +static void flush_shortcut_cache(uaecptr addr, bool super) +{ +#if MMU_IPAGECACHE + atc_last_ins_laddr = mmu_pagemask; +#endif +#if MMU_DPAGECACHE + if (addr == 0xffffffff) { + memset(&atc_data_cache_read, 0xff, sizeof atc_data_cache_read); + memset(&atc_data_cache_write, 0xff, sizeof atc_data_cache_write); + } else { + int i; + for (i = 0; i < MMUFASTCACHE_ENTRIES; i++) { + uae_u32 idx = ((addr & mmu_pagemaski) >> mmu_pageshift1m) | (super ? 1 : 0); + if (atc_data_cache_read[i].log == idx) + atc_data_cache_read[i].log = 0xffffffff; + if (atc_data_cache_write[i].log == idx) + atc_data_cache_write[i].log = 0xffffffff; + } + } +#endif +} + +static ALWAYS_INLINE int mmu_get_fc(bool super, bool data) +{ + return (super ? 4 : 0) | (data ? 1 : 2); +} + +void mmu_bus_error(uaecptr addr, uae_u32 val, int fc, bool write, int size,uae_u32 status060, bool nonmmu) +{ + if (currprefs.mmu_model == 68040) { + uae_u16 ssw = 0; + + if (ismoves) { + // MOVES special behavior + int fc2 = write ? regs.dfc : regs.sfc; + if (fc2 == 0 || fc2 == 3 || fc2 == 4 || fc2 == 7) + ssw |= MMU_SSW_TT1; + if ((fc2 & 3) != 3) + fc2 &= ~2; +#if MMUDEBUGMISC > 0 + write_log (_T("040 MMU MOVES fc=%d -> %d\n"), fc, fc2); +#endif + fc = fc2; + } + + ssw |= fc & MMU_SSW_TM; /* TM = FC */ + + switch (size) { + case sz_byte: + ssw |= MMU_SSW_SIZE_B; + break; + case sz_word: + ssw |= MMU_SSW_SIZE_W; + break; + case sz_long: + ssw |= MMU_SSW_SIZE_L; + break; + } + + regs.wb3_status = write ? 0x80 | (ssw & 0x7f) : 0; + regs.wb3_data = val; + regs.wb2_status = 0; + if (!write) + ssw |= MMU_SSW_RW; + + if (size == 16) { // MOVE16 + ssw |= MMU_SSW_SIZE_CL; + ssw |= MMU_SSW_TT0; + regs.mmu_effective_addr &= ~15; + if (write) { + // clear normal writeback if MOVE16 write + regs.wb3_status &= ~0x80; + // wb2 = cacheline size writeback + regs.wb2_status = 0x80 | MMU_SSW_SIZE_CL | (ssw & 0x1f); + regs.wb2_address = regs.mmu_effective_addr; + write_log (_T("040 MMU MOVE16 WRITE FAULT!\n")); + } + } + + if (mmu040_movem) { + ssw |= MMU_SSW_CM; + regs.mmu_effective_addr = mmu040_movem_ea; + mmu040_movem = 0; +#if MMUDEBUGMISC > 0 + write_log (_T("040 MMU_SSW_CM EA=%08X\n"), mmu040_movem_ea); +#endif + } + if (locked_rmw_cycle) { + ssw |= MMU_SSW_LK; + ssw &= ~MMU_SSW_RW; +#if MMUDEBUGMISC > 0 + write_log (_T("040 MMU_SSW_LK!\n")); +#endif + } + + if (!nonmmu) + ssw |= MMU_SSW_ATC; + regs.mmu_ssw = ssw; + +#if MMUDEBUG > 0 + write_log(_T("BF: fc=%d w=%d logical=%08x ssw=%04x PC=%08x INS=%04X\n"), fc, write, addr, ssw, m68k_getpc(), mmu_opcode); +#endif + } else { + uae_u32 fslw = 0; + + fslw |= write ? MMU_FSLW_W : MMU_FSLW_R; + fslw |= fc << 16; /* MMU_FSLW_TM */ + + switch (size) { + case sz_byte: + fslw |= MMU_FSLW_SIZE_B; + break; + case sz_word: + fslw |= MMU_FSLW_SIZE_W; + break; + case sz_long: + fslw |= MMU_FSLW_SIZE_L; + break; + case 16: // MOVE16 + addr &= ~15; + fslw |= MMU_FSLW_SIZE_D; + fslw |= MMU_FSLW_TT_16; + break; + } + if ((fc & 3) == 2) { + // instruction faults always point to opcode address +#if MMUDEBUGMISC > 0 + write_log(_T("INS FAULT %08x %08x %d\n"), addr, regs.instruction_pc, mmu060_state); +#endif + addr = regs.instruction_pc; + if (mmu060_state == 0) { + fslw |= MMU_FSLW_IO; // opword fetch + } else { + fslw |= MMU_FSLW_IO | MMU_FSLW_MA; // extension word + } + } + if (rmw_cycle) { + fslw |= MMU_FSLW_W | MMU_FSLW_R; + } + if (locked_rmw_cycle) { + fslw |= MMU_FSLW_LK; + write_log (_T("060 MMU_FSLW_LK!\n")); + } + fslw |= status060; + regs.mmu_fslw = fslw; + +#if MMUDEBUG > 0 + write_log(_T("BF: fc=%d w=%d s=%d log=%08x ssw=%08x rmw=%d PC=%08x INS=%04X\n"), fc, write, 1 << size, addr, fslw, rmw_cycle, m68k_getpc(), mmu_opcode); +#endif + + } + + rmw_cycle = false; + locked_rmw_cycle = false; + regs.mmu_fault_addr = addr; + +#if 0 + activate_debugger (); +#endif + + cache_default_data &= ~CACHE_DISABLE_ALLOCATE; + + THROW(2); +} + +/* + * mmu access is a 4 step process: + * if mmu is not enabled just read physical + * check transparent region, if transparent, read physical + * check ATC (address translation cache), read immediately if HIT + * read from mmu with the long path (and allocate ATC entry if needed) + */ + +/* check if an address matches a ttr */ +static int mmu_do_match_ttr(uae_u32 ttr, uaecptr addr, bool super) +{ + if (ttr & MMU_TTR_BIT_ENABLED) { /* TTR enabled */ + uae_u8 msb, mask; + + msb = ((addr ^ ttr) & MMU_TTR_LOGICAL_BASE) >> 24; + mask = (ttr & MMU_TTR_LOGICAL_MASK) >> 16; + + if (!(msb & ~mask)) { + + if ((ttr & MMU_TTR_BIT_SFIELD_ENABLED) == 0) { + if (((ttr & MMU_TTR_BIT_SFIELD_SUPER) == 0) != (super == 0)) { + return TTR_NO_MATCH; + } + } + if (ttr & MMU_TTR_CACHE_DISABLE) { + mmu_cache_state = CACHE_DISABLE_MMU; + } else { + mmu_cache_state = CACHE_ENABLE_ALL; + if (ttr & MMU_TTR_CACHE_MODE) { + mmu_cache_state |= CACHE_ENABLE_COPYBACK; + } + } + return (ttr & MMU_TTR_BIT_WRITE_PROTECT) ? TTR_NO_WRITE : TTR_OK_MATCH; + } + } + return TTR_NO_MATCH; +} + +int mmu_match_ttr_ins(uaecptr addr, bool super) +{ + int res; + + if (!mmu_ttr_enabled_ins) + return TTR_NO_MATCH; + res = mmu_do_match_ttr(regs.itt0, addr, super); + if (res == TTR_NO_MATCH) + res = mmu_do_match_ttr(regs.itt1, addr, super); + return res; +} + +int mmu_match_ttr(uaecptr addr, bool super, bool data) +{ + int res; + + if (!mmu_ttr_enabled) + return TTR_NO_MATCH; + if (data) { + res = mmu_do_match_ttr(regs.dtt0, addr, super); + if (res == TTR_NO_MATCH) + res = mmu_do_match_ttr(regs.dtt1, addr, super); + } else { + res = mmu_do_match_ttr(regs.itt0, addr, super); + if (res == TTR_NO_MATCH) + res = mmu_do_match_ttr(regs.itt1, addr, super); + } + return res; +} + +void mmu_bus_error_ttr_write_fault(uaecptr addr, bool super, bool data, uae_u32 val, int size) + { + uae_u32 status = 0; + + if (currprefs.mmu_model == 68060) { + status |= MMU_FSLW_TTR; + } + mmu_bus_error(addr, val, mmu_get_fc (super, data), true, size, status, false); +} + +int mmu_match_ttr_write(uaecptr addr, bool super, bool data, uae_u32 val, int size) +{ + int res = TTR_NO_MATCH; + if (mmu_ttr_enabled) { + res = mmu_match_ttr(addr, super, data); + } + if (res == TTR_NO_WRITE || (res == TTR_NO_MATCH && !regs.mmu_enabled && (regs.tcr & MMU_TCR_DWO))) + mmu_bus_error_ttr_write_fault(addr, super, data, val, size); + return res; +} + +int mmu_match_ttr_maybe_write(uaecptr addr, bool super, bool data, int size, bool write) +{ + int res = TTR_NO_MATCH; + if (mmu_ttr_enabled) { + res = mmu_match_ttr(addr, super, data); + } + if (write && ((res == TTR_NO_WRITE) || (res == TTR_NO_MATCH && !regs.mmu_enabled && (regs.tcr & MMU_TCR_DWO)))) + mmu_bus_error_ttr_write_fault(addr, super, data, 0, size); + return res; +} + +// Descriptor read accesses can use data cache but never allocate new cache lines. +static uae_u32 desc_get_long(uaecptr addr) +{ + mmu_cache_state = ce_cachable[addr >>16] | CACHE_DISABLE_ALLOCATE; + return x_phys_get_long(addr); +} +// Write accesses probably are always pushed to memomory +static void desc_put_long(uaecptr addr, uae_u32 v) +{ + mmu_cache_state = CACHE_DISABLE_MMU; + x_phys_put_long(addr, v); +} + +/* + * Lookup the address by walking the page table and updating + * the page descriptors accordingly. Returns the found descriptor + * or produces a bus error. + */ +static uae_u32 mmu_fill_atc(uaecptr addr, bool super, uae_u32 tag, bool write, struct mmu_atc_line *l, uae_u32 *status060) +{ + uae_u32 desc, desc_addr, wp; + uae_u32 status = 0; + int i; + + wp = 0; + desc = super ? regs.srp : regs.urp; + + /* fetch root table descriptor */ + i = (addr >> 23) & 0x1fc; + desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i; + + SAVE_EXCEPTION; + TRY(prb) { + desc = desc_get_long(desc_addr); + if ((desc & 2) == 0) { +#if MMUDEBUG > 1 + write_log(_T("MMU: invalid root descriptor %s for %x desc at %x desc=%x\n"), super ? _T("srp"):_T("urp"), + addr, desc_addr, desc); +#endif + *status060 |= MMU_FSLW_PTA; + goto fail; + } + + wp |= desc; + if ((desc & MMU_DES_USED) == 0) { + desc_put_long(desc_addr, desc | MMU_DES_USED); + } + + /* fetch pointer table descriptor */ + i = (addr >> 16) & 0x1fc; + desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i; + desc = desc_get_long(desc_addr); + if ((desc & 2) == 0) { +#if MMUDEBUG > 1 + write_log(_T("MMU: invalid ptr descriptor %s for %x desc at %x desc=%x\n"), super ? _T("srp"):_T("urp"), + addr, desc_addr, desc); +#endif + *status060 |= MMU_FSLW_PTB; + goto fail; + } + wp |= desc; + if ((desc & MMU_DES_USED) == 0) + desc_put_long(desc_addr, desc | MMU_DES_USED); + + /* fetch page table descriptor */ + if (mmu_pagesize_8k) { + i = (addr >> 11) & 0x7c; + desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_8) + i; + } else { + i = (addr >> 10) & 0xfc; + desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_4) + i; + } + + desc = desc_get_long(desc_addr); + if ((desc & 3) == 2) { + /* indirect */ + desc_addr = desc & MMU_PAGE_INDIRECT_MASK; + desc = desc_get_long(desc_addr); + } + if ((desc & 1) == 1) { + wp |= desc; + if (write) { + if ((wp & MMU_DES_WP) || ((desc & MMU_DES_SUPER) && !super)) { + if ((desc & MMU_DES_USED) == 0) { + desc |= MMU_DES_USED; + desc_put_long(desc_addr, desc); + } + } else if ((desc & (MMU_DES_USED|MMU_DES_MODIFIED)) != + (MMU_DES_USED|MMU_DES_MODIFIED)) { + desc |= MMU_DES_USED|MMU_DES_MODIFIED; + desc_put_long(desc_addr, desc); + } + } else { + if ((desc & MMU_DES_USED) == 0) { + desc |= MMU_DES_USED; + desc_put_long(desc_addr, desc); + } + } + desc |= wp & MMU_DES_WP; + } else { + if ((desc & 3) == 2) { + *status060 |= MMU_FSLW_IL; +#if MMUDEBUG > 1 + write_log(_T("MMU: double indirect descriptor log=%0x desc=%08x @%08x\n"), addr, desc, desc_addr); +#endif + } else { + *status060 |= MMU_FSLW_PF; +#if MMUDEBUG > 2 + write_log(_T("MMU: invalid page descriptor log=%0x desc=%08x @%08x\n"), addr, desc, desc_addr); +#endif + } +fail: + desc = 0; + } + + /* this will cause a bus error exception */ + if (!super && (desc & MMU_DES_SUPER)) { + *status060 |= MMU_FSLW_SP; + } else if (write && (desc & MMU_DES_WP)) { + *status060 |= MMU_FSLW_WP; + } + + // 68040 always creates ATC entry. 68060 only if valid descriptor was found. + if (currprefs.mmu_model == 68040 || (desc & MMU_MMUSR_R)) { + /* Create new ATC entry and return status */ + l->status = desc & (MMU_MMUSR_G|MMU_MMUSR_Ux|MMU_MMUSR_S|MMU_MMUSR_CM|MMU_MMUSR_M|MMU_MMUSR_W|MMU_MMUSR_R); + l->phys = desc & mmu_pagemaski; + l->valid = 1; + l->tag = tag; + status = l->phys | l->status; + } + + RESTORE_EXCEPTION; + } CATCH(prb) { + RESTORE_EXCEPTION; + + /* bus error during table search */ + if (currprefs.mmu_model == 68040) { + l->status = 0; + l->phys = 0; + l->valid = 1; + l->tag = tag; + } + status = MMU_MMUSR_B; + *status060 |= MMU_FSLW_LK | MMU_FSLW_TWE; + +#if MMUDEBUG > 0 + write_log(_T("MMU: bus error during table search.\n")); +#endif + } ENDTRY + +#if MMUDEBUG > 2 + write_log(_T("translate: %x,%u,%u -> %x\n"), addr, super, write, desc); +#endif + + flush_shortcut_cache(addr, super); + + return status; +} + +static void mmu_add_cache(uaecptr addr, uaecptr phys, bool super, bool data, bool write) +{ + if (!data) { +#if MMU_IPAGECACHE + uae_u32 laddr = (addr & mmu_pagemaski) | (super ? 1 : 0); + atc_last_ins_laddr = laddr; + atc_last_ins_paddr = phys; + atc_last_ins_cache = mmu_cache_state; +#else + ; +#endif +#if MMU_DPAGECACHE + } else { + uae_u32 idx1 = ((addr & mmu_pagemaski) >> mmu_pageshift1m) | (super ? 1 : 0); + uae_u32 idx2 = idx1 & (MMUFASTCACHE_ENTRIES - 1); + if (write) { + if (idx2 < MMUFASTCACHE_ENTRIES - 1) { + atc_data_cache_write[idx2].log = idx1; + atc_data_cache_write[idx2].phys = phys; + atc_data_cache_write[idx2].cache_state = mmu_cache_state; + } + } else { + if (idx2 < MMUFASTCACHE_ENTRIES - 1) { + atc_data_cache_read[idx2].log = idx1; + atc_data_cache_read[idx2].phys = phys; + atc_data_cache_read[idx2].cache_state = mmu_cache_state; + } + } +#endif + } +} + +uaecptr mmu_translate(uaecptr addr, uae_u32 val, bool super, bool data, bool write, int size) +{ + int way, i, index, way_invalid; + struct mmu_atc_line *l; + uae_u32 status060 = 0; + uae_u32 tag = ((super ? 0x80000000 : 0x00000000) | (addr >> 1)) & mmu_tagmask; + + if (mmu_pagesize_8k) + index=(addr & 0x0001E000)>>13; + else + index=(addr & 0x0000F000)>>12; + way_invalid = ATC_WAYS; + way_random++; + way = mmu_atc_ways[data]; + + for (i = 0; i < ATC_WAYS; i++) { + // if we have this + l = &mmu_atc_array[data][index][way]; + if (l->valid) { + if (tag == l->tag) { +atc_retry: + // check if we need to cause a page fault + if (((l->status&(MMU_MMUSR_W|MMU_MMUSR_S|MMU_MMUSR_R))!=MMU_MMUSR_R)) { + if (((l->status&MMU_MMUSR_W) && write) || + ((l->status&MMU_MMUSR_S) && !super) || + !(l->status&MMU_MMUSR_R)) { + + if ((l->status&MMU_MMUSR_S) && !super) + status060 |= MMU_FSLW_SP; + if ((l->status&MMU_MMUSR_W) && write) + status060 |= MMU_FSLW_WP; + + mmu_bus_error(addr, val, mmu_get_fc(super, data), write, size, status060, false); + return 0; // never reach, bus error longjumps out of the function + } + } + // if first write to this page initiate table search to set M bit (but modify this slot) + if (!(l->status&MMU_MMUSR_M) && write) { + way_invalid = way; + break; + } + + // save way for next access (likely in same page) + mmu_atc_ways[data] = way; + + if (l->status & MMU_MMUSR_CM_DISABLE) { + mmu_cache_state = CACHE_DISABLE_MMU; + } else { + mmu_cache_state = CACHE_ENABLE_ALL; + if (l->status & MMU_MMUSR_CM_MODE) { + mmu_cache_state |= CACHE_ENABLE_COPYBACK; + } + } + + mmu_add_cache(addr, l->phys, super, data, write); + + // return translated addr + return l->phys | (addr & mmu_pagemask); + } else { + way_invalid = way; + } + } + way++; + way %= ATC_WAYS; + } + // no entry found, we need to create a new one, first find an atc line to replace + way_random %= ATC_WAYS; + way = (way_invalid < ATC_WAYS) ? way_invalid : way_random; + + // then initiate table search and create a new entry + l = &mmu_atc_array[data][index][way]; + mmu_fill_atc(addr, super, tag, write, l, &status060); + + if (status060 && currprefs.mmu_model == 68060) { + mmu_bus_error(addr, val, mmu_get_fc(super, data), write, size, status060, false); + } + + // and retry the ATC search + way_random++; + goto atc_retry; +} + +static void misalignednotfirst(uaecptr addr) +{ +#if MMUDEBUGMISC > 0 + write_log (_T("misalignednotfirst %08x -> %08x %08X\n"), regs.mmu_fault_addr, addr, regs.instruction_pc); +#endif + regs.mmu_fault_addr = addr; + regs.mmu_fslw |= MMU_FSLW_MA; + regs.mmu_ssw |= MMU_SSW_MA; +} + +static void misalignednotfirstcheck(uaecptr addr) +{ + if (regs.mmu_fault_addr == addr) + return; + misalignednotfirst (addr); +} + +uae_u16 REGPARAM2 mmu_get_word_unaligned(uaecptr addr, bool data) +{ + uae_u16 res; + + res = (uae_u16)mmu_get_byte(addr, data, sz_word) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_byte(addr + 1, data, sz_word); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + return res; +} + +uae_u32 REGPARAM2 mmu_get_long_unaligned(uaecptr addr, bool data) +{ + uae_u32 res; + + if (likely(!(addr & 1))) { + res = (uae_u32)mmu_get_word(addr, data, sz_long) << 16; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_word(addr + 2, data, sz_long); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } else { + res = (uae_u32)mmu_get_byte(addr, data, sz_long) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res = (res | mmu_get_byte(addr + 1, data, sz_long)) << 8; + res = (res | mmu_get_byte(addr + 2, data, sz_long)) << 8; + res |= mmu_get_byte(addr + 3, data, sz_long); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } + return res; +} + +uae_u32 REGPARAM2 mmu_get_ilong_unaligned(uaecptr addr) +{ + uae_u32 res; + + res = (uae_u32)mmu_get_iword(addr, sz_long) << 16; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_iword(addr + 2, sz_long); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + return res; +} + +static uae_u16 REGPARAM2 mmu_get_lrmw_word_unaligned(uaecptr addr) +{ + uae_u16 res; + + res = (uae_u16)mmu_get_user_byte(addr, regs.s != 0, true, sz_word, true) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_byte(addr + 1, regs.s != 0, true, sz_word, true); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + return res; +} + +static uae_u32 REGPARAM2 mmu_get_lrmw_long_unaligned(uaecptr addr) +{ + uae_u32 res; + + if (likely(!(addr & 1))) { + res = (uae_u32)mmu_get_user_word(addr, regs.s != 0, true, sz_long, true) << 16; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_word(addr + 2, regs.s != 0, true, sz_long, true); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } else { + res = (uae_u32)mmu_get_user_byte(addr, regs.s != 0, true, sz_long, true) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res = (res | mmu_get_user_byte(addr + 1, regs.s != 0, true, sz_long, true)) << 8; + res = (res | mmu_get_user_byte(addr + 2, regs.s != 0, true, sz_long, true)) << 8; + res |= mmu_get_user_byte(addr + 3, regs.s != 0, true, sz_long, true); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } + return res; +} + + +static void REGPARAM2 mmu_put_lrmw_long_unaligned(uaecptr addr, uae_u32 val) +{ + SAVE_EXCEPTION; + TRY(prb) { + if (likely(!(addr & 1))) { + mmu_put_user_word(addr, val >> 16, regs.s != 0, sz_long, true); + mmu_put_user_word(addr + 2, val, regs.s != 0, sz_long, true); + } else { + mmu_put_user_byte(addr, val >> 24, regs.s != 0, sz_long, true); + mmu_put_user_byte(addr + 1, val >> 16, regs.s != 0, sz_long, true); + mmu_put_user_byte(addr + 2, val >> 8, regs.s != 0, sz_long, true); + mmu_put_user_byte(addr + 3, val, regs.s != 0, sz_long, true); + } + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY +} + +static void REGPARAM2 mmu_put_lrmw_word_unaligned(uaecptr addr, uae_u16 val) +{ + SAVE_EXCEPTION; + TRY(prb) { + mmu_put_user_byte(addr, val >> 8, regs.s != 0, sz_word, true); + mmu_put_user_byte(addr + 1, val, regs.s != 0, sz_word, true); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY +} + + + +void REGPARAM2 mmu_put_long_unaligned(uaecptr addr, uae_u32 val, bool data) +{ + SAVE_EXCEPTION; + TRY(prb) { + if (likely(!(addr & 1))) { + mmu_put_word(addr, val >> 16, data, sz_long); + mmu_put_word(addr + 2, val, data, sz_long); + } else { + mmu_put_byte(addr, val >> 24, data, sz_long); + mmu_put_byte(addr + 1, val >> 16, data, sz_long); + mmu_put_byte(addr + 2, val >> 8, data, sz_long); + mmu_put_byte(addr + 3, val, data, sz_long); + } + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY +} + +void REGPARAM2 mmu_put_word_unaligned(uaecptr addr, uae_u16 val, bool data) +{ + SAVE_EXCEPTION; + TRY(prb) { + mmu_put_byte(addr, val >> 8, data, sz_word); + mmu_put_byte(addr + 1, val, data, sz_word); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY +} + +uae_u32 REGPARAM2 sfc_get_long(uaecptr addr) +{ + bool super = (regs.sfc & 4) != 0; + uae_u32 res; + + ismoves = true; + if (likely(!is_unaligned(addr, 4))) { + res = mmu_get_user_long(addr, super, false, sz_long, false); + } else { + if (likely(!(addr & 1))) { + res = (uae_u32)mmu_get_user_word(addr, super, false, sz_long, false) << 16; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_word(addr + 2, super, false, sz_long, false); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } else { + res = (uae_u32)mmu_get_user_byte(addr, super, false, sz_long, false) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res = (res | mmu_get_user_byte(addr + 1, super, false, sz_long, false)) << 8; + res = (res | mmu_get_user_byte(addr + 2, super, false, sz_long, false)) << 8; + res |= mmu_get_user_byte(addr + 3, super, false, sz_long, false); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } + } + + ismoves = false; + return res; +} + +uae_u16 REGPARAM2 sfc_get_word(uaecptr addr) +{ + bool super = (regs.sfc & 4) != 0; + uae_u16 res; + + ismoves = true; + if (likely(!is_unaligned(addr, 2))) { + res = mmu_get_user_word(addr, super, false, sz_word, false); + } else { + res = (uae_u16)mmu_get_user_byte(addr, super, false, sz_word, false) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_byte(addr + 1, super, false, sz_word, false); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + misalignednotfirst(addr); + THROW_AGAIN(prb); + } ENDTRY + } + ismoves = false; + return res; +} + +uae_u8 REGPARAM2 sfc_get_byte(uaecptr addr) +{ + bool super = (regs.sfc & 4) != 0; + uae_u8 res; + + ismoves = true; + res = mmu_get_user_byte(addr, super, false, sz_byte, false); + ismoves = false; + return res; +} + +void REGPARAM2 dfc_put_long(uaecptr addr, uae_u32 val) +{ + bool super = (regs.dfc & 4) != 0; + + ismoves = true; + SAVE_EXCEPTION; + TRY(prb) { + if (likely(!is_unaligned(addr, 4))) { + mmu_put_user_long(addr, val, super, sz_long, false); + } else if (likely(!(addr & 1))) { + mmu_put_user_word(addr, val >> 16, super, sz_long, false); + mmu_put_user_word(addr + 2, val, super, sz_long, false); + } else { + mmu_put_user_byte(addr, val >> 24, super, sz_long, false); + mmu_put_user_byte(addr + 1, val >> 16, super, sz_long, false); + mmu_put_user_byte(addr + 2, val >> 8, super, sz_long, false); + mmu_put_user_byte(addr + 3, val, super, sz_long, false); + } + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY + ismoves = false; +} + +void REGPARAM2 dfc_put_word(uaecptr addr, uae_u16 val) +{ + bool super = (regs.dfc & 4) != 0; + + ismoves = true; + SAVE_EXCEPTION; + TRY(prb) { + if (likely(!is_unaligned(addr, 2))) { + mmu_put_user_word(addr, val, super, sz_word, false); + } else { + mmu_put_user_byte(addr, val >> 8, super, sz_word, false); + mmu_put_user_byte(addr + 1, val, super, sz_word, false); + } + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + misalignednotfirstcheck(addr); + THROW_AGAIN(prb); + } ENDTRY + ismoves = false; +} + +void REGPARAM2 dfc_put_byte(uaecptr addr, uae_u8 val) +{ + bool super = (regs.dfc & 4) != 0; + + ismoves = true; + SAVE_EXCEPTION; + TRY(prb) { + mmu_put_user_byte(addr, val, super, sz_byte, false); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.wb3_data = val; + THROW_AGAIN(prb); + } ENDTRY + ismoves = false; +} + +void mmu_get_move16(uaecptr addr, uae_u32 *v, bool data, int size) +{ + bool super = regs.s != 0; + int i; + + addr &= ~15; + if ((!mmu_ttr_enabled || mmu_match_ttr(addr,super,data) == TTR_NO_MATCH) && regs.mmu_enabled) { + addr = mmu_translate(addr, 0, super, data, false, size); + } + // MOVE16 read and cache miss: do not allocate new cache line + mmu_cache_state |= CACHE_DISABLE_ALLOCATE; + for (i = 0; i < 4; i++) { + v[i] = x_phys_get_long(addr + i * 4); + } +} + +void mmu_put_move16(uaecptr addr, uae_u32 *val, bool data, int size) +{ + bool super = regs.s != 0; + int i; + + addr &= ~15; + if ((!mmu_ttr_enabled || mmu_match_ttr_write(addr,super,data,val[0],size) == TTR_NO_MATCH) && regs.mmu_enabled) { + addr = mmu_translate(addr, val[0], super, data, true, size); + } + // MOVE16 write invalidates existing line and also does not allocate new cache lines. + mmu_cache_state = CACHE_DISABLE_MMU; + for (i = 0; i < 4; i++) { + x_phys_put_long(addr + i * 4, val[i]); + } +} + + +void REGPARAM2 mmu_op_real(uae_u32 opcode, uae_u16 extra) +{ + bool super = (regs.dfc & 4) != 0; + DUNUSED(extra); + if ((opcode & 0xFE0) == 0x0500) { // PFLUSH + bool glob; + int regno; + //D(didflush = 0); + uae_u32 addr; + /* PFLUSH */ + regno = opcode & 7; + glob = (opcode & 8) != 0; + + if (opcode & 16) { +#if MMUINSDEBUG > 1 + write_log(_T("pflusha(%u,%u) PC=%08x\n"), glob, regs.dfc, m68k_getpc ()); +#endif + mmu_flush_atc_all(glob); + } else { + addr = m68k_areg(regs, regno); +#if MMUINSDEBUG > 1 + write_log(_T("pflush(%u,%u,%x) PC=%08x\n"), glob, regs.dfc, addr, m68k_getpc ()); +#endif + mmu_flush_atc(addr, super, glob); + } + flush_internals(); +#ifdef USE_JIT + flush_icache(0); +#endif + } else if ((opcode & 0x0FD8) == 0x0548) { // PTEST (68040) + bool write; + int regno; + uae_u32 addr; + uae_u32 status060 = 0; + + regno = opcode & 7; + write = (opcode & 32) == 0; + addr = m68k_areg(regs, regno); +#if MMUINSDEBUG > 0 + write_log(_T("PTEST%c (A%d) %08x DFC=%d\n"), write ? 'W' : 'R', regno, addr, regs.dfc); +#endif + mmu_flush_atc(addr, super, true); + bool data = (regs.dfc & 3) != 2; + int ttr_match = mmu_match_ttr(addr,super,data); + if (ttr_match != TTR_NO_MATCH) { + if (ttr_match == TTR_NO_WRITE && write) { + regs.mmusr = MMU_MMUSR_B; + } else { + regs.mmusr = MMU_MMUSR_T | MMU_MMUSR_R; + } + } else if (!currprefs.mmu_ec) { + int way; + uae_u32 index; + uae_u32 tag = ((super ? 0x80000000 : 0x00000000) | (addr >> 1)) & mmu_tagmask; + if (mmu_pagesize_8k) + index=(addr & 0x0001E000)>>13; + else + index=(addr & 0x0000F000)>>12; + for (way = 0; way < ATC_WAYS; way++) { + if (!mmu_atc_array[data][index][way].valid) + break; + } + if (way >= ATC_WAYS) { + way = way_random % ATC_WAYS; + } + regs.mmusr = mmu_fill_atc(addr, super, tag, write, &mmu_atc_array[data][index][way], &status060); +#if MMUINSDEBUG > 0 + write_log(_T("PTEST result: mmusr %08x\n"), regs.mmusr); +#endif + } + } else if ((opcode & 0xFFB8) == 0xF588) { // PLPA (68060) + int write = (opcode & 0x40) == 0; + int regno = opcode & 7; + uae_u32 addr = m68k_areg (regs, regno); + bool data = (regs.dfc & 3) != 2; + int ttr; + +#if MMUINSDEBUG > 0 + write_log(_T("PLPA%c param: %08x\n"), write ? 'W' : 'R', addr); +#endif + if (write) + ttr = mmu_match_ttr_write(addr, super, data, 0, 1); + else + ttr = mmu_match_ttr(addr,super,data); + if (ttr == TTR_NO_MATCH) { + if (!currprefs.mmu_ec) { + m68k_areg (regs, regno) = mmu_translate(addr, 0, super, data, write, 1); + } + } +#if MMUINSDEBUG > 0 + write_log(_T("PLPA%c result: %08x\n"), write ? 'W' : 'R', m68k_areg (regs, regno)); +#endif + } else { + op_illg (opcode); + } +} + +// fixme : global parameter? +void REGPARAM2 mmu_flush_atc(uaecptr addr, bool super, bool global) +{ + int way,type,index; + + uaecptr tag = ((super ? 0x80000000 : 0) | (addr >> 1)) & mmu_tagmask; + if (mmu_pagesize_8k) + index=(addr & 0x0001E000)>>13; + else + index=(addr & 0x0000F000)>>12; + for (type=0;typestatus & MMU_MMUSR_G)) + continue; + // if we have this + if (tag == l->tag && l->valid) { + l->valid=false; + } + } + } + flush_shortcut_cache(addr, super); + mmu_flush_cache(); +} + +void REGPARAM2 mmu_flush_atc_all(bool global) +{ + int way,slot,type; + for (type=0;typestatus&MMU_MMUSR_G)) + continue; + l->valid=false; + } + } + } + flush_shortcut_cache(0xffffffff, 0); + mmu_flush_cache(); +} + +void REGPARAM2 mmu_set_funcs(void) +{ + if (currprefs.mmu_model != 68040 && currprefs.mmu_model != 68060) + return; + if (currprefs.cpu_memory_cycle_exact || currprefs.cpu_compatible) { + x_phys_get_iword = get_word_icache040; + x_phys_get_ilong = get_long_icache040; + if (currprefs.cpu_data_cache) { + x_phys_get_byte = get_byte_cache_040; + x_phys_get_word = get_word_cache_040; + x_phys_get_long = get_long_cache_040; + x_phys_put_byte = put_byte_cache_040; + x_phys_put_word = put_word_cache_040; + x_phys_put_long = put_long_cache_040; + } else if (currprefs.cpu_memory_cycle_exact) { + x_phys_get_byte = mem_access_delay_byte_read_c040; + x_phys_get_word = mem_access_delay_word_read_c040; + x_phys_get_long = mem_access_delay_long_read_c040; + x_phys_put_byte = mem_access_delay_byte_write_c040; + x_phys_put_word = mem_access_delay_word_write_c040; + x_phys_put_long = mem_access_delay_long_write_c040; + } else { + x_phys_get_byte = phys_get_byte; + x_phys_get_word = phys_get_word; + x_phys_get_long = phys_get_long; + x_phys_put_byte = phys_put_byte; + x_phys_put_word = phys_put_word; + x_phys_put_long = phys_put_long; + } + } else { + x_phys_get_iword = phys_get_word; + x_phys_get_ilong = phys_get_long; + x_phys_get_byte = phys_get_byte; + x_phys_get_word = phys_get_word; + x_phys_get_long = phys_get_long; + x_phys_put_byte = phys_put_byte; + x_phys_put_word = phys_put_word; + x_phys_put_long = phys_put_long; + } +} + +void REGPARAM2 mmu_reset(void) +{ + mmu_flush_atc_all(true); + mmu_set_funcs(); +} + +uae_u16 REGPARAM2 mmu_set_tc(uae_u16 tc) +{ + if (currprefs.mmu_ec) { + tc &= ~(0x8000 | 0x4000); + // at least 68EC040 always returns zero when TC is read. + if (currprefs.cpu_model == 68040) + tc = 0; + } + + regs.mmu_enabled = (tc & 0x8000) != 0; + mmu_pagesize_8k = (tc & 0x4000) != 0; + + mmu_tagmask = mmu_pagesize_8k ? 0xFFFF0000 : 0xFFFF8000; + mmu_pagemask = mmu_pagesize_8k ? 0x00001FFF : 0x00000FFF; + mmu_pagemaski = ~mmu_pagemask; + regs.mmu_page_size = mmu_pagesize_8k ? 8192 : 4096; + mmu_pageshift = mmu_pagesize_8k ? 13 : 12; + mmu_pageshift1m = mmu_pageshift - 1; + + cache_default_ins = CACHE_ENABLE_ALL; + cache_default_data = CACHE_ENABLE_ALL; + if (currprefs.mmu_model == 68060) { + int dc = (tc >> 3) & 3; + cache_default_ins = 0; + if (!(dc & 2)) + cache_default_ins = CACHE_ENABLE_ALL; + dc = (tc >> 8) & 3; + cache_default_data = 0; + if (!(dc & 2)) + cache_default_data = (dc & 1) ? CACHE_ENABLE_COPYBACK | CACHE_ENABLE_ALL : CACHE_ENABLE_ALL; + } + + mmu_flush_atc_all(true); + + write_log(_T("%d MMU: TC=%04x enabled=%d page8k=%d PC=%08x\n"), tc, currprefs.mmu_model, regs.mmu_enabled, mmu_pagesize_8k, m68k_getpc()); + return tc; +} + +void REGPARAM2 mmu_set_super(bool super) +{ + mmu_is_super = super ? 0x80000000 : 0; +} + +void REGPARAM2 mmu_flush_cache(void) +{ +#if MMU_ICACHE + int len = sizeof(mmu_icache_data); + memset(&mmu_icache_data, 0xff, sizeof(mmu_icache_data)); +#endif +} + +void m68k_do_rte_mmu040 (uaecptr a7) +{ + uae_u16 ssr = get_word_mmu040 (a7 + 8 + 4); + if (ssr & MMU_SSW_CT) { + uaecptr src_a7 = a7 + 8 - 8; + uaecptr dst_a7 = a7 + 8 + 52; + put_word_mmu040 (dst_a7 + 0, get_word_mmu040 (src_a7 + 0)); + put_long_mmu040 (dst_a7 + 2, get_long_mmu040 (src_a7 + 2)); + // skip this word + put_long_mmu040 (dst_a7 + 8, get_long_mmu040 (src_a7 + 8)); + } + if (ssr & MMU_SSW_CM) { + mmu040_movem = 1; + mmu040_movem_ea = get_long_mmu040 (a7 + 8); +#if MMUDEBUGMISC > 0 + write_log (_T("MMU restarted MOVEM EA=%08X\n"), mmu040_movem_ea); +#endif + } +} + +void m68k_do_rte_mmu060 (uaecptr a7) +{ +#if 0 + mmu060_state = 2; +#endif +} + +void flush_mmu040 (uaecptr addr, int n) +{ + mmu_flush_cache(); +} + +void m68k_do_rts_mmu040 (void) +{ + uaecptr stack = m68k_areg (regs, 7); + uaecptr newpc = get_long_mmu040 (stack); + m68k_areg (regs, 7) += 4; + m68k_setpc (newpc); +} +void m68k_do_bsr_mmu040 (uaecptr oldpc, uae_s32 offset) +{ + uaecptr newstack = m68k_areg (regs, 7) - 4; + put_long_mmu040 (newstack, oldpc); + m68k_areg (regs, 7) -= 4; + m68k_incpci (offset); +} + +void flush_mmu060 (uaecptr addr, int n) +{ + mmu_flush_cache(); +} + +void m68k_do_rts_mmu060 (void) +{ + uaecptr stack = m68k_areg (regs, 7); + uaecptr newpc = get_long_mmu060 (stack); + m68k_areg (regs, 7) += 4; + m68k_setpc (newpc); +} +void m68k_do_bsr_mmu060 (uaecptr oldpc, uae_s32 offset) +{ + uaecptr newstack = m68k_areg (regs, 7) - 4; + put_long_mmu060 (newstack, oldpc); + m68k_areg (regs, 7) -= 4; + m68k_incpci (offset); +} + +void uae_mmu_put_lrmw (uaecptr addr, uae_u32 v, int size, int type) +{ + locked_rmw_cycle = true; + if (size == sz_byte) { + mmu_put_user_byte(addr, v, regs.s, sz_byte, true); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) { + mmu_put_lrmw_word_unaligned(addr, v); + } else { + mmu_put_user_word(addr, v, regs.s != 0, sz_word, true); + } + } else { + if (unlikely(is_unaligned(addr, 4))) + mmu_put_lrmw_long_unaligned(addr, v); + else + mmu_put_user_long(addr, v, regs.s, sz_long, true); + } + locked_rmw_cycle = false; +} +uae_u32 uae_mmu_get_lrmw (uaecptr addr, int size, int type) +{ + uae_u32 v; + locked_rmw_cycle = true; + if (size == sz_byte) { + v = mmu_get_user_byte(addr, regs.s != 0, true, sz_byte, true); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) { + v = mmu_get_lrmw_word_unaligned(addr); + } else { + v = mmu_get_user_word(addr, regs.s != 0, true, sz_word, true); + } + } else { + if (unlikely(is_unaligned(addr, 4))) + v = mmu_get_lrmw_long_unaligned(addr); + else + v = mmu_get_user_long(addr, regs.s != 0, true, sz_long, true); + } + locked_rmw_cycle = false; + return v; +} + +uae_u32 REGPARAM2 mmu060_get_rmw_bitfield (uae_u32 src, uae_u32 bdata[2], uae_s32 offset, int width) +{ + uae_u32 tmp1, tmp2, res, mask; + + offset &= 7; + mask = 0xffffffffu << (32 - width); + switch ((offset + width + 7) >> 3) { + case 1: + tmp1 = get_rmw_byte_mmu060 (src); + res = tmp1 << (24 + offset); + bdata[0] = tmp1 & ~(mask >> (24 + offset)); + break; + case 2: + tmp1 = get_rmw_word_mmu060 (src); + res = tmp1 << (16 + offset); + bdata[0] = tmp1 & ~(mask >> (16 + offset)); + break; + case 3: + tmp1 = get_rmw_word_mmu060 (src); + tmp2 = get_rmw_byte_mmu060 (src + 2); + res = tmp1 << (16 + offset); + bdata[0] = tmp1 & ~(mask >> (16 + offset)); + res |= tmp2 << (8 + offset); + bdata[1] = tmp2 & ~(mask >> (8 + offset)); + break; + case 4: + tmp1 = get_rmw_long_mmu060 (src); + res = tmp1 << offset; + bdata[0] = tmp1 & ~(mask >> offset); + break; + case 5: + tmp1 = get_rmw_long_mmu060 (src); + tmp2 = get_rmw_byte_mmu060 (src + 4); + res = tmp1 << offset; + bdata[0] = tmp1 & ~(mask >> offset); + res |= tmp2 >> (8 - offset); + bdata[1] = tmp2 & ~(mask << (8 - offset)); + break; + default: + /* Panic? */ + write_log (_T("x_get_bitfield() can't happen %d\n"), (offset + width + 7) >> 3); + res = 0; + break; + } + return res; +} + +void REGPARAM2 mmu060_put_rmw_bitfield (uae_u32 dst, uae_u32 bdata[2], uae_u32 val, uae_s32 offset, int width) +{ + offset = (offset & 7) + width; + switch ((offset + 7) >> 3) { + case 1: + put_rmw_byte_mmu060 (dst, bdata[0] | (val << (8 - offset))); + break; + case 2: + put_rmw_word_mmu060 (dst, bdata[0] | (val << (16 - offset))); + break; + case 3: + put_rmw_word_mmu060 (dst, bdata[0] | (val >> (offset - 16))); + put_rmw_byte_mmu060 (dst + 2, bdata[1] | (val << (24 - offset))); + break; + case 4: + put_rmw_long_mmu060 (dst, bdata[0] | (val << (32 - offset))); + break; + case 5: + put_rmw_long_mmu060 (dst, bdata[0] | (val >> (offset - 32))); + put_rmw_byte_mmu060 (dst + 4, bdata[1] | (val << (40 - offset))); + break; + default: + write_log (_T("x_put_bitfield() can't happen %d\n"), (offset + 7) >> 3); + break; + } +} + + +#ifndef __cplusplus +jmp_buf __exbuf; +int __exvalue; +#define MAX_TRY_STACK 256 +static int s_try_stack_size=0; +static jmp_buf s_try_stack[MAX_TRY_STACK]; +jmp_buf* __poptry(void) { + if (s_try_stack_size>0) { + s_try_stack_size--; + if (s_try_stack_size == 0) + return NULL; + memcpy(&__exbuf,&s_try_stack[s_try_stack_size-1],sizeof(jmp_buf)); + // fprintf(stderr,"pop jmpbuf=%08x\n",s_try_stack[s_try_stack_size][0]); + return &s_try_stack[s_try_stack_size-1]; + } + else { + fprintf(stderr,"try stack underflow...\n"); + // return (NULL); + abort(); + } +} +void __pushtry(jmp_buf* j) { + if (s_try_stack_size0); } +#endif + +#else + +void mmu_op(uae_u32 opcode, uae_u16 /*extra*/) +{ + if ((opcode & 0xFE0) == 0x0500) { + /* PFLUSH instruction */ + flush_internals(); + } else if ((opcode & 0x0FD8) == 0x548) { + /* PTEST instruction */ + } else + op_illg(opcode); +} + +#endif + + +/* +vim:ts=4:sw=4: +*/