From: shanshe Date: Tue, 13 Apr 2021 08:32:30 +0000 (+0200) Subject: MMU and InstructionCache update (MAME's latest code) X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=e48448eff93e43846f73e0a73e02d5dc88a34eac;p=pistorm MMU and InstructionCache update (MAME's latest code) --- diff --git a/m68k.h b/m68k.h index 3c6ee81..a7328cf 100644 --- a/m68k.h +++ b/m68k.h @@ -71,6 +71,10 @@ extern "C" { #define M68K_IRQ_6 6 #define M68K_IRQ_7 7 +#define M68K_SZ_LONG 0 +#define M68K_SZ_BYTE 1 +#define M68K_SZ_WORD 2 + /* Special interrupt acknowledge values. * Use these as special returns from the interrupt acknowledge callback diff --git a/m68k_in.c b/m68k_in.c index cf2ab62..9283892 100644 --- a/m68k_in.c +++ b/m68k_in.c @@ -283,7 +283,7 @@ M68KMAKE_OPCODE_HANDLER_HEADER #include "m68kcpu.h" extern void m68040_fpu_op0(void); extern void m68040_fpu_op1(void); -extern void m68881_mmu_ops(); +extern void m68851_mmu_ops(); extern void m68881_ftrap(); /* ======================================================================== */ @@ -771,7 +771,8 @@ pack 16 mm ay7 1000...101001111 .......... . . U U U . . 13 1 pack 16 mm axy7 1000111101001111 .......... . . U U U . . 13 13 13 pack 16 mm . 1000...101001... .......... . . U U U . . 13 13 13 pea 32 . . 0100100001...... A..DXWLdx. U U U U U 6 6 5 5 5 -pflush 32 . . 1111010100011000 .......... . . . . S . . . . 4 TODO: correct timing +pflusha 32 . . 1111010100011... .......... . . . . S . . . . 4 TODO: correct timing +pflushan 32 . . 1111010100010... .......... . . . . S . . . . 4 TODO: correct timing pmmu 32 . . 1111000......... .......... . . S S S . . 8 8 8 reset 0 . . 0100111001110000 .......... S S S S S 0 0 0 0 0 ror 8 s . 1110...000011... .......... U U U U U 6 6 8 8 8 @@ -6822,7 +6823,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x003: /* TC */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_tc; return; } m68ki_exception_illegal(); @@ -6830,7 +6831,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x004: /* ITT0 */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_itt0; return; } m68ki_exception_illegal(); @@ -6838,7 +6839,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x005: /* ITT1 */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_itt1; return; } m68ki_exception_illegal(); @@ -6846,7 +6847,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x006: /* DTT0 */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_dtt0; return; } m68ki_exception_illegal(); @@ -6854,7 +6855,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x007: /* DTT1 */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_dtt1; return; } m68ki_exception_illegal(); @@ -6862,7 +6863,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x805: /* MMUSR */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_sr_040; return; } m68ki_exception_illegal(); @@ -6870,7 +6871,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x806: /* URP */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_urp_aptr; return; } m68ki_exception_illegal(); @@ -6878,7 +6879,7 @@ M68KMAKE_OP(movec, 32, cr, .) case 0x807: /* SRP */ if(CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + REG_DA[(word2 >> 12) & 15] = m68ki_cpu.mmu_srp_aptr; return; } m68ki_exception_illegal(); @@ -6977,7 +6978,16 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x003: /* TC */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_tc = REG_DA[(word2 >> 12) & 15]; + + if (m68ki_cpu.mmu_tc & 0x8000) + { + m68ki_cpu.pmmu_enabled = 1; + } + else + { + m68ki_cpu.pmmu_enabled = 0; + } return; } m68ki_exception_illegal(); @@ -6985,7 +6995,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x004: /* ITT0 */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_itt0 = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -6993,7 +7003,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x005: /* ITT1 */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_itt1 = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -7001,7 +7011,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x006: /* DTT0 */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_dtt0 = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -7009,7 +7019,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x007: /* DTT1 */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_dtt1 = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -7017,7 +7027,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x805: /* MMUSR */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_sr_040 = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -7025,7 +7035,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x806: /* URP */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_urp_aptr = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -7033,7 +7043,7 @@ M68KMAKE_OP(movec, 32, rc, .) case 0x807: /* SRP */ if (CPU_TYPE_IS_040_PLUS(CPU_TYPE)) { - /* TODO */ + m68ki_cpu.mmu_srp_aptr = REG_DA[(word2 >> 12) & 15]; return; } m68ki_exception_illegal(); @@ -8388,11 +8398,21 @@ M68KMAKE_OP(pea, 32, ., .) m68ki_push_32(ea); } -M68KMAKE_OP(pflush, 32, ., .) +M68KMAKE_OP(pflusha, 32, ., .) { - if ((CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) && (HAS_PMMU)) + if (HAS_PMMU) + { + fprintf(stderr,"68040: unhandled PFLUSHA (ir=%04x)\n", REG_IR); + return; + } + m68ki_exception_1111(); +} + +M68KMAKE_OP(pflushan, 32, ., .) +{ + if (HAS_PMMU) { - fprintf(stderr,"68040: unhandled PFLUSH\n"); + fprintf(stderr,"68040: unhandled PFLUSHAN (ir=%04x)\n", REG_IR); return; } m68ki_exception_1111(); @@ -8402,7 +8422,7 @@ M68KMAKE_OP(pmmu, 32, ., .) { if ((CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) && (HAS_PMMU)) { - m68881_mmu_ops(); + m68851_mmu_ops(); } else { diff --git a/m68kcpu.c b/m68kcpu.c index 293f937..4cc48c5 100644 --- a/m68kcpu.c +++ b/m68kcpu.c @@ -40,7 +40,7 @@ extern void m68040_fpu_op0(void); extern void m68040_fpu_op1(void); -extern void m68881_mmu_ops(); +extern void m68851_mmu_ops(); extern unsigned char m68ki_cycles[][0x10000]; extern void (*m68ki_instruction_jump_table[0x10000])(void); /* opcode handler jump table */ extern void m68ki_build_opcode_table(void); @@ -145,8 +145,8 @@ const uint8 m68ki_exception_cycle_table[5][256] = 34, /* 7: TRAPV */ 34, /* 8: Privilege Violation */ 34, /* 9: Trace */ - 34, /* 10: 1010 */ - 34, /* 11: 1111 */ + 4, /* 10: 1010 */ + 4, /* 11: 1111 */ 4, /* 12: RESERVED */ 4, /* 13: Coprocessor Protocol Violation (unemulated) */ 4, /* 14: Format Error */ @@ -635,11 +635,11 @@ unsigned int m68k_get_reg(void* context, m68k_register_t regnum) case M68K_REG_A6: return cpu->dar[14]; case M68K_REG_A7: return cpu->dar[15]; case M68K_REG_PC: return MASK_OUT_ABOVE_32(cpu->pc); - case M68K_REG_SR: return cpu->t1_flag | - cpu->t0_flag | + case M68K_REG_SR: return cpu->t1_flag | + cpu->t0_flag | (cpu->s_flag << 11) | (cpu->m_flag << 11) | - cpu->int_mask | + cpu->int_mask | ((cpu->x_flag & XFLAG_SET) >> 4) | ((cpu->n_flag & NFLAG_SET) >> 4) | ((!cpu->not_z_flag) << 2) | @@ -663,8 +663,12 @@ unsigned int m68k_get_reg(void* context, m68k_register_t regnum) { case CPU_TYPE_000: return (unsigned int)M68K_CPU_TYPE_68000; case CPU_TYPE_010: return (unsigned int)M68K_CPU_TYPE_68010; - case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020; + case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020; case CPU_TYPE_020: return (unsigned int)M68K_CPU_TYPE_68020; + case CPU_TYPE_EC030: return (unsigned int)M68K_CPU_TYPE_68EC030; + case CPU_TYPE_030: return (unsigned int)M68K_CPU_TYPE_68030; + case CPU_TYPE_EC040: return (unsigned int)M68K_CPU_TYPE_68EC040; + case CPU_TYPE_LC040: return (unsigned int)M68K_CPU_TYPE_68LC040; case CPU_TYPE_040: return (unsigned int)M68K_CPU_TYPE_68040; } return M68K_CPU_TYPE_INVALID; @@ -1096,7 +1100,7 @@ void m68k_init(void) /* The first call to this function initializes the opcode handler jump table */ if(!emulation_initialized) - { + { m68ki_build_opcode_table(); emulation_initialized = 1; } @@ -1122,8 +1126,13 @@ void m68k_pulse_bus_error(void) /* Pulse the RESET line on the CPU */ void m68k_pulse_reset(void) { - /* Disable the PMMU on reset */ + /* Disable the PMMU/HMMU on reset, if any */ m68ki_cpu.pmmu_enabled = 0; +// m68ki_cpu.hmmu_enabled = 0; + + m68ki_cpu.mmu_tc = 0; + m68ki_cpu.mmu_tt0 = 0; + m68ki_cpu.mmu_tt1 = 0; /* Clear all stop levels and eat up all remaining cycles */ CPU_STOPPED = 0; @@ -1159,6 +1168,15 @@ void m68k_pulse_reset(void) CPU_RUN_MODE = RUN_MODE_NORMAL; RESET_CYCLES = CYC_EXCEPTION[EXCEPTION_RESET]; + + /* flush the MMU's cache */ + pmmu_atc_flush(); + + if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) + { + // clear instruction cache + m68ki_ic_clear(); + } } /* Pulse the HALT line on the CPU */ diff --git a/m68kcpu.h b/m68kcpu.h index 8cd03d6..923f143 100644 --- a/m68kcpu.h +++ b/m68kcpu.h @@ -144,13 +144,16 @@ typedef uint32 uint64; } #endif /* UINT_MAX == 0xffffffff */ - - - /* ======================================================================== */ /* ============================ GENERAL DEFINES =========================== */ /* ======================================================================== */ +/* MMU constants */ +#define MMU_ATC_ENTRIES 22 // 68851 has 64, 030 has 22 + +/* instruction cache constants */ +#define M68K_IC_SIZE 128 + /* Exception Vectors handled by emulation */ #define EXCEPTION_RESET 0 #define EXCEPTION_BUS_ERROR 2 /* This one is not emulated! */ @@ -168,6 +171,7 @@ typedef uint32 uint64; #define EXCEPTION_SPURIOUS_INTERRUPT 24 #define EXCEPTION_INTERRUPT_AUTOVECTOR 24 #define EXCEPTION_TRAP_BASE 32 +#define EXCEPTION_MMU_CONFIGURATION 56 // only on 020/030 /* Function codes set by CPU during data/address bus activity */ #define FUNCTION_CODE_USER_DATA 1 @@ -177,7 +181,7 @@ typedef uint32 uint64; #define FUNCTION_CODE_CPU_SPACE 7 /* CPU types for deciding what to emulate */ -#define CPU_TYPE_000 (0x00000001) +#define CPU_TYPE_000 (0x00000001) #define CPU_TYPE_008 (0x00000002) #define CPU_TYPE_010 (0x00000004) #define CPU_TYPE_EC020 (0x00000008) @@ -199,8 +203,15 @@ typedef uint32 uint64; #define MODE_READ 0x10 #define MODE_WRITE 0 -#define RUN_MODE_NORMAL 0 -#define RUN_MODE_BERR_AERR_RESET 1 +#define RUN_MODE_NORMAL 0 +#define RUN_MODE_BERR_AERR_RESET_WSF 1 // writing the stack frame +#define RUN_MODE_BERR_AERR_RESET 2 // stack frame done + +#define M68K_CACR_IBE 0x10 // Instruction Burst Enable +#define M68K_CACR_CI 0x08 // Clear Instruction Cache +#define M68K_CACR_CEI 0x04 // Clear Entry in Instruction Cache +#define M68K_CACR_FI 0x02 // Freeze Instruction Cache +#define M68K_CACR_EI 0x01 // Enable Instruction Cache #ifndef NULL #define NULL ((void*)0) @@ -328,10 +339,10 @@ typedef uint32 uint64; #define CPU_TYPE m68ki_cpu.cpu_type #define REG_DA m68ki_cpu.dar /* easy access to data and address regs */ -#define REG_DA_SAVE m68ki_cpu.dar_save +#define REG_DA_SAVE m68ki_cpu.dar_save #define REG_D m68ki_cpu.dar #define REG_A (m68ki_cpu.dar+8) -#define REG_PPC m68ki_cpu.ppc +#define REG_PPC m68ki_cpu.ppc #define REG_PC m68ki_cpu.pc #define REG_SP_BASE m68ki_cpu.sp #define REG_USP m68ki_cpu.sp[0] @@ -387,16 +398,16 @@ typedef uint32 uint64; #define RESET_CYCLES m68ki_cpu.reset_cycles -#define CALLBACK_INT_ACK m68ki_cpu.int_ack_callback -#define CALLBACK_BKPT_ACK m68ki_cpu.bkpt_ack_callback -#define CALLBACK_RESET_INSTR m68ki_cpu.reset_instr_callback +#define CALLBACK_INT_ACK m68ki_cpu.int_ack_callback +#define CALLBACK_BKPT_ACK m68ki_cpu.bkpt_ack_callback +#define CALLBACK_RESET_INSTR m68ki_cpu.reset_instr_callback #define CALLBACK_CMPILD_INSTR m68ki_cpu.cmpild_instr_callback #define CALLBACK_RTE_INSTR m68ki_cpu.rte_instr_callback #define CALLBACK_TAS_INSTR m68ki_cpu.tas_instr_callback -#define CALLBACK_ILLG_INSTR m68ki_cpu.illg_instr_callback -#define CALLBACK_PC_CHANGED m68ki_cpu.pc_changed_callback -#define CALLBACK_SET_FC m68ki_cpu.set_fc_callback -#define CALLBACK_INSTR_HOOK m68ki_cpu.instr_hook_callback +#define CALLBACK_ILLG_INSTR m68ki_cpu.illg_instr_callback +#define CALLBACK_PC_CHANGED m68ki_cpu.pc_changed_callback +#define CALLBACK_SET_FC m68ki_cpu.set_fc_callback +#define CALLBACK_INSTR_HOOK m68ki_cpu.instr_hook_callback @@ -406,7 +417,7 @@ typedef uint32 uint64; /* Disable certain comparisons if we're not using all CPU types */ #if M68K_EMULATE_040 -#define CPU_TYPE_IS_040_PLUS(A) ((A) & (CPU_TYPE_040 | CPU_TYPE_EC040)) +#define CPU_TYPE_IS_040_PLUS(A) ((A) & (CPU_TYPE_040 | CPU_TYPE_EC040 | CPU_TYPE_LC040)) #define CPU_TYPE_IS_040_LESS(A) 1 #else #define CPU_TYPE_IS_040_PLUS(A) 0 @@ -414,7 +425,7 @@ typedef uint32 uint64; #endif #if M68K_EMULATE_030 -#define CPU_TYPE_IS_030_PLUS(A) ((A) & (CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040)) +#define CPU_TYPE_IS_030_PLUS(A) ((A) & (CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040 | CPU_TYPE_LC040)) #define CPU_TYPE_IS_030_LESS(A) 1 #else #define CPU_TYPE_IS_030_PLUS(A) 0 @@ -422,7 +433,7 @@ typedef uint32 uint64; #endif #if M68K_EMULATE_020 -#define CPU_TYPE_IS_020_PLUS(A) ((A) & (CPU_TYPE_020 | CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040)) +#define CPU_TYPE_IS_020_PLUS(A) ((A) & (CPU_TYPE_020 | CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040 | CPU_TYPE_LC040)) #define CPU_TYPE_IS_020_LESS(A) 1 #else #define CPU_TYPE_IS_020_PLUS(A) 0 @@ -430,7 +441,7 @@ typedef uint32 uint64; #endif #if M68K_EMULATE_EC020 -#define CPU_TYPE_IS_EC020_PLUS(A) ((A) & (CPU_TYPE_EC020 | CPU_TYPE_020 | CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040)) +#define CPU_TYPE_IS_EC020_PLUS(A) ((A) & (CPU_TYPE_EC020 | CPU_TYPE_020 | CPU_TYPE_030 | CPU_TYPE_EC030 | CPU_TYPE_040 | CPU_TYPE_EC040 | CPU_TYPE_LC040)) #define CPU_TYPE_IS_EC020_LESS(A) ((A) & (CPU_TYPE_000 | CPU_TYPE_010 | CPU_TYPE_EC020)) #else #define CPU_TYPE_IS_EC020_PLUS(A) CPU_TYPE_IS_020_PLUS(A) @@ -439,7 +450,7 @@ typedef uint32 uint64; #if M68K_EMULATE_010 #define CPU_TYPE_IS_010(A) ((A) == CPU_TYPE_010) -#define CPU_TYPE_IS_010_PLUS(A) ((A) & (CPU_TYPE_010 | CPU_TYPE_EC020 | CPU_TYPE_020 | CPU_TYPE_EC030 | CPU_TYPE_030 | CPU_TYPE_040 | CPU_TYPE_EC040)) +#define CPU_TYPE_IS_010_PLUS(A) ((A) & (CPU_TYPE_010 | CPU_TYPE_EC020 | CPU_TYPE_020 | CPU_TYPE_EC030 | CPU_TYPE_030 | CPU_TYPE_040 | CPU_TYPE_EC040 | CPU_TYPE_LC040)) #define CPU_TYPE_IS_010_LESS(A) ((A) & (CPU_TYPE_000 | CPU_TYPE_008 | CPU_TYPE_010)) #else #define CPU_TYPE_IS_010(A) 0 @@ -988,6 +999,32 @@ typedef struct uint mmu_tc; uint16 mmu_sr; + uint mmu_urp_aptr; /* 040 only */ + uint mmu_sr_040; + uint mmu_atc_tag[MMU_ATC_ENTRIES], mmu_atc_data[MMU_ATC_ENTRIES]; + uint mmu_atc_rr; + uint mmu_tt0, mmu_tt1; + uint mmu_itt0, mmu_itt1, mmu_dtt0, mmu_dtt1; + uint mmu_acr0, mmu_acr1, mmu_acr2, mmu_acr3; + uint mmu_last_page_entry, mmu_last_page_entry_addr; + + uint16 mmu_tmp_sr; /* temporary hack: status code for ptest and to handle write protection */ + uint16 mmu_tmp_fc; /* temporary hack: function code for the mmu (moves) */ + uint16 mmu_tmp_rw; /* temporary hack: read/write (1/0) for the mmu */ + uint8 mmu_tmp_sz; /* temporary hack: size for mmu */ + + uint mmu_tmp_buserror_address; /* temporary hack: (first) bus error address */ + uint16 mmu_tmp_buserror_occurred; /* temporary hack: flag that bus error has occurred from mmu */ + uint16 mmu_tmp_buserror_fc; /* temporary hack: (first) bus error fc */ + uint16 mmu_tmp_buserror_rw; /* temporary hack: (first) bus error rw */ + uint16 mmu_tmp_buserror_sz; /* temporary hack: (first) bus error size` */ + + uint8 mmu_tablewalk; /* set when MMU walks page tables */ + uint mmu_last_logical_addr; + uint ic_address[M68K_IC_SIZE]; /* instruction cache address data */ + uint ic_data[M68K_IC_SIZE]; /* instruction cache content data */ + uint8 ic_valid[M68K_IC_SIZE]; /* instruction cache valid flags */ + const uint8* cyc_instruction; const uint8* cyc_exception; @@ -1046,7 +1083,69 @@ extern unsigned int write_addr[8]; extern unsigned int write_upper[8]; extern unsigned char *write_data[8]; -extern uint pmmu_translate_addr(uint addr_in); +// clear the instruction cache +inline void m68ki_ic_clear() +{ + int i; + for (i=0; i< M68K_IC_SIZE; i++) { + m68ki_cpu.ic_address[i] = ~0; + } +} + +extern uint32 pmmu_translate_addr(uint32 addr_in, const uint16 rw); + +// read immediate word using the instruction cache + +static inline uint32 m68ki_ic_readimm16(uint32 address) +{ + if (m68ki_cpu.cacr & M68K_CACR_EI) + { + // 68020 series I-cache (MC68020 User's Manual, Section 4 - On-Chip Cache Memory) + if (CPU_TYPE & (CPU_TYPE_EC020 | CPU_TYPE_020)) + { + uint32 tag = (address >> 8) | (m68ki_cpu.s_flag ? 0x1000000 : 0); + int idx = (address >> 2) & 0x3f; // 1-of-64 select + + // do a cache fill if the line is invalid or the tags don't match + if ((!m68ki_cpu.ic_valid[idx]) || (m68ki_cpu.ic_address[idx] != tag)) + { + // if the cache is frozen, don't update it + if (m68ki_cpu.cacr & M68K_CACR_FI) + { + return m68k_read_immediate_16(address); + } + + uint32 data = m68ki_read_32(address & ~3); + + //printf("m68k: doing cache fill at %08x (tag %08x idx %d)\n", address, tag, idx); + + // if no buserror occurred, validate the tag + if (!m68ki_cpu.mmu_tmp_buserror_occurred) + { + m68ki_cpu.ic_address[idx] = tag; + m68ki_cpu.ic_data[idx] = data; + m68ki_cpu.ic_valid[idx] = 1; + } + else + { + return m68k_read_immediate_16(address); + } + } + + // at this point, the cache is guaranteed to be valid, either as + // a hit or because we just filled it. + if (address & 2) + { + return m68ki_cpu.ic_data[idx] & 0xffff; + } + else + { + return m68ki_cpu.ic_data[idx] >> 16; + } + } + } + return m68k_read_immediate_16(address); +} /* Handles all immediate reads, does address error check, function code setting, * and prefetching if they are enabled in m68kconf.h @@ -1054,27 +1153,28 @@ extern uint pmmu_translate_addr(uint addr_in); static inline uint m68ki_read_imm_16(void) { m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = FLAG_S | FUNCTION_CODE_USER_PROGRAM; + m68ki_cpu.mmu_tmp_rw = 1; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_WORD; m68ki_check_address_error(REG_PC, MODE_READ, FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ -#if M68K_SEPARATE_READS -/*#if M68K_EMULATE_PMMU - if (PMMU_ENABLED) - address = pmmu_translate_addr(ADDRESS_68K(CPU_PREF_ADDR)); -#endif*/ -#endif - #if M68K_EMULATE_PREFETCH { uint result; if(REG_PC != CPU_PREF_ADDR) { - CPU_PREF_ADDR = REG_PC; - CPU_PREF_DATA = m68k_read_immediate_16(ADDRESS_68K(CPU_PREF_ADDR)); + CPU_PREF_DATA = m68ki_ic_readimm16(REG_PC); + CPU_PREF_ADDR = m68ki_cpu.mmu_tmp_buserror_occurred ? ((uint32)~0) : REG_PC; } result = MASK_OUT_ABOVE_16(CPU_PREF_DATA); REG_PC += 2; - CPU_PREF_ADDR = REG_PC; - CPU_PREF_DATA = m68k_read_immediate_16(ADDRESS_68K(CPU_PREF_ADDR)); + if (!m68ki_cpu.mmu_tmp_buserror_occurred) { + // prefetch only if no bus error occurred in opcode fetch + CPU_PREF_DATA = m68ki_ic_readimm16(REG_PC); + CPU_PREF_ADDR = m68ki_cpu.mmu_tmp_buserror_occurred ? ((uint32)~0) : REG_PC; + // ignore bus error on prefetch + m68ki_cpu.mmu_tmp_buserror_occurred = 0; + } return result; } #else @@ -1088,7 +1188,7 @@ static inline uint m68ki_read_imm_16(void) } } - return m68k_read_immediate_16(ADDRESS_68K(REG_PC-2)); + return m68k_read_immediate_16(address); #endif /* M68K_EMULATE_PREFETCH */ } @@ -1101,33 +1201,35 @@ static inline uint m68ki_read_imm_8(void) static inline uint m68ki_read_imm_32(void) { #if M68K_SEPARATE_READS -/*#if M68K_EMULATE_PMMU - if (PMMU_ENABLED) - address = pmmu_translate_addr(ADDRESS_68K(CPU_PREF_ADDR)); -#endif*/ +#if M68K_EMULATE_PMMU +// if (PMMU_ENABLED) +// address = pmmu_translate_addr(address,1); +#endif #endif #if M68K_EMULATE_PREFETCH uint temp_val; m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = FLAG_S | FUNCTION_CODE_USER_PROGRAM; + m68ki_cpu.mmu_tmp_rw = 1; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_LONG; m68ki_check_address_error(REG_PC, MODE_READ, FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */ if(REG_PC != CPU_PREF_ADDR) { CPU_PREF_ADDR = REG_PC; - - CPU_PREF_DATA = m68k_read_immediate_16(ADDRESS_68K(CPU_PREF_ADDR)); + CPU_PREF_DATA = m68ki_ic_readimm16(ADDRESS_68K(CPU_PREF_ADDR)); } temp_val = MASK_OUT_ABOVE_16(CPU_PREF_DATA); REG_PC += 2; CPU_PREF_ADDR = REG_PC; - CPU_PREF_DATA = m68k_read_immediate_16(ADDRESS_68K(CPU_PREF_ADDR)); + CPU_PREF_DATA = m68ki_ic_readimm16(ADDRESS_68K(CPU_PREF_ADDR)); temp_val = MASK_OUT_ABOVE_32((temp_val << 16) | MASK_OUT_ABOVE_16(CPU_PREF_DATA)); REG_PC += 2; - CPU_PREF_ADDR = REG_PC; - CPU_PREF_DATA = m68k_read_immediate_16(ADDRESS_68K(CPU_PREF_ADDR)); + CPU_PREF_DATA = m68ki_ic_readimm16(REG_PC); + CPU_PREF_ADDR = m68ki_cpu.mmu_tmp_buserror_occurred ? ((uint32)~0) : REG_PC; return temp_val; #else @@ -1141,7 +1243,7 @@ static inline uint m68ki_read_imm_32(void) } } - return m68k_read_immediate_32(ADDRESS_68K(REG_PC-4)); + return m68k_read_immediate_32(address); #endif /* M68K_EMULATE_PREFETCH */ } @@ -1158,10 +1260,13 @@ static inline uint m68ki_read_8_fc(uint address, uint fc) { (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 1; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_BYTE; #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,1); #endif for (int i = 0; i < read_ranges; i++) { @@ -1174,13 +1279,15 @@ static inline uint m68ki_read_8_fc(uint address, uint fc) } static inline uint m68ki_read_16_fc(uint address, uint fc) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 1; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_WORD; m68ki_check_address_error_010_less(address, MODE_READ, fc); /* auto-disable (see m68kcpu.h) */ #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,1); #endif for (int i = 0; i < read_ranges; i++) { @@ -1193,13 +1300,15 @@ static inline uint m68ki_read_16_fc(uint address, uint fc) } static inline uint m68ki_read_32_fc(uint address, uint fc) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 1; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_LONG; m68ki_check_address_error_010_less(address, MODE_READ, fc); /* auto-disable (see m68kcpu.h) */ #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,1); #endif for (int i = 0; i < read_ranges; i++) { @@ -1213,12 +1322,14 @@ static inline uint m68ki_read_32_fc(uint address, uint fc) static inline void m68ki_write_8_fc(uint address, uint fc, uint value) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 0; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_BYTE; #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,0); #endif for (int i = 0; i < write_ranges; i++) { @@ -1232,13 +1343,15 @@ static inline void m68ki_write_8_fc(uint address, uint fc, uint value) } static inline void m68ki_write_16_fc(uint address, uint fc, uint value) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 0; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_WORD; m68ki_check_address_error_010_less(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,0); #endif for (int i = 0; i < write_ranges; i++) { @@ -1252,13 +1365,15 @@ static inline void m68ki_write_16_fc(uint address, uint fc, uint value) } static inline void m68ki_write_32_fc(uint address, uint fc, uint value) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 0; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_LONG; m68ki_check_address_error_010_less(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,0); #endif for (int i = 0; i < write_ranges; i++) { @@ -1272,15 +1387,22 @@ static inline void m68ki_write_32_fc(uint address, uint fc, uint value) } #if M68K_SIMULATE_PD_WRITES +/* Special call to simulate undocumented 68k behavior when move.l with a + * predecrement destination mode is executed. + * A real 68k first writes the high word to [address+2], and then writes the + * low word to [address]. + */ static inline void m68ki_write_32_pd_fc(uint address, uint fc, uint value) { - (void)fc; m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */ + m68ki_cpu.mmu_tmp_fc = fc; + m68ki_cpu.mmu_tmp_rw = 0; + m68ki_cpu.mmu_tmp_sz = M68K_SZ_LONG; m68ki_check_address_error_010_less(address, MODE_WRITE, fc); /* auto-disable (see m68kcpu.h) */ #if M68K_EMULATE_PMMU if (PMMU_ENABLED) - address = pmmu_translate_addr(address); + address = pmmu_translate_addr(address,0); #endif m68k_write_memory_32_pd(ADDRESS_68K(address), value); @@ -1785,8 +1907,12 @@ static inline void m68ki_stack_frame_1000(uint pc, uint sr, uint vector) * if the error happens at an instruction boundary. * PC stacked is address of next instruction. */ -static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc) +static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc, uint fault_address) { + int orig_rw = m68ki_cpu.mmu_tmp_buserror_rw; // this gets splatted by the following pushes, so save it now + int orig_fc = m68ki_cpu.mmu_tmp_buserror_fc; + int orig_sz = m68ki_cpu.mmu_tmp_buserror_sz; + /* INTERNAL REGISTER */ m68ki_push_16(0); @@ -1803,7 +1929,7 @@ static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc) m68ki_push_16(0); /* DATA CYCLE FAULT ADDRESS (2 words) */ - m68ki_push_32(0); + m68ki_push_32(fault_address); /* INSTRUCTION PIPE STAGE B */ m68ki_push_16(0); @@ -1812,7 +1938,9 @@ static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc) m68ki_push_16(0); /* SPECIAL STATUS REGISTER */ - m68ki_push_16(0); + // set bit for: Rerun Faulted bus Cycle, or run pending prefetch + // set FC + m68ki_push_16(0x0100 | orig_fc | orig_rw<<6 | orig_sz<<4); /* INTERNAL REGISTER */ m68ki_push_16(0); @@ -1832,8 +1960,11 @@ static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc) * if the error happens during instruction execution. * PC stacked is address of instruction in progress. */ -static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc) +static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc, uint fault_address) { + int orig_rw = m68ki_cpu.mmu_tmp_buserror_rw; // this gets splatted by the following pushes, so save it now + int orig_fc = m68ki_cpu.mmu_tmp_buserror_fc; + int orig_sz = m68ki_cpu.mmu_tmp_buserror_sz; /* INTERNAL REGISTERS (18 words) */ m68ki_push_32(0); m68ki_push_32(0); @@ -1875,7 +2006,7 @@ static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc) m68ki_push_16(0); /* DATA CYCLE FAULT ADDRESS (2 words) */ - m68ki_push_32(0); + m68ki_push_32(fault_address); /* INSTRUCTION PIPE STAGE B */ m68ki_push_16(0); @@ -1884,7 +2015,7 @@ static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc) m68ki_push_16(0); /* SPECIAL STATUS REGISTER */ - m68ki_push_16(0); + m68ki_push_16(0x0100 | orig_fc | (orig_rw<<6) | (orig_sz<<4)); /* INTERNAL REGISTER */ m68ki_push_16(0); @@ -1899,6 +2030,48 @@ static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc) m68ki_push_16(sr); } +/* Type 7 stack frame (access fault). + * This is used by the 68040 for bus fault and mmu trap + * 30 words + */ +static inline void m68ki_stack_frame_0111(uint sr, uint vector, uint pc, uint fault_address, uint8 in_mmu) +{ + int orig_rw = m68ki_cpu.mmu_tmp_buserror_rw; // this gets splatted by the following pushes, so save it now + int orig_fc = m68ki_cpu.mmu_tmp_buserror_fc; + + /* INTERNAL REGISTERS (18 words) */ + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + m68ki_push_32(0); + + /* FAULT ADDRESS (2 words) */ + m68ki_push_32(fault_address); + + /* INTERNAL REGISTERS (3 words) */ + m68ki_push_32(0); + m68ki_push_16(0); + + /* SPECIAL STATUS REGISTER (1 word) */ + m68ki_push_16((in_mmu ? 0x400 : 0) | orig_fc | (orig_rw<<8)); + + /* EFFECTIVE ADDRESS (2 words) */ + m68ki_push_32(fault_address); + + /* 0111, VECTOR OFFSET (1 word) */ + m68ki_push_16(0x7000 | (vector<<2)); + + /* PROGRAM COUNTER (2 words) */ + m68ki_push_32(pc); + + /* STATUS REGISTER (1 word) */ + m68ki_push_16(sr); +} /* Used for Group 2 exceptions. * These stack a type 2 frame on the 020. @@ -1990,7 +2163,7 @@ static inline void m68ki_exception_bus_error(void) */ if(CPU_RUN_MODE == RUN_MODE_BERR_AERR_RESET) { -m68k_read_memory_8(0x00ffff01); + m68k_read_memory_8(0x00ffff01); CPU_STOPPED = STOP_LEVEL_HALT; return; } @@ -2019,7 +2192,7 @@ static inline void m68ki_exception_1010(void) #if M68K_LOG_1010_1111 == OPT_ON M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: called 1010 instruction %04x (%s)\n", m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, - m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC),CPU_TYPE))); #endif sr = m68ki_init_exception(); @@ -2038,7 +2211,7 @@ static inline void m68ki_exception_1111(void) #if M68K_LOG_1010_1111 == OPT_ON M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: called 1111 instruction %04x (%s)\n", m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, - m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC),CPU_TYPE))); #endif sr = m68ki_init_exception(); @@ -2060,7 +2233,7 @@ static inline void m68ki_exception_illegal(void) M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: illegal instruction %04x (%s)\n", m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PPC), REG_IR, - m68ki_disassemble_quick(ADDRESS_68K(REG_PPC)))); + m68ki_disassemble_quick(ADDRESS_68K(REG_PPC),CPU_TYPE))); if (m68ki_illg_callback(REG_IR)) return; @@ -2094,15 +2267,15 @@ static inline void m68ki_exception_format_error(void) /* Exception for address error */ static inline void m68ki_exception_address_error(void) { - uint sr = m68ki_init_exception(); + uint32 sr = m68ki_init_exception(); /* If we were processing a bus error, address error, or reset, * this is a catastrophic failure. * Halt the CPU */ - if(CPU_RUN_MODE == RUN_MODE_BERR_AERR_RESET) + if(CPU_RUN_MODE == RUN_MODE_BERR_AERR_RESET_WSF) { -m68k_read_memory_8(0x00ffff01); + m68k_read_memory_8(0x00ffff01); CPU_STOPPED = STOP_LEVEL_HALT; return; } diff --git a/m68kmmu.h b/m68kmmu.h index faba371..9085314 100644 --- a/m68kmmu.h +++ b/m68kmmu.h @@ -7,193 +7,1233 @@ Visit http://mamedev.org for licensing and usage restrictions. */ -/* - pmmu_translate_addr: perform 68851/68030-style PMMU address translation -*/ -uint pmmu_translate_addr(uint addr_in) +// MMU status register bit definitions + +#if 0 +#define MMULOG(A) printf A +#else +#define MMULOG(...) +#endif +#if 1 +#define logerror printf +#else +#define logerror(...) +#endif + +// MMU SR register fields +#define M68K_MMU_SR_BUS_ERROR 0x8000 +#define M68K_MMU_SR_SUPERVISOR_ONLY 0x2000 +#define M68K_MMU_SR_WRITE_PROTECT 0x0800 +#define M68K_MMU_SR_INVALID 0x0400 +#define M68K_MMU_SR_MODIFIED 0x0200 +#define M68K_MMU_SR_TRANSPARENT 0x0040 + +// MMU translation table descriptor field definitions +#define M68K_MMU_DF_DT 0x00000003 +#define M68K_MMU_DF_DT_INVALID 0x00000000 +#define M68K_MMU_DF_DT_PAGE 0x00000001 +#define M68K_MMU_DF_DT_TABLE_4BYTE 0x00000002 +#define M68K_MMU_DF_DT_TABLE_8BYTE 0x00000003 +#define M68K_MMU_DF_WP 0x00000004 +#define M68K_MMU_DF_USED 0x00000008 +#define M68K_MMU_DF_MODIFIED 0x00000010 +#define M68K_MMU_DF_CI 0x00000040 +#define M68K_MMU_DF_SUPERVISOR 0x00000100 +#define M68K_MMU_DF_ADDR_MASK 0xfffffff0 +#define M68K_MMU_DF_IND_ADDR_MASK 0xfffffffc + +// MMU ATC Fields +#define M68K_MMU_ATC_BUSERROR 0x08000000 +#define M68K_MMU_ATC_CACHE_IN 0x04000000 +#define M68K_MMU_ATC_WRITE_PR 0x02000000 +#define M68K_MMU_ATC_MODIFIED 0x01000000 +#define M68K_MMU_ATC_MASK 0x00ffffff +#define M68K_MMU_ATC_SHIFT 8 +#define M68K_MMU_ATC_VALID 0x08000000 + +// MMU Translation Control register +#define M68K_MMU_TC_SRE 0x02000000 +#define M68K_MMU_TC_FCL 0x01000000 + +// TT register +#define M68K_MMU_TT_ENABLE 0x8000 + +#define m_side_effects_disabled 0 + +/* decodes the effective address */ +uint32 DECODE_EA_32(int ea) { - uint32 addr_out, tbl_entry = 0, tbl_entry2, tamode = 0, tbmode = 0, tcmode = 0; - uint root_aptr, root_limit, tofs, is, abits, bbits, cbits; - uint resolved, tptr, shift; + int mode = (ea >> 3) & 0x7; + int reg = (ea & 0x7); - resolved = 0; - addr_out = addr_in; + switch (mode) + { + case 2: // (An) + { + return REG_A[reg]; + } + case 3: // (An)+ + { + uint32 ea = EA_AY_PI_32(); + return ea; + } + case 5: // (d16, An) + { + uint32 ea = EA_AY_DI_32(); + return ea; + } + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_32(); + return ea; + } + case 7: + { + switch (reg) + { + case 0: // (xxx).W + { + uint32 ea = OPER_I_16(); + return ea; + } + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + return ea; + } + case 2: // (d16, PC) + { + uint32 ea = EA_PCDI_32(); + return ea; + } + default: fatalerror("m68k: DECODE_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC); + } + break; + } + default: fatalerror("m68k: DECODE_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC); + } + return 0; +} - // if SRP is enabled and we're in supervisor mode, use it - if ((m68ki_cpu.mmu_tc & 0x02000000) && (m68ki_get_sr() & 0x2000)) +void pmmu_set_buserror(uint32 addr_in) +{ + if (!m_side_effects_disabled && ++m68ki_cpu.mmu_tmp_buserror_occurred == 1) { - root_aptr = m68ki_cpu.mmu_srp_aptr; - root_limit = m68ki_cpu.mmu_srp_limit; + m68ki_cpu.mmu_tmp_buserror_address = addr_in; + m68ki_cpu.mmu_tmp_buserror_rw = m68ki_cpu.mmu_tmp_rw; + m68ki_cpu.mmu_tmp_buserror_fc = m68ki_cpu.mmu_tmp_fc; + m68ki_cpu.mmu_tmp_buserror_sz = m68ki_cpu.mmu_tmp_sz; } - else // else use the CRP +} + + +// pmmu_atc_add: adds this address to the ATC +void pmmu_atc_add(uint32 logical, uint32 physical, int fc, int rw) +{ + // get page size (i.e. # of bits to ignore); is 10 for Apollo + int ps = (m68ki_cpu.mmu_tc >> 20) & 0xf; + uint32 atc_tag = M68K_MMU_ATC_VALID | ((fc & 7) << 24) | ((logical >> ps) << (ps - 8)); + uint32 atc_data = (physical >> ps) << (ps - 8); + + if (m68ki_cpu.mmu_tmp_sr & (M68K_MMU_SR_BUS_ERROR|M68K_MMU_SR_INVALID|M68K_MMU_SR_SUPERVISOR_ONLY)) { - root_aptr = m68ki_cpu.mmu_crp_aptr; - root_limit = m68ki_cpu.mmu_crp_limit; + atc_data |= M68K_MMU_ATC_BUSERROR; } - // get initial shift (# of top bits to ignore) - is = (m68ki_cpu.mmu_tc>>16) & 0xf; - abits = (m68ki_cpu.mmu_tc>>12)&0xf; - bbits = (m68ki_cpu.mmu_tc>>8)&0xf; - cbits = (m68ki_cpu.mmu_tc>>4)&0xf; + if (m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT) + { + atc_data |= M68K_MMU_ATC_WRITE_PR; + } -// fprintf(stderr,"PMMU: tcr %08x limit %08x aptr %08x is %x abits %d bbits %d cbits %d\n", m68ki_cpu.mmu_tc, root_limit, root_aptr, is, abits, bbits, cbits); + if (!rw && !(m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT)) + { + atc_data |= M68K_MMU_ATC_MODIFIED; + } - // get table A offset - tofs = (addr_in<>(32-abits); + // first see if this is already in the cache + for (int i = 0; i < MMU_ATC_ENTRIES; i++) + { + // if tag bits and function code match, don't add + if (m68ki_cpu.mmu_atc_tag[i] == atc_tag) + { + MMULOG(("%s: hit, old %08x new %08x\n", __func__, m68ki_cpu.mmu_atc_data[i], atc_data)); + m68ki_cpu.mmu_atc_data[i] = atc_data; + return; + } + } - // find out what format table A is - switch (root_limit & 3) + // find an open entry + int found = -1; + for (int i = 0; i < MMU_ATC_ENTRIES; i++) { - case 0: // invalid, should cause MMU exception - case 1: // page descriptor, should cause direct mapping - fatalerror("680x0 PMMU: Unhandled root mode\n"); + if (!(m68ki_cpu.mmu_atc_tag[i] & M68K_MMU_ATC_VALID)) + { + found = i; break; + } + } - case 2: // valid 4 byte descriptors - tofs *= 4; -// fprintf(stderr,"PMMU: reading table A entry at %08x\n", tofs + (root_aptr & 0xfffffffc)); - tbl_entry = m68k_read_memory_32( tofs + (root_aptr & 0xfffffffc)); - tamode = tbl_entry & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tamode, tofs); - break; + // did we find an entry? steal one by round-robin then + if (found == -1) + { + found = m68ki_cpu.mmu_atc_rr++; - case 3: // valid 8 byte descriptors - tofs *= 8; -// fprintf(stderr,"PMMU: reading table A entries at %08x\n", tofs + (root_aptr & 0xfffffffc)); - tbl_entry2 = m68k_read_memory_32( tofs + (root_aptr & 0xfffffffc)); - tbl_entry = m68k_read_memory_32( tofs + (root_aptr & 0xfffffffc)+4); - tamode = tbl_entry2 & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tamode, tofs); - break; + if (m68ki_cpu.mmu_atc_rr >= MMU_ATC_ENTRIES) + { + m68ki_cpu.mmu_atc_rr = 0; + } } - // get table B offset and pointer - tofs = (addr_in<<(is+abits))>>(32-bbits); - tptr = tbl_entry & 0xfffffff0; + // add the entry + MMULOG(("ATC[%2d] add: log %08x -> phys %08x (fc=%d) data=%08x\n", + found, (logical >> ps) << ps, (physical >> ps) << ps, fc, atc_data)); + m68ki_cpu.mmu_atc_tag[found] = atc_tag; + m68ki_cpu.mmu_atc_data[found] = atc_data; +} + +// pmmu_atc_flush: flush entire ATC +// 7fff0003 001ffd10 80f05750 is what should load +void pmmu_atc_flush() +{ + MMULOG(("ATC flush: pc=%08x\n", m68ki_cpu.ppc)); +// std::fill(std::begin(m68ki_cpu.mmu_atc_tag), std::end(m68ki_cpu.mmu_atc_tag), 0); + for(int i=0;i> 5) & 7; + unsigned int fc = fc_from_modes(modes) & fcmask; + unsigned int ps = (m68ki_cpu.mmu_tc >> 20) & 0xf; + unsigned int mode = (modes >> 10) & 7; + uint32 ea; + + switch (mode) { - case 0: // invalid, should cause MMU exception - fatalerror("680x0 PMMU: Unhandled Table A mode %d (addr_in %08x)\n", tamode, addr_in); - break; + case 1: // PFLUSHA + MMULOG(("PFLUSHA: mode %d\n", mode)); + pmmu_atc_flush(); + break; - case 2: // 4-byte table B descriptor - tofs *= 4; -// fprintf(stderr,"PMMU: reading table B entry at %08x\n", tofs + tptr); - tbl_entry = m68k_read_memory_32( tofs + tptr); - tbmode = tbl_entry & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); - break; + case 4: // flush by fc + MMULOG(("flush by fc: %d, mask %d\n", fc, fcmask)); + for(int i=0,e;i> 24) & fcmask) == fc) + { + MMULOG(("flushing entry %08x\n", e)); + m68ki_cpu.mmu_atc_tag[i] = 0; + } + } + break; - case 3: // 8-byte table B descriptor - tofs *= 8; -// fprintf(stderr,"PMMU: reading table B entries at %08x\n", tofs + tptr); - tbl_entry2 = m68k_read_memory_32( tofs + tptr); - tbl_entry = m68k_read_memory_32( tofs + tptr + 4); - tbmode = tbl_entry2 & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); - break; + case 6: // flush by fc + ea - case 1: // early termination descriptor - tbl_entry &= 0xffffff00; + ea = DECODE_EA_32(m68ki_cpu.ir); + MMULOG(("flush by fc/ea: fc %d, mask %d, ea %08x\n", fc, fcmask, ea)); + for(unsigned int i=0,e;i> 24) & fcmask) == fc) && +// (((e >> ps) << (ps - 8)) == ((ea >> ps) << (ps - 8)))) + ( (e << ps) == (ea >> 8 << ps) )) + { + MMULOG(("flushing entry %08x\n", e)); + m68ki_cpu.mmu_atc_tag[i] = 0; + } + } + break; - shift = is+abits; - addr_out = ((addr_in<>shift) + tbl_entry; - resolved = 1; - break; + default: + logerror("PFLUSH mode %d not supported\n", mode); + break; } +} - // if table A wasn't early-out, continue to process table B - if (!resolved) +//template +uint16 pmmu_atc_lookup(uint32 addr_in, int fc, uint16 rw, + uint32 *addr_out,int ptest) +{ + MMULOG(("%s: LOOKUP addr_in=%08x, fc=%d, ptest=%d, rw=%d\n", __func__, addr_in, fc, ptest,rw)); + unsigned int ps = (m68ki_cpu.mmu_tc >> 20) & 0xf; + uint32 atc_tag = M68K_MMU_ATC_VALID | ((fc & 7) << 24) | ((addr_in >> ps) << (ps - 8)); + + for (int i = 0; i < MMU_ATC_ENTRIES; i++) { - // get table C offset and pointer - tofs = (addr_in<<(is+abits+bbits))>>(32-cbits); - tptr = tbl_entry & 0xfffffff0; - switch (tbmode) + if (m68ki_cpu.mmu_atc_tag[i] != atc_tag) + { + continue; + } + + uint32 atc_data = m68ki_cpu.mmu_atc_data[i]; + + if (!ptest && !rw) + { + // According to MC86030UM: + // "If the M bit is clear and a write access to this logical + // address is attempted, the MC68030 aborts the access and initiates a table + // search, setting the M bit in the page descriptor, invalidating the old ATC + // entry, and creating a new entry with the M bit set. + if (!(atc_data & M68K_MMU_ATC_MODIFIED)) + { + m68ki_cpu.mmu_atc_tag[i] = 0; + continue; + } + } + + m68ki_cpu.mmu_tmp_sr = 0; + if (atc_data & M68K_MMU_ATC_MODIFIED) + { + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_MODIFIED; + } + + if (atc_data & M68K_MMU_ATC_WRITE_PR) { - case 0: // invalid, should cause MMU exception - fatalerror("680x0 PMMU: Unhandled Table B mode %d (addr_in %08x PC %x)\n", tbmode, addr_in, REG_PC); + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_WRITE_PROTECT; + } + + if (atc_data & M68K_MMU_ATC_BUSERROR) + { + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_BUS_ERROR|M68K_MMU_SR_INVALID; + } + *addr_out = (atc_data << 8) | (addr_in & ~(((uint32)~0) << ps)); + MMULOG(("%s: addr_in=%08x, addr_out=%08x, MMU SR %04x\n", + __func__, addr_in, *addr_out, m68ki_cpu.mmu_tmp_sr)); + return 1; + } + MMULOG(("%s: lookup failed\n", __func__)); + if (ptest) + { + m68ki_cpu.mmu_tmp_sr = M68K_MMU_SR_INVALID; + } + return 0; +} + +uint16 pmmu_match_tt(uint32 addr_in, int fc, uint32 tt, uint16 rw) +{ + if (!(tt & M68K_MMU_TT_ENABLE)) + { + return 0; + } + + // transparent translation enabled + uint32 address_base = tt & 0xff000000; + uint32 address_mask = ((tt << 8) & 0xff000000) ^ 0xff000000; + uint32 fcmask = (~tt) & 7; + uint32 fcbits = (tt >> 4) & 7; + uint16 rwmask = !!(~tt & 0x100); + uint16 rwbit = !!(tt & 0x200); + + if ((addr_in & address_mask) != (address_base & address_mask)) + { + return 0; + } + + if ((fc & fcmask) != (fcbits & fcmask)) + { + return 0; + } + + if ((rw & rwmask) != (rwbit & rwmask)) + { + return 0; + } + + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_TRANSPARENT; + return 1; +} + +void update_descriptor(uint32 tptr, int type, uint32 entry, int16 rw) +{ + if (type == M68K_MMU_DF_DT_PAGE && !rw && + !(entry & M68K_MMU_DF_MODIFIED) && + !(entry & M68K_MMU_DF_WP)) + { + MMULOG(("%s: set M+U at %08x\n", __func__, tptr)); + m68k_write_memory_32(tptr, entry | M68K_MMU_DF_USED | M68K_MMU_DF_MODIFIED); + } + else if (type != M68K_MMU_DF_DT_INVALID && !(entry & M68K_MMU_DF_USED)) + { + MMULOG(("%s: set U at %08x\n", __func__, tptr)); + m68k_write_memory_32(tptr, entry | M68K_MMU_DF_USED); + } +} + + +//template +void update_sr(int type, uint32 tbl_entry, int fc,uint16 _long) +{ + if (m_side_effects_disabled) + { + return; + } + + switch(type) + { + case M68K_MMU_DF_DT_INVALID: + // Invalid has no flags + break; + + case M68K_MMU_DF_DT_PAGE: + if (tbl_entry & M68K_MMU_DF_MODIFIED) + { + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_MODIFIED; + } + /* FALLTHROUGH */ + + case M68K_MMU_DF_DT_TABLE_4BYTE: + /* FALLTHROUGH */ + + case M68K_MMU_DF_DT_TABLE_8BYTE: + + if (tbl_entry & M68K_MMU_DF_WP) + { + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_WRITE_PROTECT; + } + + if (_long && !(fc & 4) && (tbl_entry & M68K_MMU_DF_SUPERVISOR)) + { + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_SUPERVISOR_ONLY; + } + break; + default: + break; + } +} + +//template +uint16 pmmu_walk_tables(uint32 addr_in, int type, uint32 table, uint8 fc, + int limit, uint16 rw, uint32 *addr_out, int ptest) +{ + int level = 0; + uint32 bits = m68ki_cpu.mmu_tc & 0xffff; + int pagesize = (m68ki_cpu.mmu_tc >> 20) & 0xf; + int is = (m68ki_cpu.mmu_tc >> 16) & 0xf; + int bitpos = 12; + int resolved = 0; + int pageshift = is; + + addr_in <<= is; + + m68ki_cpu.mmu_tablewalk = 1; + + if (m68ki_cpu.mmu_tc & M68K_MMU_TC_FCL) + { + bitpos = 16; + } + + do + { + int indexbits = (bits >> bitpos) & 0xf; + int table_index = (bitpos == 16) ? fc : (addr_in >> (32 - indexbits)); + bitpos -= 4; + uint16 indirect = (!bitpos || !(bits >> bitpos)) && indexbits; + uint32 tbl_entry, tbl_entry2; + + MMULOG(("%s: type %d, table %08x, addr_in %08x, indexbits %d, pageshift %d, indirect %d table_index %08x, rw=%d fc=%d\n", + __func__, type, table, addr_in, indexbits, pageshift, indirect, table_index, rw, fc)); + + switch(type) + { + case M68K_MMU_DF_DT_INVALID: // invalid, will cause MMU exception + m68ki_cpu.mmu_tmp_sr = M68K_MMU_SR_INVALID; + MMULOG(("PMMU: DT0 PC=%x (addr_in %08x -> %08x)\n", m68ki_cpu.ppc, addr_in, *addr_out)); + resolved = 1; break; - case 2: // 4-byte table C descriptor - tofs *= 4; -// fprintf(stderr,"PMMU: reading table C entry at %08x\n", tofs + tptr); - tbl_entry = m68k_read_memory_32(tofs + tptr); - tcmode = tbl_entry & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); + case M68K_MMU_DF_DT_PAGE: // page descriptor, will cause direct mapping + if (!ptest) + { + table &= ((uint32)~0) << pagesize; + *addr_out = table + (addr_in >> pageshift); + } + resolved = 1; break; - case 3: // 8-byte table C descriptor - tofs *= 8; -// fprintf(stderr,"PMMU: reading table C entries at %08x\n", tofs + tptr); - tbl_entry2 = m68k_read_memory_32(tofs + tptr); - tbl_entry = m68k_read_memory_32(tofs + tptr + 4); - tcmode = tbl_entry2 & 3; -// fprintf(stderr,"PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); + case M68K_MMU_DF_DT_TABLE_4BYTE: // valid 4 byte descriptors + level++; + *addr_out = table + (table_index << 2); + tbl_entry = m68k_read_memory_32(*addr_out); + type = tbl_entry & M68K_MMU_DF_DT; + + if (indirect && (type == 2 || type == 3)) + { + level++; + MMULOG(("SHORT INDIRECT DESC: %08x\n", tbl_entry)); + *addr_out = tbl_entry & M68K_MMU_DF_IND_ADDR_MASK; + tbl_entry = m68k_read_memory_32(*addr_out); + type = tbl_entry & M68K_MMU_DF_DT; + } + + MMULOG(("SHORT DESC: %08x\n", tbl_entry)); + table = tbl_entry & M68K_MMU_DF_ADDR_MASK; + if (!m_side_effects_disabled) + { + update_sr(type, tbl_entry, fc,0); + if (!ptest) + { + update_descriptor(*addr_out, type, tbl_entry, rw); + } + } break; - case 1: // termination descriptor - tbl_entry &= 0xffffff00; + case M68K_MMU_DF_DT_TABLE_8BYTE: // valid 8 byte descriptors + level++; + *addr_out = table + (table_index << 3); + tbl_entry = m68k_read_memory_32(*addr_out); + tbl_entry2 = m68k_read_memory_32((*addr_out) + 4); + type = tbl_entry & M68K_MMU_DF_DT; + + if (indirect && (type == 2 || type == 3)) + { + level++; + MMULOG(("LONG INDIRECT DESC: %08x%08x\n", tbl_entry, tbl_entry2)); + *addr_out = tbl_entry2 & M68K_MMU_DF_IND_ADDR_MASK; + tbl_entry = m68k_read_memory_32(*addr_out); + tbl_entry2 = m68k_read_memory_32(*addr_out); + type = tbl_entry & M68K_MMU_DF_DT; + } + + MMULOG(("LONG DESC: %08x %08x\n", tbl_entry, tbl_entry2)); + table = tbl_entry2 & M68K_MMU_DF_ADDR_MASK; + if (!m_side_effects_disabled) + { + update_sr(type, tbl_entry, fc,1); + if (!ptest) + { + update_descriptor(*addr_out, type, tbl_entry, rw); + } + } + break; + } + + if (m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_BUS_ERROR) + { + // Bus error during page table walking is always fatal + resolved = 1; + break; + } + + if (!ptest && !m_side_effects_disabled) + { + if (!rw && (m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT)) + { + resolved = 1; + break; + } - shift = is+abits+bbits; - addr_out = ((addr_in<>shift) + tbl_entry; + if (!(fc & 4) && (m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_SUPERVISOR_ONLY)) + { resolved = 1; break; + } + } + addr_in <<= indexbits; + pageshift += indexbits; + } while(level < limit && !resolved); + + + m68ki_cpu.mmu_tmp_sr &= 0xfff0; + m68ki_cpu.mmu_tmp_sr |= level; + MMULOG(("MMU SR after walk: %04X\n", m68ki_cpu.mmu_tmp_sr)); + m68ki_cpu.mmu_tablewalk = 0; + return resolved; +} + +// pmmu_translate_addr_with_fc: perform 68851/68030-style PMMU address translation +//template +uint32 pmmu_translate_addr_with_fc(uint32 addr_in, uint8 fc, uint16 rw, int limit,int ptest,int pload) +{ + uint32 addr_out = 0; + + + MMULOG(("%s: addr_in=%08x, fc=%d, ptest=%d, rw=%d, limit=%d, pload=%d\n", + __func__, addr_in, fc, ptest, rw, limit, pload)); + m68ki_cpu.mmu_tmp_sr = 0; + + m68ki_cpu.mmu_last_logical_addr = addr_in; + + if (pmmu_match_tt(addr_in, fc, m68ki_cpu.mmu_tt0, rw) || + pmmu_match_tt(addr_in, fc, m68ki_cpu.mmu_tt1, rw) || + fc == 7) + { + return addr_in; } - if (!resolved) + if (ptest && limit == 0) { - switch (tcmode) + pmmu_atc_lookup(addr_in, fc, rw, &addr_out, 1); + return addr_out; + } + + if (!ptest && !pload && pmmu_atc_lookup(addr_in, fc, rw, &addr_out, 0)) + { + if ((m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_BUS_ERROR) || (!rw && (m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT))) { - case 0: // invalid, should cause MMU exception - case 2: // 4-byte ??? descriptor - case 3: // 8-byte ??? descriptor - fatalerror("680x0 PMMU: Unhandled Table B mode %d (addr_in %08x PC %x)\n", tbmode, addr_in, REG_PC); - break; + MMULOG(("set atc hit buserror: addr_in=%08x, addr_out=%x, rw=%x, fc=%d, sz=%d\n", + addr_in, addr_out, m68ki_cpu.mmu_tmp_rw, m68ki_cpu.mmu_tmp_fc, m68ki_cpu.mmu_tmp_sz)); + pmmu_set_buserror(addr_in); + } + return addr_out; + } + + int type; + uint32 tbl_addr; + // if SRP is enabled and we're in supervisor mode, use it + if ((m68ki_cpu.mmu_tc & M68K_MMU_TC_SRE) && (fc & 4)) + { + tbl_addr = m68ki_cpu.mmu_srp_aptr & M68K_MMU_DF_ADDR_MASK; + type = m68ki_cpu.mmu_srp_limit & M68K_MMU_DF_DT; + } + else // else use the CRP + { + tbl_addr = m68ki_cpu.mmu_crp_aptr & M68K_MMU_DF_ADDR_MASK; + type = m68ki_cpu.mmu_crp_limit & M68K_MMU_DF_DT; + } - case 1: // termination descriptor - tbl_entry &= 0xffffff00; + if (!pmmu_walk_tables(addr_in, type, tbl_addr, fc, limit, rw, &addr_out, ptest)) + { + MMULOG(("%s: addr_in=%08x, type=%x, tbl_addr=%x, fc=%d, limit=%x, rw=%x, addr_out=%x, ptest=%d\n", + __func__, addr_in, type, tbl_addr, fc, limit, rw, addr_out, ptest)); + fatalerror("Table walk did not resolve\n"); + } - shift = is+abits+bbits+cbits; - addr_out = ((addr_in<>shift) + tbl_entry; - resolved = 1; + if (ptest) + { + return addr_out; + } + + if ((m68ki_cpu.mmu_tmp_sr & (M68K_MMU_SR_INVALID|M68K_MMU_SR_SUPERVISOR_ONLY)) || + ((m68ki_cpu.mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT) && !rw)) + { + + if (!pload) + { + MMULOG(("%s: set buserror (SR %04X)\n", __func__, m68ki_cpu.mmu_tmp_sr)); + pmmu_set_buserror(addr_in); + } + } + + // it seems like at least the 68030 sets the M bit in the MMU SR + // if the root descriptor is of PAGE type, so do a logical and + // between RW and the root type + if (!m_side_effects_disabled) + { + pmmu_atc_add(addr_in, addr_out, fc, rw && type != 1); + } + MMULOG(("PMMU: [%08x] => [%08x] (SR %04x)\n", addr_in, addr_out, m68ki_cpu.mmu_tmp_sr)); + return addr_out; +} + +// FC bits: 2 = supervisor, 1 = program, 0 = data +// the 68040 is a subset of the 68851 and 68030 PMMUs - the page table sizes are fixed, there is no early termination, etc, etc. +uint32 pmmu_translate_addr_with_fc_040(uint32 addr_in, uint8 fc, uint8 ptest) +{ + uint32 addr_out, tt0, tt1; + + addr_out = addr_in; + m68ki_cpu.mmu_tmp_sr = 0; + + // transparent translation registers are always in force even if the PMMU itself is disabled + // they don't do much in emulation because we never write out of order, but the write-protect and cache control features + // are emulatable, and apparently transparent translation regions skip the page table lookup. + if (fc & 1) // data, use DTT0/DTT1 + { + tt0 = m68ki_cpu.mmu_dtt0; + tt1 = m68ki_cpu.mmu_dtt1; + } + else if (fc & 2) // program, use ITT0/ITT1 + { + tt0 = m68ki_cpu.mmu_itt0; + tt1 = m68ki_cpu.mmu_itt1; + } + else + { + fatalerror("68040: function code %d is neither data nor program!\n", fc & 7); + } + + if (tt0 & M68K_MMU_TT_ENABLE) + { + int fcmask[4] = { 4, 4, 0, 0 }; + int fcmatch[4] = { 0, 4, 0, 0 }; + uint32 mask = (tt0 >> 16) & 0xff; + mask ^= 0xff; + mask <<= 24; + + if ((addr_in & mask) == (tt0 & mask) && (fc & fcmask[(tt0 >> 13) & 3]) == fcmatch[(tt0 >> 13) & 3]) + { + MMULOG(("TT0 match on address %08x (TT0 = %08x, mask = %08x)\n", addr_in, tt0, mask)); + if ((tt0 & 4) && !m68ki_cpu.mmu_tmp_rw && !ptest) // write protect? + { + pmmu_set_buserror(addr_in); + } + + return addr_in; + } + } + + if (tt1 & M68K_MMU_TT_ENABLE) + { + static int fcmask[4] = { 4, 4, 0, 0 }; + static int fcmatch[4] = { 0, 4, 0, 0 }; + uint32 mask = (tt1 >> 16) & 0xff; + mask ^= 0xff; + mask <<= 24; + + if ((addr_in & mask) == (tt1 & mask) && (fc & fcmask[(tt1 >> 13) & 3]) == fcmatch[(tt1 >> 13) & 3]) + { + MMULOG(("TT1 match on address %08x (TT0 = %08x, mask = %08x)\n", addr_in, tt1, mask)); + if ((tt1 & 4) && !m68ki_cpu.mmu_tmp_rw && !ptest) // write protect? + { + pmmu_set_buserror(addr_in); + } + + return addr_in; + } + } + + if (m68ki_cpu.pmmu_enabled) + { + uint32 root_idx = (addr_in >> 25) & 0x7f; + uint32 ptr_idx = (addr_in >> 18) & 0x7f; + uint32 page_idx, page; + uint32 root_ptr, pointer_ptr, page_ptr; + uint32 root_entry, pointer_entry, page_entry; + + // select supervisor or user root pointer + if (fc & 4) + { + root_ptr = m68ki_cpu.mmu_srp_aptr + (root_idx<<2); + } + else + { + root_ptr = m68ki_cpu.mmu_urp_aptr + (root_idx<<2); + } + + // get the root entry + root_entry = m68k_read_memory_32(root_ptr); + + // is UDT marked valid? + if (root_entry & 2) + { + // we're accessing through this root entry, so set the U bit + if ((!(root_entry & 0x8)) && (!ptest) && !m_side_effects_disabled) + { + root_entry |= 0x8; + m68k_write_memory_32(root_ptr, root_entry); + } + + // PTEST: any write protect bits set in the search tree will set W in SR + if ((ptest) && (root_entry & 4)) + { + m68ki_cpu.mmu_tmp_sr |= 4; + } + + pointer_ptr = (root_entry & ~0x1ff) + (ptr_idx<<2); + pointer_entry = m68k_read_memory_32(pointer_ptr); + + // PTEST: any write protect bits set in the search tree will set W in SR + if ((ptest) && (pointer_entry & 4)) + { + m68ki_cpu.mmu_tmp_sr |= 4; + } + + // update U bit on this pointer entry too + if ((!(pointer_entry & 0x8)) && (!ptest) && !m_side_effects_disabled) + { + pointer_entry |= 0x8; + m68k_write_memory_32(pointer_ptr, pointer_entry); + } + + MMULOG(("pointer entry = %08x\n", pointer_entry)); + + // write protected by the root or pointer entries? + if ((((root_entry & 4) && !m68ki_cpu.mmu_tmp_rw) || ((pointer_entry & 4) && !m68ki_cpu.mmu_tmp_rw)) && !ptest) + { + pmmu_set_buserror(addr_in); + return addr_in; + } + + // is UDT valid on the pointer entry? + if (!(pointer_entry & 2) && !ptest) + { + logerror("Invalid pointer entry! PC=%x, addr=%x\n", m68ki_cpu.ppc, addr_in); + pmmu_set_buserror(addr_in); + return addr_in; + } + + // (fall out of these ifs into the page lookup below) + } + else // throw an error + { + logerror("Invalid root entry! PC=%x, addr=%x\n", m68ki_cpu.ppc, addr_in); + + if (!ptest) + { + pmmu_set_buserror(addr_in); + } + + return addr_in; + } + + // now do the page lookup + if (m68ki_cpu.mmu_tc & 0x4000) // 8k pages? + { + page_idx = (addr_in >> 13) & 0x1f; + page = addr_in & 0x1fff; + pointer_entry &= ~0x7f; + MMULOG(("8k pages: index %x page %x\n", page_idx, page)); + } + else // 4k pages + { + page_idx = (addr_in >> 12) & 0x3f; + page = addr_in & 0xfff; + pointer_entry &= ~0xff; + MMULOG(("4k pages: index %x page %x\n", page_idx, page)); + } + + page_ptr = pointer_entry + (page_idx<<2); + page_entry = m68k_read_memory_32(page_ptr); + m68ki_cpu.mmu_last_page_entry_addr = page_ptr; + + MMULOG(("page_entry = %08x\n", page_entry)); + + // resolve indirect page pointers + while ((page_entry & 3) == 2) + { + page_entry = m68k_read_memory_32(page_entry & ~0x3); + m68ki_cpu.mmu_last_page_entry_addr = (page_entry & ~0x3); + } + m68ki_cpu.mmu_last_page_entry = page_entry; + + // is the page write protected or supervisor protected? + if ((((page_entry & 4) && !m68ki_cpu.mmu_tmp_rw) || ((page_entry & 0x80) && !(fc & 4))) && !ptest) + { + pmmu_set_buserror(addr_in); + return addr_in; + } + + switch (page_entry & 3) + { + case 0: // invalid + MMULOG(("Invalid page entry! PC=%x, addr=%x\n", m68ki_cpu.ppc, addr_in)); + if (!ptest) + { + pmmu_set_buserror(addr_in); + } + + return addr_in; + + case 1: + case 3: // normal + if (m68ki_cpu.mmu_tc & 0x4000) // 8k pages? + { + addr_out = (page_entry & ~0x1fff) | page; + } + else + { + addr_out = (page_entry & ~0xfff) | page; + } + + if (!(ptest)) + { + page_entry |= 0x8; // always set the U bit + + // if we're writing, the M bit comes into play + if (!m68ki_cpu.mmu_tmp_rw) + { + page_entry |= 0x10; // set Modified + } + + // if these updates resulted in a change, write the entry back where we found it + if (page_entry != m68ki_cpu.mmu_last_page_entry && !m_side_effects_disabled) + { + m68ki_cpu.mmu_last_page_entry = page_entry; + m68k_write_memory_32(m68ki_cpu.mmu_last_page_entry_addr, m68ki_cpu.mmu_last_page_entry); + } + } + else + { + // page entry: UR G U1 U0 S CM CM M U W PDT + // SR: B G U1 U0 S CM CM M 0 W T R + m68ki_cpu.mmu_tmp_sr |= ((addr_out & ~0xfff) || (page_entry & 0x7f4)); + } + break; + + case 2: // shouldn't happen + fatalerror("68040: got indirect final page pointer, shouldn't be possible\n"); break; } + // if (addr_in != addr_out) MMULOG(("040MMU: [%08x] => [%08x]\n", addr_in, addr_out)); } + return addr_out; +} -// fprintf(stderr,"PMMU: [%08x] => [%08x]\n", addr_in, addr_out); +// pmmu_translate_addr: perform 68851/68030-style PMMU address translation +uint32 pmmu_translate_addr(uint32 addr_in, uint16 rw) +{ + uint32 addr_out; + if (CPU_TYPE_IS_040_PLUS(m68ki_cpu.cpu_type)) + { + addr_out = pmmu_translate_addr_with_fc_040(addr_in, m68ki_cpu.mmu_tmp_fc, 0); + } + else + { + addr_out = pmmu_translate_addr_with_fc(addr_in, m68ki_cpu.mmu_tmp_fc, rw,7,0,0); + MMULOG(("ADDRIN %08X, ADDROUT %08X\n", addr_in, addr_out)); + } return addr_out; } -/* +int fc_from_modes(uint16 modes) +{ + if ((modes & 0x1f) == 0) + { + return m68ki_cpu.sfc; + } - m68881_mmu_ops: COP 0 MMU opcode handling + if ((modes & 0x1f) == 1) + { + return m68ki_cpu.dfc; + } -*/ + if (m68ki_cpu.cpu_type & CPU_TYPE_030) + { + // 68030 has 3 bits fc, but 68851 4 bits + if (((modes >> 3) & 3) == 1) + { + return REG_D[modes & 7] & 0x7; + } -void m68881_mmu_ops() + if (((modes >> 3) & 3) == 2) + { + return modes & 7; + } + } + else + { + if (((modes >> 3) & 3) == 1) + { + return REG_D[modes & 7] & 0xf; + } + + if (modes & 0x10) + { + return modes & 0xf; + } + } + + + fatalerror("%s: unknown fc mode: 0x%02xn", __func__, modes & 0x1f); + return 0; +} + +void m68851_pload(uint32 ea, uint16 modes) +{ + uint32 ltmp = DECODE_EA_32(ea); + int fc = fc_from_modes(modes); + uint16 rw = !!(modes & 0x200); + + MMULOG(("%s: PLOAD%c addr=%08x, fc=%d\n", __func__, rw ? 'R' : 'W', ltmp, fc)); + + // MC68851 traps if MMU is not enabled, 030 not + if (m68ki_cpu.pmmu_enabled || (m68ki_cpu.cpu_type & CPU_TYPE_030)) + { + if (CPU_TYPE_IS_040_PLUS(m68ki_cpu.cpu_type)) + { + pmmu_translate_addr_with_fc_040(ltmp, fc, 0); + } + else + { + pmmu_translate_addr_with_fc(ltmp, fc, rw , 7, 0, 1); + } + } + else + { + MMULOG(("PLOAD with MMU disabled on MC68851\n")); + m68ki_exception_trap(57); + return; + } +} + +void m68851_ptest(uint32 ea, uint16 modes) +{ + uint32 v_addr = DECODE_EA_32(ea); + uint32 p_addr; + + int level = (modes >> 10) & 7; + uint16 rw = !!(modes & 0x200); + int fc = fc_from_modes(modes); + + MMULOG(("PMMU: PTEST%c (%04X) pc=%08x sp=%08x va=%08x fc=%x level=%x a=%d, areg=%d\n", + rw ? 'R' : 'W', modes, m68ki_cpu.ppc, REG_A[7], v_addr, fc, level, + (modes & 0x100) ? 1 : 0, (modes >> 5) & 7)); + + if (CPU_TYPE_IS_040_PLUS(m68ki_cpu.cpu_type)) + { + p_addr = pmmu_translate_addr_with_fc_040(v_addr, fc, 1); + } + else + { + p_addr = pmmu_translate_addr_with_fc(v_addr, fc, rw, level, 1, 0); + } + + m68ki_cpu.mmu_sr = m68ki_cpu.mmu_tmp_sr; + + MMULOG(("PMMU: PTEST result: %04x pa=%08x\n", m68ki_cpu.mmu_sr, p_addr)); + if (modes & 0x100) + { + int areg = (modes >> 5) & 7; + WRITE_EA_32(0x08 | areg, p_addr); + } +} + +void m68851_pmove_get(uint32 ea, uint16 modes) +{ + switch ((modes>>10) & 0x3f) + { + case 0x02: // transparent translation register 0 + WRITE_EA_32(ea, m68ki_cpu.mmu_tt0); + MMULOG(("PMMU: pc=%x PMOVE from mmu_tt0=%08x\n", m68ki_cpu.ppc, m68ki_cpu.mmu_tt0)); + break; + case 0x03: // transparent translation register 1 + WRITE_EA_32(ea, m68ki_cpu.mmu_tt1); + MMULOG(("PMMU: pc=%x PMOVE from mmu_tt1=%08x\n", m68ki_cpu.ppc, m68ki_cpu.mmu_tt1)); + break; + case 0x10: // translation control register + WRITE_EA_32(ea, m68ki_cpu.mmu_tc); + MMULOG(("PMMU: pc=%x PMOVE from mmu_tc=%08x\n", m68ki_cpu.ppc, m68ki_cpu.mmu_tc)); + break; + + case 0x12: // supervisor root pointer + WRITE_EA_64(ea, (uint64)m68ki_cpu.mmu_srp_limit<<32 | (uint64)m68ki_cpu.mmu_srp_aptr); + MMULOG(("PMMU: pc=%x PMOVE from SRP limit = %08x, aptr = %08x\n", m68ki_cpu.ppc, m68ki_cpu.mmu_srp_limit, m68ki_cpu.mmu_srp_aptr)); + break; + + case 0x13: // CPU root pointer + WRITE_EA_64(ea, (uint64)m68ki_cpu.mmu_crp_limit<<32 | (uint64)m68ki_cpu.mmu_crp_aptr); + MMULOG(("PMMU: pc=%x PMOVE from CRP limit = %08x, aptr = %08x\n", m68ki_cpu.ppc, m68ki_cpu.mmu_crp_limit, m68ki_cpu.mmu_crp_aptr)); + break; + + default: + logerror("680x0: PMOVE from unknown MMU register %x, PC %x\n", (modes>>10) & 7, m68ki_cpu.pc); + return; + } + + if (!(modes & 0x100)) // flush ATC on moves to TC, SRP, CRP, TT with FD bit clear + { + pmmu_atc_flush(); + } + +} + +void m68851_pmove_put(uint32 ea, uint16 modes) +{ + uint64 temp64; + switch ((modes>>13) & 7) + { + case 0: + { + uint32 temp = READ_EA_32(ea); + + if (((modes >> 10) & 7) == 2) + { + MMULOG(("WRITE TT0 = 0x%08x\n", m68ki_cpu.mmu_tt0)); + m68ki_cpu.mmu_tt0 = temp; + } + else if (((modes >> 10) & 7) == 3) + { + MMULOG(("WRITE TT1 = 0x%08x\n", m68ki_cpu.mmu_tt1)); + m68ki_cpu.mmu_tt1 = temp; + } + break; + + // FIXME: unreachable + if (!(modes & 0x100)) + { + pmmu_atc_flush(); + } + } + /* fall through */ + /* no break */ + + case 1: + logerror("680x0: unknown PMOVE case 1, PC %x\n", m68ki_cpu.pc); + break; + + case 2: + switch ((modes >> 10) & 7) + { + case 0: // translation control register + m68ki_cpu.mmu_tc = READ_EA_32(ea); + MMULOG(("PMMU: TC = %08x\n", m68ki_cpu.mmu_tc)); + + if (m68ki_cpu.mmu_tc & 0x80000000) + { + int bits = 0; + for (int shift = 20; shift >= 0; shift -= 4) + { + bits += (m68ki_cpu.mmu_tc >> shift) & 0x0f; + } + + if (bits != 32 || !((m68ki_cpu.mmu_tc >> 23) & 1)) + { + logerror("MMU: TC invalid!\n"); + m68ki_cpu.mmu_tc &= ~0x80000000; + m68ki_exception_trap(EXCEPTION_MMU_CONFIGURATION); + } else { + m68ki_cpu.pmmu_enabled = 1; + } + MMULOG(("PMMU enabled\n")); + } + else + { + m68ki_cpu.pmmu_enabled = 0; + MMULOG(("PMMU disabled\n")); + } + + if (!(modes & 0x100)) // flush ATC on moves to TC, SRP, CRP with FD bit clear + { + pmmu_atc_flush(); + } + break; + + case 2: // supervisor root pointer + temp64 = READ_EA_64(ea); + m68ki_cpu.mmu_srp_limit = (temp64 >> 32) & 0xffffffff; + m68ki_cpu.mmu_srp_aptr = temp64 & 0xffffffff; + MMULOG(("PMMU: SRP limit = %08x aptr = %08x\n", m68ki_cpu.mmu_srp_limit, m68ki_cpu.mmu_srp_aptr)); + // SRP type 0 is not allowed + if ((m68ki_cpu.mmu_srp_limit & 3) == 0) + { + m68ki_exception_trap(EXCEPTION_MMU_CONFIGURATION); + return; + } + + if (!(modes & 0x100)) + { + pmmu_atc_flush(); + } + break; + + case 3: // CPU root pointer + temp64 = READ_EA_64(ea); + m68ki_cpu.mmu_crp_limit = (temp64 >> 32) & 0xffffffff; + m68ki_cpu.mmu_crp_aptr = temp64 & 0xffffffff; + MMULOG(("PMMU: CRP limit = %08x aptr = %08x\n", m68ki_cpu.mmu_crp_limit, m68ki_cpu.mmu_crp_aptr)); + // CRP type 0 is not allowed + if ((m68ki_cpu.mmu_crp_limit & 3) == 0) + { + m68ki_exception_trap(EXCEPTION_MMU_CONFIGURATION); + return; + } + + if (!(modes & 0x100)) + { + pmmu_atc_flush(); + } + break; + + case 7: // MC68851 Access Control Register + if (m68ki_cpu.cpu_type == CPU_TYPE_020) + { + // DomainOS on Apollo DN3000 will only reset this to 0 + uint16 mmu_ac = READ_EA_16(ea); + if (mmu_ac != 0) + { + MMULOG(("680x0 PMMU: pc=%x PMOVE to mmu_ac=%08x\n", + m68ki_cpu.ppc, mmu_ac)); + } + break; + } + // fall through; unknown PMOVE mode unless MC68020 with MC68851 + /* fall through */ + /* no break */ + default: + logerror("680x0: PMOVE to unknown MMU register %x, PC %x\n", (modes>>10) & 7, m68ki_cpu.pc); + break; + } + break; + case 3: // MMU status + { + uint32 temp = READ_EA_32(ea); + logerror("680x0: unsupported PMOVE %x to MMU status, PC %x\n", temp, m68ki_cpu.pc); + } + break; + } +} + + +void m68851_pmove(uint32 ea, uint16 modes) +{ + switch ((modes>>13) & 0x7) + { + case 0: // MC68030/040 form with FD bit + case 2: // MC68851 form, FD never set + if (modes & 0x200) + { + m68851_pmove_get(ea, modes); + break; + } + else // top 3 bits of modes: 010 for this, 011 for status, 000 for transparent translation regs + { + m68851_pmove_put(ea, modes); + break; + } + case 3: // MC68030 to/from status reg + if (modes & 0x200) + { + MMULOG(("%s: read SR = %04x\n", __func__, m68ki_cpu.mmu_sr)); + WRITE_EA_16(ea, m68ki_cpu.mmu_sr); + } + else + { + m68ki_cpu.mmu_sr = READ_EA_16(ea); + MMULOG(("%s: write SR = %04X\n", __func__, m68ki_cpu.mmu_sr)); + } + break; + + default: + logerror("680x0: unknown PMOVE mode %x (modes %04x) (PC %x)\n", (modes >> 13) & 0x7, modes, m68ki_cpu.pc); + break; + + } + +} + +void m68851_mmu_ops() { uint16 modes; uint32 ea = m68ki_cpu.ir & 0x3f; - uint64 temp64; // catch the 2 "weird" encodings up front (PBcc) if ((m68ki_cpu.ir & 0xffc0) == 0xf0c0) { - fprintf(stderr,"680x0: unhandled PBcc\n"); + logerror("680x0: unhandled PBcc\n"); return; } else if ((m68ki_cpu.ir & 0xffc0) == 0xf080) { - fprintf(stderr,"680x0: unhandled PBcc\n"); + logerror("680x0: unhandled PBcc\n"); return; } + else if ((m68ki_cpu.ir & 0xffe0) == 0xf500) + { + MMULOG(("68040 pflush: pc=%08x ir=%04x opmode=%d register=%d\n", REG_PC-4, m68ki_cpu.ir, (m68ki_cpu.ir >> 3) & 3, m68ki_cpu.ir & 7)); + pmmu_atc_flush(); + } else // the rest are 1111000xxxXXXXXX where xxx is the instruction family { switch ((m68ki_cpu.ir>>9) & 0x7) @@ -203,119 +1243,101 @@ void m68881_mmu_ops() if ((modes & 0xfde0) == 0x2000) // PLOAD { - fprintf(stderr,"680x0: unhandled PLOAD\n"); + m68851_pload(ea, modes); return; } else if ((modes & 0xe200) == 0x2000) // PFLUSH { - fprintf(stderr,"680x0: unhandled PFLUSH PC=%x\n", REG_PC); + pmmu_atc_flush_fc_ea(modes); return; } else if (modes == 0xa000) // PFLUSHR { - fprintf(stderr,"680x0: unhandled PFLUSHR\n"); + pmmu_atc_flush(); return; } else if (modes == 0x2800) // PVALID (FORMAT 1) { - fprintf(stderr,"680x0: unhandled PVALID1\n"); + logerror("680x0: unhandled PVALID1\n"); return; } else if ((modes & 0xfff8) == 0x2c00) // PVALID (FORMAT 2) { - fprintf(stderr,"680x0: unhandled PVALID2\n"); + logerror("680x0: unhandled PVALID2\n"); return; } else if ((modes & 0xe000) == 0x8000) // PTEST { - fprintf(stderr,"680x0: unhandled PTEST\n"); + m68851_ptest(ea, modes); return; } else { - switch ((modes>>13) & 0x7) - { - case 0: // MC68030/040 form with FD bit - case 2: // MC68881 form, FD never set - if (modes & 0x200) - { - switch ((modes>>10) & 7) - { - case 0: // translation control register - WRITE_EA_32(ea, m68ki_cpu.mmu_tc); - break; - - case 2: // supervisor root pointer - WRITE_EA_64(ea, (uint64)m68ki_cpu.mmu_srp_limit<<32 | (uint64)m68ki_cpu.mmu_srp_aptr); - break; - - case 3: // CPU root pointer - WRITE_EA_64(ea, (uint64)m68ki_cpu.mmu_crp_limit<<32 | (uint64)m68ki_cpu.mmu_crp_aptr); - break; - - default: - fprintf(stderr,"680x0: PMOVE from unknown MMU register %x, PC %x\n", (modes>>10) & 7, REG_PC); - break; - } - } - else - { - switch ((modes>>10) & 7) - { - case 0: // translation control register - m68ki_cpu.mmu_tc = READ_EA_32(ea); - - if (m68ki_cpu.mmu_tc & 0x80000000) - { - m68ki_cpu.pmmu_enabled = 1; - } - else - { - m68ki_cpu.pmmu_enabled = 0; - } - break; - - case 2: // supervisor root pointer - temp64 = READ_EA_64(ea); - m68ki_cpu.mmu_srp_limit = (temp64>>32) & 0xffffffff; - m68ki_cpu.mmu_srp_aptr = temp64 & 0xffffffff; - break; - - case 3: // CPU root pointer - temp64 = READ_EA_64(ea); - m68ki_cpu.mmu_crp_limit = (temp64>>32) & 0xffffffff; - m68ki_cpu.mmu_crp_aptr = temp64 & 0xffffffff; - break; - - default: - fprintf(stderr,"680x0: PMOVE to unknown MMU register %x, PC %x\n", (modes>>10) & 7, REG_PC); - break; - } - } - break; - - case 3: // MC68030 to/from status reg - if (modes & 0x200) - { - WRITE_EA_32(ea, m68ki_cpu.mmu_sr); - } - else - { - m68ki_cpu.mmu_sr = READ_EA_32(ea); - } - break; - - default: - fprintf(stderr,"680x0: unknown PMOVE mode %x (modes %04x) (PC %x)\n", (modes>>13) & 0x7, modes, REG_PC); - break; - } + m68851_pmove(ea, modes); } break; default: - fprintf(stderr,"680x0: unknown PMMU instruction group %d\n", (m68ki_cpu.ir>>9) & 0x7); + logerror("680x0: unknown PMMU instruction group %d\n", (m68ki_cpu.ir>>9) & 0x7); break; } } } + +/* Apple HMMU translation is much simpler */ +/* +inline uint32 hmmu_translate_addr(uint32 addr_in) +{ + uint32 addr_out; + + addr_out = addr_in; + + // check if LC 24-bit mode is enabled - this simply blanks out A31, the V8 ignores A30-24 always + if (m68ki_cpu.hmmu_enabled == M68K_HMMU_ENABLE_LC) + { + addr_out = addr_in & 0xffffff; + } + else if (m68ki_cpu.hmmu_enabled == M68K_HMMU_ENABLE_II) // the original II does a more complex translation + { + addr_out = addr_in & 0xffffff; + + if ((addr_out >= 0x800000) && (addr_out <= 0x8fffff)) + { + addr_out |= 0x40000000; // ROM + } + else if ((addr_out >= 0x900000) && (addr_out <= 0xefffff)) + { + addr_out = 0xf0000000; // NuBus + addr_out |= ((addr_in & 0xf00000)<<4); + addr_out |= (addr_in & 0xfffff); + } + else if (addr_out >= 0xf00000) + { + addr_out |= 0x50000000; // I/O + } + + // (RAM is at 0 and doesn't need special massaging) + } + + return addr_out; +} + +int m68851_buserror(u32& addr) +{ + if (!m68ki_cpu.pmmu_enabled) + { + return false; + } + + if (m68ki_cpu.mmu_tablewalk) + { + MMULOG(("buserror during table walk\n")); + m68ki_cpu.mmu_tmp_sr |= M68K_MMU_SR_BUS_ERROR|M68K_MMU_SR_INVALID; + return true; + } + + addr = m68ki_cpu.mmu_last_logical_addr; + return false; +} +*/