m680x0 update:

- Added working PMMU address translation (not feature complete, but sufficient
  to boot several 68030 Macs in MESS)
- Fixed up disassembly of some PMMU instructions
- Added "68020 with 68851" CPU type
This commit is contained in:
R. Belmont 2009-10-12 02:50:35 +00:00
parent acf91ebdf7
commit 17fc4a4a78
7 changed files with 475 additions and 8 deletions

1
.gitattributes vendored
View File

@ -241,6 +241,7 @@ src/emu/cpu/m68000/m68kcpu.h svneol=native#text/plain
src/emu/cpu/m68000/m68kdasm.c svneol=native#text/plain
src/emu/cpu/m68000/m68kfpu.c svneol=native#text/plain
src/emu/cpu/m68000/m68kmake.c svneol=native#text/plain
src/emu/cpu/m68000/m68kmmu.h svneol=native#text/plain
src/emu/cpu/m6805/6805dasm.c svneol=native#text/plain
src/emu/cpu/m6805/6805ops.c svneol=native#text/plain
src/emu/cpu/m6805/m6805.c svneol=native#text/plain

View File

@ -76,6 +76,7 @@ CPU_GET_INFO( m68008 );
CPU_GET_INFO( m68010 );
CPU_GET_INFO( m68ec020 );
CPU_GET_INFO( m68020 );
CPU_GET_INFO( m68020pmmu );
CPU_GET_INFO( m68ec030 );
CPU_GET_INFO( m68030 );
CPU_GET_INFO( m68ec040 );
@ -88,6 +89,7 @@ CPU_GET_INFO( scc68070 );
#define CPU_M68010 CPU_GET_INFO_NAME( m68010 )
#define CPU_M68EC020 CPU_GET_INFO_NAME( m68ec020 )
#define CPU_M68020 CPU_GET_INFO_NAME( m68020 )
#define CPU_M68020_68851 CPU_GET_INFO_NAME( m68020pmmu )
#define CPU_M68EC030 CPU_GET_INFO_NAME( m68ec030 )
#define CPU_M68030 CPU_GET_INFO_NAME( m68030 )
#define CPU_M68EC040 CPU_GET_INFO_NAME( m68ec040 )

View File

