]> git.sesse.net Git - pistorm/commitdiff
MMU and InstructionCache update (MAME's latest code)
authorshanshe <shanshe@gmail.com>
Tue, 13 Apr 2021 08:32:30 +0000 (10:32 +0200)
committershanshe <shanshe@gmail.com>
Tue, 13 Apr 2021 08:32:30 +0000 (10:32 +0200)
m68k.h
m68k_in.c
m68kcpu.c
m68kcpu.h
m68kmmu.h

diff --git a/m68k.h b/m68k.h
index 3c6ee81fd5205b8ed50ebdb7fc94f3323e5fe595..a7328cfed42f82a34052752417419a3331196398 100644 (file)
--- 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
index cf2ab6273b66157d1a5042fb7a815573fa961cb0..9283892dc99f458f272055e899414b0b3eb0620a 100644 (file)
--- 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
        {
index 293f937175d4aaaa4530fd938f0ed7991c05ed34..4cc48c5f75044fc636e4da876bd9b0a55f939fcf 100644 (file)
--- 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 */
index 8cd03d6573b4bd6e5539e67f37fc36985db1382f..923f143b49d2c64c02dcdee6aff4e1e8a0a81a76 100644 (file)
--- 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;
        }
index faba371e72bedbd75b5cff65b40686d110dab2b6..90853143ac3da9f9bcc4f60454c1404231d96686 100644 (file)
--- a/m68kmmu.h
+++ b/m68kmmu.h
     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<<is)>>(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<MMU_ATC_ENTRIES;i++)
+               m68ki_cpu.mmu_atc_tag[i]=0;
+       m68ki_cpu.mmu_atc_rr = 0;
+}
+
+int fc_from_modes(uint16 modes);
 
-       // find out what format table B is, if any
-       switch (tamode)
+void pmmu_atc_flush_fc_ea(uint16 modes)
+{
+       unsigned int fcmask = (modes >> 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<MMU_ATC_ENTRIES;i++)
+               {
+                       e=m68ki_cpu.mmu_atc_tag[i];
+                       if ((e & M68K_MMU_ATC_VALID) && ((e >> 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<MMU_ATC_ENTRIES;i++)
+               {
+                       e=m68ki_cpu.mmu_atc_tag[i];
+                       if ((e & M68K_MMU_ATC_VALID) &&
+                               (((e >> 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)>>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<bool ptest>
+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<bool _long>
+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<bool ptest>
+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)>>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<bool ptest, bool pload>
+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)>>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;
+}
+*/