diff --git a/src/emu/cpu/m68000/m68000.h b/src/emu/cpu/m68000/m68000.h index b1dbc70f507..78bebaf4016 100644 --- a/src/emu/cpu/m68000/m68000.h +++ b/src/emu/cpu/m68000/m68000.h @@ -33,6 +33,10 @@ enum M68K_CPU_TYPE_SCC68070 }; +/* HMMU enable types for use with m68k_set_hmmu_enable() */ +#define M68K_HMMU_DISABLE 0 /* no translation */ +#define M68K_HMMU_ENABLE_II 1 /* Mac II style fixed translation */ +#define M68K_HMMU_ENABLE_LC 2 /* Mac LC style fixed translation */ /* Special interrupt acknowledge values. * Use these as special returns from the interrupt acknowledge callback @@ -79,6 +83,7 @@ DECLARE_LEGACY_CPU_DEVICE(M68010, m68010); DECLARE_LEGACY_CPU_DEVICE(M68EC020, m68ec020); DECLARE_LEGACY_CPU_DEVICE(M68020, m68020); DECLARE_LEGACY_CPU_DEVICE(M68020PMMU, m68020pmmu); +DECLARE_LEGACY_CPU_DEVICE(M68020HMMU, m68020hmmu); DECLARE_LEGACY_CPU_DEVICE(M68EC030, m68ec030); DECLARE_LEGACY_CPU_DEVICE(M68030, m68030); DECLARE_LEGACY_CPU_DEVICE(M68EC040, m68ec040); @@ -89,6 +94,8 @@ DECLARE_LEGACY_CPU_DEVICE(SCC68070, scc68070); void m68k_set_encrypted_opcode_range(running_device *device, offs_t start, offs_t end); +void m68k_set_hmmu_enable(running_device *device, int enable); + unsigned int m68k_disassemble_raw(char* str_buff, unsigned int pc, const unsigned char* opdata, const unsigned char* argdata, unsigned int cpu_type); void m68k_set_reset_callback(running_device *device, m68k_reset_func callback); diff --git a/src/emu/cpu/m68000/m68kcpu.c b/src/emu/cpu/m68000/m68kcpu.c index 1ab3dd3f0fc..b15304f20fb 100644 --- a/src/emu/cpu/m68000/m68kcpu.c +++ b/src/emu/cpu/m68000/m68kcpu.c @@ -571,6 +571,22 @@ static CPU_TRANSLATE( m68k ) return TRUE; } +/* translate logical to physical addresses for Apple HMMU */ +static CPU_TRANSLATE( m68khmmu ) +{ + m68ki_cpu_core *m68k = get_safe_token(device); + + /* only applies to the program address space and only does something if the MMU's enabled */ + if (m68k) + { + if ((space == ADDRESS_SPACE_PROGRAM) && (m68k->hmmu_enabled)) + { + *address = hmmu_translate_addr(m68k, *address); + } + } + return TRUE; +} + /* Execute some instructions until we use up cycles clock cycles */ static CPU_EXECUTE( m68k ) { @@ -633,6 +649,12 @@ static CPU_INIT( m68k ) m68k->program = device->space(AS_PROGRAM); m68k->int_ack_callback = irqcallback; + /* disable all MMUs */ + m68k->has_pmmu = 0; + m68k->has_hmmu = 0; + m68k->pmmu_enabled = 0; + m68k->hmmu_enabled = 0; + /* The first call to this function initializes the opcode handler jump table */ if(!emulation_initialized) { @@ -667,8 +689,9 @@ static CPU_RESET( m68k ) { m68ki_cpu_core *m68k = get_safe_token(device); - /* Disable the PMMU on reset */ + /* Disable the PMMU/HMMU on reset, if any */ m68k->pmmu_enabled = 0; + m68k->hmmu_enabled = 0; /* Clear all stop levels and eat up all remaining cycles */ m68k->stopped = 0; @@ -702,6 +725,9 @@ static CPU_RESET( m68k ) m68k->run_mode = RUN_MODE_NORMAL; m68k->reset_cycles = m68k->cyc_exception[EXCEPTION_RESET]; + + /* flush the MMU's cache */ + pmmu_atc_flush(m68k); } static CPU_DISASSEMBLE( m68k ) @@ -940,6 +966,13 @@ void m68k_set_encrypted_opcode_range(running_device *device, offs_t start, offs_ m68k->encrypted_end = end; } +void m68k_set_hmmu_enable(running_device *device, int enable) +{ + m68ki_cpu_core *m68k = get_safe_token(device); + + m68k->hmmu_enabled = enable; +} + /**************************************************************************** * 8-bit data memory interface ****************************************************************************/ @@ -1143,6 +1176,132 @@ void m68k_memory_interface::init32mmu(address_space &space) } +/* interface for 32-bit data bus with PMMU (68EC020, 68020) */ +UINT8 m68k_memory_interface::read_byte_32_hmmu(offs_t address) +{ + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + return m_space->read_byte(address); +} + +void m68k_memory_interface::write_byte_32_hmmu(offs_t address, UINT8 data) +{ + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + m_space->write_byte(address, data); +} + +UINT16 m68k_memory_interface::read_immediate_16_hmmu(offs_t address) +{ + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + return m_direct->read_decrypted_word((address) ^ m_cpustate->memory.opcode_xor); +} + +/* potentially misaligned 16-bit reads with a 32-bit data bus (and 24-bit address bus) */ +UINT16 m68k_memory_interface::readword_d32_hmmu(offs_t address) +{ + UINT16 result; + + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + if (!(address & 1)) + return m_space->read_word(address); + result = m_space->read_byte(address) << 8; + return result | m_space->read_byte(address + 1); +} + +/* potentially misaligned 16-bit writes with a 32-bit data bus (and 24-bit address bus) */ +void m68k_memory_interface::writeword_d32_hmmu(offs_t address, UINT16 data) +{ + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + if (!(address & 1)) + { + m_space->write_word(address, data); + return; + } + m_space->write_byte(address, data >> 8); + m_space->write_byte(address + 1, data); +} + +/* potentially misaligned 32-bit reads with a 32-bit data bus (and 24-bit address bus) */ +UINT32 m68k_memory_interface::readlong_d32_hmmu(offs_t address) +{ + UINT32 result; + + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + if (!(address & 3)) + return m_space->read_dword(address); + else if (!(address & 1)) + { + result = m_space->read_word(address) << 16; + return result | m_space->read_word(address + 2); + } + result = m_space->read_byte(address) << 24; + result |= m_space->read_word(address + 1) << 8; + return result | m_space->read_byte(address + 3); +} + +/* potentially misaligned 32-bit writes with a 32-bit data bus (and 24-bit address bus) */ +void m68k_memory_interface::writelong_d32_hmmu(offs_t address, UINT32 data) +{ + if (m_cpustate->hmmu_enabled) + { + address = hmmu_translate_addr(m_cpustate, address); + } + + if (!(address & 3)) + { + m_space->write_dword(address, data); + return; + } + else if (!(address & 1)) + { + m_space->write_word(address, data >> 16); + m_space->write_word(address + 2, data); + return; + } + m_space->write_byte(address, data >> 24); + m_space->write_word(address + 1, data >> 8); + m_space->write_byte(address + 3, data); +} + +void m68k_memory_interface::init32hmmu(address_space &space) +{ + m_space = &space; + m_direct = &space.direct(); + m_cpustate = get_safe_token(&space.device()); + opcode_xor = WORD_XOR_BE(0); + + readimm16 = m68k_readimm16_delegate(m68k_readimm16_proto_delegate::create_member(m68k_memory_interface, read_immediate_16_hmmu), *this); + read8 = m68k_read8_delegate(m68k_read8_proto_delegate::create_member(m68k_memory_interface, read_byte_32_hmmu), *this); + read16 = m68k_read16_delegate(m68k_read16_proto_delegate::create_member(m68k_memory_interface, readword_d32_hmmu), *this); + read32 = m68k_read32_delegate(m68k_read32_proto_delegate::create_member(m68k_memory_interface, readlong_d32_hmmu), *this); + write8 = m68k_write8_delegate(m68k_write8_proto_delegate::create_member(m68k_memory_interface, write_byte_32_hmmu), *this); + write16 = m68k_write16_delegate(m68k_write16_proto_delegate::create_member(m68k_memory_interface, writeword_d32_hmmu), *this); + write32 = m68k_write32_delegate(m68k_write32_proto_delegate::create_member(m68k_memory_interface, writelong_d32_hmmu), *this); +} + void m68k_set_reset_callback(running_device *device, m68k_reset_func callback) { m68ki_cpu_core *m68k = get_safe_token(device); @@ -1253,6 +1412,7 @@ static CPU_INIT( m68000 ) m68k->cyc_shift = 1; m68k->cyc_reset = 132; m68k->has_pmmu = 0; + m68k->has_hmmu = 0; define_state(device); } @@ -1403,7 +1563,6 @@ static CPU_INIT( m68020 ) m68k->cyc_movem_l = 2; m68k->cyc_shift = 0; m68k->cyc_reset = 518; - m68k->has_pmmu = 0; define_state(device); } @@ -1459,6 +1618,36 @@ CPU_GET_INFO( m68020pmmu ) } } +// 68020 with Apple HMMU +static CPU_INIT( m68020hmmu ) +{ + m68ki_cpu_core *m68k = get_safe_token(device); + + CPU_INIT_CALL(m68020); + + m68k->has_hmmu = 1; +// hack alert: we use placement new to ensure we are properly initialized +// because we live in the device state which is allocated as bytes +// remove me when we have a real C++ device + new(&m68k->memory) m68k_memory_interface; + m68k->memory.init32mmu(*m68k->program); +} + +CPU_GET_INFO( m68020hmmu ) +{ + switch (state) + { + /* --- the following bits of info are returned as pointers to data or functions --- */ + case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(m68020hmmu); break; + case CPUINFO_FCT_TRANSLATE: info->translate = CPU_TRANSLATE_NAME(m68khmmu); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "68020, Apple HMMU"); break; + + default: CPU_GET_INFO_CALL(m68020); break; + } +} + /**************************************************************************** * M680EC20 section ****************************************************************************/ @@ -1803,6 +1992,7 @@ DEFINE_LEGACY_CPU_DEVICE(M68010, m68010); DEFINE_LEGACY_CPU_DEVICE(M68EC020, m68ec020); DEFINE_LEGACY_CPU_DEVICE(M68020, m68020); DEFINE_LEGACY_CPU_DEVICE(M68020PMMU, m68020pmmu); +DEFINE_LEGACY_CPU_DEVICE(M68020HMMU, m68020hmmu); DEFINE_LEGACY_CPU_DEVICE(M68EC030, m68ec030); DEFINE_LEGACY_CPU_DEVICE(M68030, m68030); DEFINE_LEGACY_CPU_DEVICE(M68EC040, m68ec040); diff --git a/src/emu/cpu/m68000/m68kcpu.h b/src/emu/cpu/m68000/m68kcpu.h index d16bf70a14f..6a1e4918349 100644 --- a/src/emu/cpu/m68000/m68kcpu.h +++ b/src/emu/cpu/m68000/m68kcpu.h @@ -100,6 +100,8 @@ typedef struct _m68ki_cpu_core m68ki_cpu_core; #define RUN_MODE_NORMAL 0 #define RUN_MODE_BERR_AERR_RESET 1 +/* MMU constants */ +#define MMU_ATC_ENTRIES (22) // 68851 has 64, 030 has 22 /* ======================================================================== */ /* ================================ MACROS ================================ */ @@ -558,6 +560,7 @@ public: void init16(address_space &space); void init32(address_space &space); void init32mmu(address_space &space); + void init32hmmu(address_space &space); offs_t opcode_xor; // Address Calculation m68k_readimm16_delegate readimm16; // Immediate read 16 bit @@ -581,6 +584,14 @@ private: UINT32 readlong_d32_mmu(offs_t address); void writelong_d32_mmu(offs_t address, UINT32 data); + UINT8 read_byte_32_hmmu(offs_t address); + void write_byte_32_hmmu(offs_t address, UINT8 data); + UINT16 read_immediate_16_hmmu(offs_t address); + UINT16 readword_d32_hmmu(offs_t address); + void writeword_d32_hmmu(offs_t address, UINT16 data); + UINT32 readlong_d32_hmmu(offs_t address); + void writelong_d32_hmmu(offs_t address, UINT32 data); + address_space *m_space; direct_read_data *m_direct; m68ki_cpu_core *m_cpustate; @@ -622,7 +633,9 @@ struct _m68ki_cpu_core UINT32 instr_mode; /* Stores whether we are in instruction mode or group 0/1 exception mode */ UINT32 run_mode; /* Stores whether we are processing a reset, bus error, address error, or something else */ int has_pmmu; /* Indicates if a PMMU available (yes on 030, 040, no on EC030) */ + int has_hmmu; /* Indicates if an Apple HMMU is available in place of the 68851 (020 only) */ int pmmu_enabled; /* Indicates if the PMMU is enabled */ + int hmmu_enabled; /* Indicates if the HMMU is enabled */ int fpu_just_reset; /* Indicates the FPU was just reset */ /* Clocks required for instructions / exceptions */ @@ -683,6 +696,9 @@ struct _m68ki_cpu_core UINT32 mmu_srp_aptr, mmu_srp_limit; UINT32 mmu_tc; UINT16 mmu_sr; + UINT32 mmu_atc_tag[MMU_ATC_ENTRIES], mmu_atc_data[MMU_ATC_ENTRIES]; + UINT32 mmu_atc_rr; + UINT32 mmu_tt0, mmu_tt1; }; diff --git a/src/emu/cpu/m68000/m68kmmu.h b/src/emu/cpu/m68000/m68kmmu.h index f2b601f0c96..b4837730e20 100644 --- a/src/emu/cpu/m68000/m68kmmu.h +++ b/src/emu/cpu/m68000/m68kmmu.h @@ -1,5 +1,6 @@ /* m68kmmu.h - PMMU implementation for 68851/68030/68040 + HMMU implementation for 68020 (II and LC variants) By R. Belmont @@ -7,6 +8,128 @@ Visit http://mamedev.org for licensing and usage restrictions. */ +/* decodes the effective address */ +static UINT32 DECODE_EA_32(m68ki_cpu_core *m68k, int ea) +{ + int mode = (ea >> 3) & 0x7; + int reg = (ea & 0x7); + + switch (mode) + { + case 2: // (An) + { + return REG_A[reg]; + } + case 3: // (An)+ + { + UINT32 ea = EA_AY_PI_32(m68k); + return ea; + } + case 5: // (d16, An) + { + UINT32 ea = EA_AY_DI_32(m68k); + return ea; + } + case 6: // (An) + (Xn) + d8 + { + UINT32 ea = EA_AY_IX_32(m68k); + return ea; + } + case 7: + { + switch (reg) + { + case 0: // (xxx).W + { + UINT32 ea = (UINT32)OPER_I_16(m68k); + return ea; + } + case 1: // (xxx).L + { + UINT32 d1 = OPER_I_16(m68k); + UINT32 d2 = OPER_I_16(m68k); + UINT32 ea = (d1 << 16) | d2; + return ea; + } + case 2: // (d16, PC) + { + UINT32 ea = EA_PCDI_32(m68k); + 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; +} + +/* + pmmu_atc_add: adds this address to the ATC +*/ +void pmmu_atc_add(m68ki_cpu_core *m68k, UINT32 logical, UINT32 physical) +{ + int i, found, curfc; + + curfc = (m68ki_get_sr(m68k) & 0x2000) ? 4 : 0; + curfc |= 1; // program vs. data space is not implemented + + // first see if this is already in the cache + for (i = 0; i < MMU_ATC_ENTRIES; i++) + { + // if tag bits and function code match, don't add + if (((m68k->mmu_atc_tag[i] & 0xffffff) == (logical>>8)) && (((m68k->mmu_atc_tag[i]>>24) & 7) == curfc)) + { + return; + } + } + + // find an open entry + found = -1; + for (i = 0; i < MMU_ATC_ENTRIES; i++) + { + if (!(m68k->mmu_atc_tag[i] & 0x80000000)) + { + found = i; + break; + } + } + + // did we find an entry? steal one by round-robin then + if (found == -1) + { + found = m68k->mmu_atc_rr++; + + if (m68k->mmu_atc_rr >= MMU_ATC_ENTRIES) + { + m68k->mmu_atc_rr = 0; + } + } + + // add the entry +// logerror("ATC[%d] add: log %08x -> phys %08x\n", found, logical, physical); + m68k->mmu_atc_tag[found] = (logical>>8) | (curfc<<24) | 0x80000000; + m68k->mmu_atc_data[found] = (physical>>8); +} + +/* + pmmu_atc_flush: flush entire ATC + + 7fff0003 001ffd10 80f05750 is what should load +*/ +void pmmu_atc_flush(m68ki_cpu_core *m68k) +{ + int i; + + for (i = 0; i < MMU_ATC_ENTRIES; i++) + { + m68k->mmu_atc_tag[i] = 0; + } + + m68k->mmu_atc_rr = 0; +} + /* pmmu_translate_addr: perform 68851/68030-style PMMU address translation */ @@ -15,10 +138,29 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) UINT32 addr_out, tbl_entry = 0, tbl_entry2, tamode = 0, tbmode = 0, tcmode = 0; UINT32 root_aptr, root_limit, tofs, is, abits, bbits, cbits; UINT32 resolved, tptr, shift; + int curfc, i; +// int verbose = 0; resolved = 0; addr_out = addr_in; + curfc = (m68ki_get_sr(m68k) & 0x2000) ? 4 : 0; + curfc |= 1; // program vs. data space is not implemented + + // first see if this is already in the ATC + for (i = 0; i < MMU_ATC_ENTRIES; i++) + { + // if tag bits and function code match, we've got it + if (((m68k->mmu_atc_tag[i] & 0xffffff) == (addr_in>>8)) && (((m68k->mmu_atc_tag[i]>>24) & 7) == curfc)) + { + addr_out = (m68k->mmu_atc_data[i]<<8) | (addr_in & 0xff); +// logerror("ATC[%d] hit: log %08x -> phys %08x\n", i, addr_in, addr_out); + return addr_out; + } + } + +// if (addr_in == 0x5fa04c) verbose = 1; + // if SRP is enabled and we're in supervisor mode, use it if ((m68k->mmu_tc & 0x02000000) && (m68ki_get_sr(m68k) & 0x2000)) { @@ -37,7 +179,7 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) bbits = (m68k->mmu_tc>>8)&0xf; cbits = (m68k->mmu_tc>>4)&0xf; -// logerror("PMMU: tcr %08x limit %08x aptr %08x is %x abits %d bbits %d cbits %d\n", m68k->mmu_tc, root_limit, root_aptr, is, abits, bbits, cbits); + //logerror("PMMU: tcr %08x limit %08x aptr %08x is %x abits %d bbits %d cbits %d\n", m68k->mmu_tc, root_limit, root_aptr, is, abits, bbits, cbits); // get table A offset tofs = (addr_in<>(32-abits); @@ -47,24 +189,26 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) { case 0: // invalid, should cause MMU exception case 1: // page descriptor, should cause direct mapping - fatalerror("680x0 PMMU: Unhandled root mode\n"); + logerror("680x0 PMMU: Unhandled root mode %d, not translating (addr_in %x)\n", root_limit & 3, addr_in); + logerror(" : aptr %08x limit %08x\n", root_aptr, root_limit); + return addr_in; break; case 2: // valid 4 byte descriptors tofs *= 4; -// logerror("PMMU: reading table A entry at %08x\n", tofs + (root_aptr & 0xfffffffc)); +// if (verbose) logerror("PMMU: reading table A entry at %08x\n", tofs + (root_aptr & 0xfffffffc)); tbl_entry = m68k->program->read_dword(tofs + (root_aptr & 0xfffffffc)); tamode = tbl_entry & 3; -// logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tamode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tamode, tofs); break; case 3: // valid 8 byte descriptors tofs *= 8; -// logerror("PMMU: reading table A entries at %08x\n", tofs + (root_aptr & 0xfffffffc)); +// if (verbose) logerror("PMMU: reading table A entries at %08x\n", tofs + (root_aptr & 0xfffffffc)); tbl_entry2 = m68k->program->read_dword(tofs + (root_aptr & 0xfffffffc)); tbl_entry = m68k->program->read_dword(tofs + (root_aptr & 0xfffffffc)+4); tamode = tbl_entry2 & 3; -// logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tamode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tamode, tofs); break; } @@ -76,24 +220,24 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) switch (tamode) { case 0: // invalid, should cause MMU exception - fatalerror("680x0 PMMU: Unhandled Table A mode %d (addr_in %08x)\n", tamode, addr_in); + fatalerror("680x0 PMMU: Unhandled Table A mode %d (addr_in %08x PC %x)\n", tamode, addr_in, m68k->pc); break; case 2: // 4-byte table B descriptor tofs *= 4; -// logerror("PMMU: reading table B entry at %08x\n", tofs + tptr); +// if (verbose) logerror("PMMU: reading table B entry at %08x\n", tofs + tptr); tbl_entry = m68k->program->read_dword(tofs + tptr); tbmode = tbl_entry & 3; -// logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); break; case 3: // 8-byte table B descriptor tofs *= 8; -// logerror("PMMU: reading table B entries at %08x\n", tofs + tptr); +// if (verbose) logerror("PMMU: reading table B entries at %08x\n", tofs + tptr); tbl_entry2 = m68k->program->read_dword(tofs + tptr); tbl_entry = m68k->program->read_dword(tofs + tptr + 4); tbmode = tbl_entry2 & 3; -// logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); break; case 1: // early termination descriptor @@ -120,19 +264,19 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) case 2: // 4-byte table C descriptor tofs *= 4; -// logerror("PMMU: reading table C entry at %08x\n", tofs + tptr); +// if (verbose) logerror("PMMU: reading table C entry at %08x\n", tofs + tptr); tbl_entry = m68k->program->read_dword(tofs + tptr); tcmode = tbl_entry & 3; -// logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x mode %x tofs %x\n", addr_in, tbl_entry, tbmode, tofs); break; case 3: // 8-byte table C descriptor tofs *= 8; -// logerror("PMMU: reading table C entries at %08x\n", tofs + tptr); +// if (verbose) logerror("PMMU: reading table C entries at %08x\n", tofs + tptr); tbl_entry2 = m68k->program->read_dword(tofs + tptr); tbl_entry = m68k->program->read_dword(tofs + tptr + 4); tcmode = tbl_entry2 & 3; -// logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); +// if (verbose) logerror("PMMU: addr %08x entry %08x entry2 %08x mode %x tofs %x\n", addr_in, tbl_entry, tbl_entry2, tbmode, tofs); break; case 1: // termination descriptor @@ -150,9 +294,9 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) switch (tcmode) { 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, m68k->pc); + case 2: // 4-byte table D descriptor + case 3: // 8-byte table D descriptor + fatalerror("680x0 PMMU: Unhandled Table C mode %d (addr_in %08x PC %x)\n", tbmode, addr_in, m68k->pc); break; case 1: // termination descriptor @@ -165,8 +309,9 @@ INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) } } + pmmu_atc_add(m68k, addr_in, addr_out); -// logerror("PMMU: [%08x] => [%08x]\n", addr_in, addr_out); + //logerror("PMMU: [%08x] => [%08x]\n", addr_in, addr_out); return addr_out; } @@ -202,17 +347,28 @@ void m68881_mmu_ops(m68ki_cpu_core *m68k) if ((modes & 0xfde0) == 0x2000) // PLOAD { - logerror("680x0: unhandled PLOAD\n"); + UINT32 ltmp = DECODE_EA_32(m68k, ea); + UINT32 ptmp; + + ptmp = ltmp; + if (m68k->pmmu_enabled) + { + ptmp = pmmu_translate_addr(m68k, ltmp); + } + +// logerror("680x0: PLOADing ATC with logical %08x => phys %08x\n", ltmp, ptmp); + + pmmu_atc_add(m68k, ltmp, ptmp); return; } else if ((modes & 0xe200) == 0x2000) // PFLUSH { - logerror("680x0: unhandled PFLUSH PC=%x\n", m68k->pc); + pmmu_atc_flush(m68k); return; } else if (modes == 0xa000) // PFLUSHR { - logerror("680x0: unhandled PFLUSHR\n"); + pmmu_atc_flush(m68k); return; } else if (modes == 0x2800) // PVALID (FORMAT 1) @@ -257,37 +413,86 @@ void m68881_mmu_ops(m68ki_cpu_core *m68k) break; } } - else + else // top 3 bits of modes: 010 for this, 011 for status, 000 for transparent translation regs { - switch ((modes>>10) & 7) + switch ((modes>>13) & 7) { - case 0: // translation control register - m68k->mmu_tc = READ_EA_32(m68k, ea); + case 0: + { + UINT32 temp = READ_EA_32(m68k, ea); - if (m68k->mmu_tc & 0x80000000) - { - m68k->pmmu_enabled = 1; - } - else - { - m68k->pmmu_enabled = 0; + if (((modes>>10) & 7) == 2) + { + m68k->mmu_tt0 = temp; + } + else if (((modes>>10) & 7) == 3) + { + m68k->mmu_tt1 = temp; + } } break; - case 2: // supervisor root pointer - temp64 = READ_EA_64(m68k, ea); - m68k->mmu_srp_limit = (temp64>>32) & 0xffffffff; - m68k->mmu_srp_aptr = temp64 & 0xffffffff; + case 1: + logerror("680x0: unknown PMOVE case 1, PC %x\n", m68k->pc); break; - case 3: // CPU root pointer - temp64 = READ_EA_64(m68k, ea); - m68k->mmu_crp_limit = (temp64>>32) & 0xffffffff; - m68k->mmu_crp_aptr = temp64 & 0xffffffff; + case 2: + switch ((modes>>10) & 7) + { + case 0: // translation control register + m68k->mmu_tc = READ_EA_32(m68k, ea); +// logerror("PMMU: TC = %08x\n", m68k->mmu_tc); + + if (m68k->mmu_tc & 0x80000000) + { + m68k->pmmu_enabled = 1; +// logerror("PMMU enabled\n"); + } + else + { + m68k->pmmu_enabled = 0; +// logerror("PMMU disabled\n"); + } + + if (!(modes & 0x100)) // flush ATC on moves to TC, SRP, CRP with FD bit clear + { + pmmu_atc_flush(m68k); + } + break; + + case 2: // supervisor root pointer + temp64 = READ_EA_64(m68k, ea); + m68k->mmu_srp_limit = (temp64>>32) & 0xffffffff; + m68k->mmu_srp_aptr = temp64 & 0xffffffff; +// logerror("PMMU: SRP limit = %08x aptr = %08x\n", m68k->mmu_srp_limit, m68k->mmu_srp_aptr); + if (!(modes & 0x100)) + { + pmmu_atc_flush(m68k); + } + break; + + case 3: // CPU root pointer + temp64 = READ_EA_64(m68k, ea); + m68k->mmu_crp_limit = (temp64>>32) & 0xffffffff; + m68k->mmu_crp_aptr = temp64 & 0xffffffff; +// logerror("PMMU: CRP limit = %08x aptr = %08x\n", m68k->mmu_crp_limit, m68k->mmu_crp_aptr); + if (!(modes & 0x100)) + { + pmmu_atc_flush(m68k); + } + break; + + default: + logerror("680x0: PMOVE to unknown MMU register %x, PC %x\n", (modes>>10) & 7, m68k->pc); + break; + } break; - default: - logerror("680x0: PMOVE to unknown MMU register %x, PC %x\n", (modes>>10) & 7, m68k->pc); + case 3: // MMU status + { + UINT32 temp = READ_EA_32(m68k, ea); + logerror("680x0: unsupported PMOVE %x to MMU status, PC %x\n", temp, m68k->pc); + } break; } } @@ -307,6 +512,7 @@ void m68881_mmu_ops(m68ki_cpu_core *m68k) default: logerror("680x0: unknown PMOVE mode %x (modes %04x) (PC %x)\n", (modes>>13) & 0x7, modes, m68k->pc); break; + } } break; @@ -318,3 +524,34 @@ void m68881_mmu_ops(m68ki_cpu_core *m68k) } } + +/* Apple HMMU translation is much simpler */ +INLINE UINT32 hmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in) +{ + UINT32 addr_out; + + addr_out = addr_in; + + // check if LC mode is enabled + if (m68k->hmmu_enabled == M68K_HMMU_ENABLE_LC) + { + addr_in &= 0xffffff; + addr_out &= 0xffffff; + + if ((addr_in >= 0xa00000) && (addr_in <= 0xdfffff)) + { + addr_out |= 0x40000000; + } + if ((addr_in >= 0xe00000) && (addr_in <= 0xefffff)) + { + addr_out &= 0xfffff; + addr_out |= 0xfe000000; + } + else if (addr_in >= 0xf00000) + { + addr_out |= 0x50000000; + } + } + + return addr_out; +}