@ -8015,7 +8015,81 @@ M68KMAKE_OP(pmove, 32, ., .)
modes = m68ki_read_imm_16(m68k);
ea = M68KMAKE_GET_EA_AY_32;
logerror("680x0: unhandled PMOVE modes %x ea %x\n", modes, ea);
if ((modes & 0xfde0) == 0x2000) // PLOAD
{
logerror("680x0: unhandled PLOAD\n");
return;
}
if ((modes & 0xe200) == 0x2000) // PFLUSHA
{
logerror("680x0: unhandled PFLUSHA\n");
return;
}
switch ((modes>>13) & 0x7)
{
case 0: // MC68030/040 form with FD bit
case 2: // MC68881 form, FD never set
if (modes & 0x200)
{
logerror("680x0: PMOVE from MMU not supported\n");
}
else
{
switch ((modes>>10) & 7)
{
case 0: // translation control register
m68k->mmu_tc = memory_read_word_32be(m68k->program, ea)<<16;
m68k->mmu_tc |= memory_read_word_32be(m68k->program, ea+2);
if (m68k->mmu_tc & 0x80000000)
{
m68k->pmmu_enabled = 1;
}
else
{
m68k->pmmu_enabled = 0;
}
break;
case 2: // supervisor root pointer
m68k->mmu_srp_limit = memory_read_word_32be(m68k->program, ea)<<16;
m68k->mmu_srp_limit |= memory_read_word_32be(m68k->program, ea+2);
m68k->mmu_srp_aptr = memory_read_word_32be(m68k->program, ea+4)<<16;
m68k->mmu_srp_aptr |= memory_read_word_32be(m68k->program, ea+6);
break;
case 3: // CPU root pointer
m68k->mmu_crp_limit = memory_read_word_32be(m68k->program, ea)<<16;
m68k->mmu_crp_limit |= memory_read_word_32be(m68k->program, ea+2);
m68k->mmu_crp_aptr = memory_read_word_32be(m68k->program, ea+4)<<16;
m68k->mmu_crp_aptr |= memory_read_word_32be(m68k->program, ea+6);
break;
default:
logerror("680x0: PMOVE to unknown MMU register %x\n", (modes>>10) & 7);
break;
}
}
break;
case 3: // MC68030 to/from status reg
if (modes & 0x200)
{
memory_write_word_32be(m68k->program, ea, m68k->mmu_sr);
}
else
{
m68k->mmu_sr = memory_read_word_32be(m68k->program, ea);
}
break;
default:
logerror("680x0: unknown PMOVE mode %x (modes %04x) (PC %x)\n", (modes>>13) & 0x7, modes, m68k->pc);
break;
}
}
else
{

View File

@ -5,7 +5,7 @@
#if 0
static const char copyright_notice[] =
"MUSASHI\n"
"Version 4.10 (2009-09-27)\n"
"Version 4.5 (2009-10-09)\n"
"A portable Motorola M680x0 processor emulation engine.\n"
"Copyright Karl Stenerud. All rights reserved.\n"
"\n"
@ -38,6 +38,8 @@ static const char copyright_notice[] =
#include "m68kfpu.c"
#include "debugger.h"
#include "m68kmmu.h"
extern void m68040_fpu_op0(m68ki_cpu_core *m68k);
extern void m68040_fpu_op1(m68ki_cpu_core *m68k);
@ -611,6 +613,21 @@ static void m68k_postload(running_machine *machine, void *param)
m68ki_jump(m68k, REG_PC);
}
/* translate logical to physical addresses */
static CPU_TRANSLATE( m68k )
{
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->pmmu_enabled))
{
*address = pmmu_translate_addr(m68k, *address);
}
}
return TRUE;
}
/* Execute some instructions until we use up cycles clock cycles */
static CPU_EXECUTE( m68k )
@ -718,6 +735,9 @@ static CPU_RESET( m68k )
{
m68ki_cpu_core *m68k = get_safe_token(device);
/* Disable the PMMU on reset */
m68k->pmmu_enabled = 0;
/* Clear all stop levels and eat up all remaining cycles */
m68k->stopped = 0;
if (m68k->remaining_cycles > 0)
@ -890,6 +910,7 @@ static CPU_GET_INFO( m68k )
case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(m68k); break;
case CPUINFO_FCT_IMPORT_STATE: info->import_state = CPU_IMPORT_STATE_NAME(m68k); break;
case CPUINFO_FCT_EXPORT_STATE: info->export_state = CPU_EXPORT_STATE_NAME(m68k); break;
case CPUINFO_FCT_TRANSLATE: info->translate = CPU_TRANSLATE_NAME(m68k); break;
/* --- the following bits of info are returned as pointers --- */
case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &m68k->remaining_cycles; break;
@ -898,7 +919,7 @@ static CPU_GET_INFO( m68k )
/* --- the following bits of info are returned as NULL-terminated strings --- */
case DEVINFO_STR_NAME: /* set per-core */ break;
case DEVINFO_STR_FAMILY: strcpy(info->s, "Motorola 68K"); break;
case DEVINFO_STR_VERSION: strcpy(info->s, "4.00"); break;
case DEVINFO_STR_VERSION: strcpy(info->s, "4.50"); break;
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Karl Stenerud. All rights reserved. (2.1 fixes HJB)"); break;
@ -1062,7 +1083,139 @@ static const m68k_memory_interface interface_d32 =
writelong_d32
};
/* interface for 32-bit data bus with PMMU (68EC020, 68020) */
static UINT8 read_byte_32_mmu(const address_space *space, offs_t address)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
return memory_read_byte_32be(space, address);
}
static void write_byte_32_mmu(const address_space *space, offs_t address, UINT8 data)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
memory_write_byte_32be(space, address, data);
}
static UINT16 read_immediate_16_mmu(const address_space *space, offs_t address)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
return memory_decrypted_read_word(space, (address) ^ m68k->memory.opcode_xor);
}
/* potentially misaligned 16-bit reads with a 32-bit data bus (and 24-bit address bus) */
static UINT16 readword_d32_mmu(const address_space *space, offs_t address)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
UINT16 result;
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
if (!(address & 1))
return memory_read_word_32be(space, address);
result = memory_read_byte_32be(space, address) << 8;
return result | memory_read_byte_32be(space, address + 1);
}
/* potentially misaligned 16-bit writes with a 32-bit data bus (and 24-bit address bus) */
static void writeword_d32_mmu(const address_space *space, offs_t address, UINT16 data)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
if (!(address & 1))
{
memory_write_word_32be(space, address, data);
return;
}
memory_write_byte_32be(space, address, data >> 8);
memory_write_byte_32be(space, address + 1, data);
}
/* potentially misaligned 32-bit reads with a 32-bit data bus (and 24-bit address bus) */
static UINT32 readlong_d32_mmu(const address_space *space, offs_t address)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
UINT32 result;
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
if (!(address & 3))
return memory_read_dword_32be(space, address);
else if (!(address & 1))
{
result = memory_read_word_32be(space, address) << 16;
return result | memory_read_word_32be(space, address + 2);
}
result = memory_read_byte_32be(space, address) << 24;
result |= memory_read_word_32be(space, address + 1) << 8;
return result | memory_read_byte_32be(space, address + 3);
}
/* potentially misaligned 32-bit writes with a 32-bit data bus (and 24-bit address bus) */
static void writelong_d32_mmu(const address_space *space, offs_t address, UINT32 data)
{
m68ki_cpu_core *m68k = get_safe_token(space->cpu);
if (m68k->pmmu_enabled)
{
address = pmmu_translate_addr(m68k, address);
}
if (!(address & 3))
{
memory_write_dword_32be(space, address, data);
return;
}
else if (!(address & 1))
{
memory_write_word_32be(space, address, data >> 16);
memory_write_word_32be(space, address + 2, data);
return;
}
memory_write_byte_32be(space, address, data >> 24);
memory_write_word_32be(space, address + 1, data >> 8);
memory_write_byte_32be(space, address + 3, data);
}
static const m68k_memory_interface interface_d32_mmu =
{
WORD_XOR_BE(0),
read_immediate_16_mmu,
read_byte_32_mmu,
readword_d32_mmu,
readlong_d32_mmu,
write_byte_32_mmu,
writeword_d32_mmu,
writelong_d32_mmu
};
void m68k_set_reset_callback(const device_config *device, m68k_reset_func callback)
@ -1301,6 +1454,30 @@ CPU_GET_INFO( m68020 )
}
}
// 68020 with 68851 PMMU
static CPU_INIT( m68020pmmu )
{
m68ki_cpu_core *m68k = get_safe_token(device);
CPU_INIT_CALL(m68020);
m68k->has_pmmu = 1;
m68k->memory = interface_d32_mmu;
}
CPU_GET_INFO( m68020pmmu )
{
switch (state)
{
/* --- the following bits of info are returned as pointers to data or functions --- */
case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(m68020pmmu); break;
/* --- the following bits of info are returned as NULL-terminated strings --- */
case DEVINFO_STR_NAME: strcpy(info->s, "68020, 68851"); break;
default: CPU_GET_INFO_CALL(m68020); break;
}
}
/****************************************************************************
* M680EC20 section
@ -1361,7 +1538,7 @@ static CPU_INIT( m68030 )
m68k->cpu_type = CPU_TYPE_030;
m68k->state.subtypemask = m68k->cpu_type;
m68k->dasm_type = M68K_CPU_TYPE_68030;
m68k->memory = interface_d32;
m68k->memory = interface_d32_mmu;
m68k->sr_mask = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
m68k->cyc_instruction = m68ki_cycles[3];
m68k->cyc_exception = m68ki_exception_cycle_table[3];
@ -1480,7 +1657,7 @@ static CPU_INIT( m68040 )
m68k->cpu_type = CPU_TYPE_040;
m68k->state.subtypemask = m68k->cpu_type;
m68k->dasm_type = M68K_CPU_TYPE_68040;
m68k->memory = interface_d32;
m68k->memory = interface_d32_mmu;
m68k->sr_mask = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
m68k->cyc_instruction = m68ki_cycles[4];
m68k->cyc_exception = m68ki_exception_cycle_table[4];
@ -1568,7 +1745,7 @@ static CPU_INIT( m68ec040 )
m68k->cyc_movem_l = 2;
m68k->cyc_shift = 0;
m68k->cyc_reset = 518;
m68k->has_pmmu = 1;
m68k->has_pmmu = 0;
}
CPU_GET_INFO( m68ec040 )

View File

@ -3,7 +3,7 @@
/* ======================================================================== */
/*
* MUSASHI
* Version 4.00
* Version 4.50
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright Karl Stenerud. All rights reserved.
@ -581,6 +581,7 @@ 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 pmmu_enabled; /* Indicates if the PMMU is enabled */
/* Clocks required for instructions / exceptions */
UINT32 cyc_bcc_notake_b;
@ -635,6 +636,12 @@ struct _m68ki_cpu_core
UINT16 save_sr;
UINT8 save_stopped;
UINT8 save_halted;
/* PMMU registers */
UINT32 mmu_crp_aptr, mmu_crp_limit;
UINT32 mmu_srp_aptr, mmu_srp_limit;
UINT32 mmu_tc;
UINT16 mmu_sr;
};

