+ if (!pmmu_walk_tables(state, 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");
+ }
+
+ if (ptest)
+ {
+ return addr_out;
+ }
+
+ if ((state->mmu_tmp_sr & (M68K_MMU_SR_INVALID|M68K_MMU_SR_SUPERVISOR_ONLY)) ||
+ ((state->mmu_tmp_sr & M68K_MMU_SR_WRITE_PROTECT) && !rw))
+ {
+
+ if (!pload)
+ {
+ MMULOG(("%s: set buserror (SR %04X)\n", __func__, state->mmu_tmp_sr));
+ pmmu_set_buserror(state, 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(state, addr_in, addr_out, fc, rw && type != 1);
+ }
+ MMULOG(("PMMU: [%08x] => [%08x] (SR %04x)\n", addr_in, addr_out, state->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(m68ki_cpu_core *state, uint32 addr_in, uint8 fc, uint8 ptest)
+{
+ uint32 addr_out, tt0, tt1;
+
+ addr_out = addr_in;
+ state->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 = state->mmu_dtt0;
+ tt1 = state->mmu_dtt1;
+ }
+ else if (fc & 2) // program, use ITT0/ITT1
+ {
+ tt0 = state->mmu_itt0;
+ tt1 = state->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) && !state->mmu_tmp_rw && !ptest) // write protect?
+ {
+ pmmu_set_buserror(state, 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) && !state->mmu_tmp_rw && !ptest) // write protect?
+ {
+ pmmu_set_buserror(state, addr_in);
+ }
+
+ return addr_in;
+ }
+ }
+
+ if (state->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 = state->mmu_srp_aptr + (root_idx<<2);
+ }
+ else
+ {
+ root_ptr = state->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))
+ {
+ state->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))
+ {
+ state->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) && !state->mmu_tmp_rw) || ((pointer_entry & 4) && !state->mmu_tmp_rw)) && !ptest)
+ {
+ pmmu_set_buserror(state, 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", state->ppc, addr_in);
+ pmmu_set_buserror(state, 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", state->ppc, addr_in);
+
+ if (!ptest)
+ {
+ pmmu_set_buserror(state, addr_in);
+ }
+
+ return addr_in;
+ }
+
+ // now do the page lookup
+ if (state->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);
+ state->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);
+ state->mmu_last_page_entry_addr = (page_entry & ~0x3);
+ }
+ state->mmu_last_page_entry = page_entry;
+
+ // is the page write protected or supervisor protected?
+ if ((((page_entry & 4) && !state->mmu_tmp_rw) || ((page_entry & 0x80) && !(fc & 4))) && !ptest)
+ {
+ pmmu_set_buserror(state, addr_in);
+ return addr_in;
+ }
+
+ switch (page_entry & 3)
+ {
+ case 0: // invalid
+ MMULOG(("Invalid page entry! PC=%x, addr=%x\n", state->ppc, addr_in));
+ if (!ptest)
+ {
+ pmmu_set_buserror(state, addr_in);
+ }
+
+ return addr_in;
+
+ case 1:
+ case 3: // normal
+ if (state->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 (!state->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 != state->mmu_last_page_entry && !m_side_effects_disabled)
+ {
+ state->mmu_last_page_entry = page_entry;
+ m68k_write_memory_32(state->mmu_last_page_entry_addr, state->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
+ state->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");