View File

@ -3027,6 +3027,32 @@ static void d68030_pmove(void)
// do this after fetching the second PMOVE word so we properly get the 3rd if necessary
str = get_ea_mode_str_32(g_cpu_ir);
if ((modes & 0xfde0) == 0x2000) // PLOAD
{
if (modes & 0x0200)
{
sprintf(g_dasm_str, "pload #%d, %s", (modes>>10)&7, str);
}
else
{
sprintf(g_dasm_str, "pload %s, #%d", str, (modes>>10)&7);
}
return;
}
if ((modes & 0xe200) == 0x2000) // PFLUSHA
{
if (modes & 0x0200)
{
sprintf(g_dasm_str, "pflush %s, %s", g_mmuregs[(modes>>10)&7], str);
}
else
{
sprintf(g_dasm_str, "pflush %s, %s", str, g_mmuregs[(modes>>10)&7]);
}
return;
}
switch ((modes>>13) & 0x7)
{
case 0: // MC68030/040 form with FD bit
@ -3803,7 +3829,7 @@ CPU_DISASSEMBLE( m68040 )
return m68k_disassemble_raw(buffer, pc, oprom, opram, M68K_CPU_TYPE_68040);
}
// f028 2215 0008
/* ======================================================================== */
/* ============================== END OF FILE ============================= */

View File

@ -0,0 +1,180 @@
/*
m68kmmu.h - PMMU implementation for 68851/68030/68040
By R. Belmont
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
*/
/*
pmmu_translate_addr: perform 68851/68030-style PMMU address translation
*/
INLINE UINT32 pmmu_translate_addr(m68ki_cpu_core *m68k, UINT32 addr_in)
{
UINT32 addr_out, tbl_entry, tbl_entry2, tamode, tbmode;
UINT32 root_aptr, root_limit, tofs, is, abits, bbits, cbits;
UINT32 resolved, tptr, shift;
resolved = 0;
addr_out = addr_in;
// if SRP is enabled and we're in supervisor mode, use it
if ((m68k->mmu_tc & 0x02000000) && (m68ki_get_sr(m68k) & 0x2000))
{
root_aptr = m68k->mmu_srp_aptr;
root_limit = m68k->mmu_srp_limit;
}
else // else use the CRP
{
root_aptr = m68k->mmu_crp_aptr;
root_limit = m68k->mmu_crp_limit;
}
// get initial shift (# of top bits to ignore)
is = (m68k->mmu_tc>>16) & 0xf;
abits = (m68k->mmu_tc>>12)&0xf;
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);
// get table A offset
tofs = (addr_in<<is)>>(32-abits);
// find out what format table A is
switch (root_limit & 3)
{
case 0: // invalid, should cause MMU exception
case 1: // page descriptor, should cause direct mapping
fatalerror("680x0 PMMU: Unhandled root mode\n");
break;
case 2: // valid 4 byte descriptors
tofs *= 4;
// logerror("PMMU: reading table A entry at %08x\n", tofs + (root_aptr & 0xfffffffc));
tbl_entry = memory_read_dword_32be(m68k->program, 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);
break;
case 3: // valid 8 byte descriptors
tofs *= 8;
// logerror("PMMU: reading table A entries at %08x\n", tofs + (root_aptr & 0xfffffffc));
tbl_entry2 = memory_read_dword_32be(m68k->program, tofs + (root_aptr & 0xfffffffc));
tbl_entry = memory_read_dword_32be(m68k->program, 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);
break;
}
// get table B offset and pointer
tofs = (addr_in<<(is+abits))>>(32-bbits);
tptr = tbl_entry & 0xfffffff0;
// find out what format table B is, if any
switch (tamode)
{
case 0: // invalid, should cause MMU exception
fatalerror("680x0 PMMU: Unhandled Table A mode %d (addr_in %08x)\n", tamode, addr_in);
break;
case 2: // 4-byte table B descriptor
tofs *= 4;
// logerror("PMMU: reading table B entry at %08x\n", tofs + tptr);
tbl_entry = memory_read_dword_32be(m68k->program, tofs + tptr);
tbmode = tbl_entry & 3;
// 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);
tbl_entry2 = memory_read_dword_32be(m68k->program, tofs + tptr);
tbl_entry = memory_read_dword_32be(m68k->program, 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);
break;
case 1: // early termination descriptor
tbl_entry &= 0xffffff00;
shift = is+abits;
addr_out = ((addr_in<<shift)>>shift) + tbl_entry;
resolved = 1;
break;
}
// if table A wasn't early-out, continue to process table B
if (!resolved)
{
switch (tbmode)
{
case 0: // invalid, should cause MMU exception
case 2: // 4-byte table C descriptor
case 3: // 8-byte table C descriptor
fatalerror("680x0 PMMU: Unhandled Table B mode %d (addr_in %08x)\n", tbmode, addr_in);
break;
case 1: // early termination descriptor
tbl_entry &= 0xffffff00;
shift = is+abits+bbits;
addr_out = ((addr_in<<shift)>>shift) + tbl_entry;
resolved = 1;
break;
}
}
// if ((addr_in < 0x40000000) || (addr_in > 0x4fffffff)) printf("PMMU: [%08x] => [%08x]\n", addr_in, addr_out);
return addr_out;
}
/*
maincpu at 40804366: called unimplemented instruction f000 (cpgen)
PMMU: tcr 80f05570 limit 7fff0003 aptr 043ffcc0 is 0
PMMU: reading table A entries at 043ffce0
PMMU: addr 4080438a entry 00000000 entry2 7ffffc18 mode 0 aofs 20
680x0 PMMU: Unhandled Table A mode 0
enable, PS = f
tblA @ 043ffcc0:
043ffcc0 0001fc0a 043ffcb0 => 00000019 04000019
043ffcc8 7ffffc18 00000000
043ffcd0 7ffffc18 00000000
043ffcd8 7ffffc18 00000000
043ffce0 7ffffc18 00000000
043ffce8 7ffffc18 00000000
043ffcf0 7ffffc18 00000000
043ffcf8 7ffffc18 00000000
043ffd00 7ffffc19 40000000
043ffd08 7ffffc19 48000000
043ffd10 7ffffc59 50000000
043ffd18 7ffffc59 58000000
043ffd20 7ffffc59 60000000
043ffd28 7ffffc59 68000000
043ffd30 7ffffc59 70000000
043ffd38 7ffffc59 78000000
043ffd40 7ffffc59 80000000
043ffd48 7ffffc59 88000000
043ffd50 7ffffc59 90000000
043ffd58 7ffffc59 98000000
043ffd60 7ffffc59 a0000000
043ffd68 7ffffc59 a8000000
043ffd70 7ffffc59 b0000000
043ffd78 7ffffc59 b8000000
043ffd80 7ffffc59 c0000000
043ffd88 7ffffc59 c8000000
043ffd90 7ffffc59 d0000000
043ffd98 7ffffc59 d8000000
043ffda0 7ffffc59 e0000000
043ffda8 7ffffc59 e8000000
043ffdb0 7ffffc59 f0000000
043ffdb8 7ffffc59 f8000000
*/