Merge pull request #52 from zelurker/master

the pmmu emulation
This commit is contained in:
Karl Stenerud 2019-12-13 20:00:27 +01:00 committed by GitHub
commit 710f795aaf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 814 additions and 119 deletions

1
.gitignore vendored
View file

@ -24,3 +24,4 @@ Thumbs.db
m68kmake
m68kops.?
sim
tags

View file

@ -1,7 +1,7 @@
# Just a basic makefile to quickly test that everyting is working, it just
# compiles the .o and the generator
MUSASHIFILES = m68kcpu.c m68kdasm.c m68kfpu.c
MUSASHIFILES = m68kcpu.c m68kdasm.c
MUSASHIGENCFILES = m68kops.c
MUSASHIGENHFILES = m68kops.h
MUSASHIGENERATOR = m68kmake

View file

@ -4,7 +4,7 @@ OSD_DOS = osd_dos.c
OSDFILES = osd_linux.c # $(OSD_DOS)
MAINFILES = sim.c
MUSASHIFILES = m68kcpu.c m68kdasm.c m68kfpu.c
MUSASHIFILES = m68kcpu.c m68kdasm.c
MUSASHIGENCFILES = m68kops.c
MUSASHIGENHFILES = m68kops.h
MUSASHIGENERATOR = m68kmake

1
example/m68kmmu.h Symbolic link
View file

@ -0,0 +1 @@
../m68kmmu.h

1
m68k.h
View file

@ -101,6 +101,7 @@ enum
M68K_CPU_TYPE_68EC030,
M68K_CPU_TYPE_68030,
M68K_CPU_TYPE_68EC040,
M68K_CPU_TYPE_68LC040,
M68K_CPU_TYPE_68040,
M68K_CPU_TYPE_SCC68070
};

View file

@ -283,6 +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();
/* ======================================================================== */
/* ========================= INSTRUCTION HANDLERS ========================= */
@ -769,7 +770,7 @@ pack 16 mm axy7 1000111101001111 .......... . . U U U . . 13 1
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
pmove 32 . . 1111000000...... A..DXWLdx. . . S S S . . 8 8 8
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
ror 16 s . 1110...001011... .......... U U U U U 6 6 8 8 8
@ -914,7 +915,7 @@ M68KMAKE_OP(1111, 0, ., .)
M68KMAKE_OP(040fpu0, 32, ., .)
{
if(CPU_TYPE_IS_040_PLUS(CPU_TYPE))
if(CPU_TYPE_IS_030_PLUS(CPU_TYPE))
{
m68040_fpu_op0();
return;
@ -925,7 +926,7 @@ M68KMAKE_OP(040fpu0, 32, ., .)
M68KMAKE_OP(040fpu1, 32, ., .)
{
if(CPU_TYPE_IS_040_PLUS(CPU_TYPE))
if(CPU_TYPE_IS_030_PLUS(CPU_TYPE))
{
m68040_fpu_op1();
return;
@ -8448,28 +8449,21 @@ M68KMAKE_OP(pea, 32, ., .)
m68ki_push_32(ea);
}
M68KMAKE_OP(pflush, 32, ., .)
{
if ((CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) && (HAS_PMMU))
{
fprintf(stderr,"680x0: unhandled PFLUSH\n");
fprintf(stderr,"68040: unhandled PFLUSH\n");
return;
}
m68ki_exception_1111();
}
M68KMAKE_OP(pmove, 32, ., .)
M68KMAKE_OP(pmmu, 32, ., .)
{
uint16 modes;
uint32 ea;
if ((CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) && (HAS_PMMU))
{
modes = m68ki_read_imm_16();
ea = M68KMAKE_GET_EA_AY_32;
fprintf(stderr,"680x0: unhandled PMOVE modes %x ea %x\n", modes, ea);
m68881_mmu_ops();
}
else
{

View file

@ -184,6 +184,9 @@
#define M68K_LOG_1010_1111 OPT_OFF
#define M68K_LOG_FILEHANDLE some_file_handle
/* Emulate PMMU : if you enable this, there will be a test to see if the current chip has some enabled pmmu added to every memory access,
* so enable this only if it's useful */
#define M68K_EMULATE_PMMU OPT_ON
/* ----------------------------- COMPATIBILITY ---------------------------- */

View file

@ -3,7 +3,7 @@
/* ======================================================================== */
/*
* MUSASHI
* Version 4.10
* Version 4.55
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright Karl Stenerud. All rights reserved.
@ -40,6 +40,7 @@
extern void m68040_fpu_op0(void);
extern void m68040_fpu_op1(void);
extern void m68881_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);
@ -47,6 +48,9 @@ extern void m68ki_build_opcode_table(void);
#include "m68kops.h"
#include "m68kcpu.h"
#include "m68kfpu.c"
#include "m68kmmu.h" // uses some functions from m68kfpu.c which are static !
/* ======================================================================== */
/* ================================= DATA ================================= */
/* ======================================================================== */
@ -599,7 +603,6 @@ static void default_instr_hook_callback(unsigned int pc)
jmp_buf m68ki_aerr_trap;
#endif /* M68K_EMULATE_ADDRESS_ERROR */
/* ======================================================================== */
/* ================================= API ================================== */
/* ======================================================================== */
@ -896,6 +899,39 @@ void m68k_set_cpu_type(unsigned int cpu_type)
CYC_RESET = 518;
HAS_PMMU = 1;
return;
case M68K_CPU_TYPE_68EC040: // Just a 68040 without pmmu apparently...
CPU_TYPE = CPU_TYPE_EC040;
CPU_ADDRESS_MASK = 0xffffffff;
CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
CYC_INSTRUCTION = m68ki_cycles[4];
CYC_EXCEPTION = m68ki_exception_cycle_table[4];
CYC_BCC_NOTAKE_B = -2;
CYC_BCC_NOTAKE_W = 0;
CYC_DBCC_F_NOEXP = 0;
CYC_DBCC_F_EXP = 4;
CYC_SCC_R_TRUE = 0;
CYC_MOVEM_W = 2;
CYC_MOVEM_L = 2;
CYC_SHIFT = 0;
CYC_RESET = 518;
HAS_PMMU = 0;
return;
case M68K_CPU_TYPE_68LC040:
CPU_TYPE = CPU_TYPE_LC040;
m68ki_cpu.sr_mask = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
m68ki_cpu.cyc_instruction = m68ki_cycles[4];
m68ki_cpu.cyc_exception = m68ki_exception_cycle_table[4];
m68ki_cpu.cyc_bcc_notake_b = -2;
m68ki_cpu.cyc_bcc_notake_w = 0;
m68ki_cpu.cyc_dbcc_f_noexp = 0;
m68ki_cpu.cyc_dbcc_f_exp = 4;
m68ki_cpu.cyc_scc_r_true = 0;
m68ki_cpu.cyc_movem_w = 2;
m68ki_cpu.cyc_movem_l = 2;
m68ki_cpu.cyc_shift = 0;
m68ki_cpu.cyc_reset = 518;
HAS_PMMU = 1;
return;
}
}
@ -1062,6 +1098,9 @@ void m68k_pulse_bus_error(void)
/* Pulse the RESET line on the CPU */
void m68k_pulse_reset(void)
{
/* Disable the PMMU on reset */
m68ki_cpu.pmmu_enabled = 0;
/* Clear all stop levels and eat up all remaining cycles */
CPU_STOPPED = 0;
SET_CYCLES(0);
@ -1104,7 +1143,6 @@ void m68k_pulse_halt(void)
CPU_STOPPED |= STOP_LEVEL_HALT;
}
/* Get and set the current CPU context */
/* This is to allow for multiple CPUs */
unsigned int m68k_context_size()

View file

@ -3,7 +3,7 @@
/* ======================================================================== */
/*
* MUSASHI
* Version 3.32
* Version 4.5
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright Karl Stenerud. All rights reserved.
@ -172,8 +172,9 @@ extern "C" {
#define CPU_TYPE_EC030 (0x00000020)
#define CPU_TYPE_030 (0x00000040)
#define CPU_TYPE_EC040 (0x00000080)
#define CPU_TYPE_040 (0x00000100)
#define CPU_TYPE_SCC070 (0x00000200)
#define CPU_TYPE_LC040 (0x00000100)
#define CPU_TYPE_040 (0x00000200)
#define CPU_TYPE_SCC070 (0x00000400)
/* Different ways to stop the CPU */
#define STOP_LEVEL_STOP 1
@ -368,6 +369,7 @@ extern "C" {
#define CYC_SHIFT m68ki_cpu.cyc_shift
#define CYC_RESET m68ki_cpu.cyc_reset
#define HAS_PMMU m68ki_cpu.has_pmmu
#define PMMU_ENABLED m68ki_cpu.pmmu_enabled
#define RESET_CYCLES m68ki_cpu.reset_cycles
@ -946,6 +948,7 @@ typedef struct
uint instr_mode; /* Stores whether we are in instruction mode or group 0/1 exception mode */
uint 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 */
uint reset_cycles;
/* Clocks required for instructions / exceptions */
@ -963,6 +966,12 @@ typedef struct
uint virq_state;
uint nmi_pending;
/* PMMU registers */
uint mmu_crp_aptr, mmu_crp_limit;
uint mmu_srp_aptr, mmu_srp_limit;
uint mmu_tc;
uint16 mmu_sr;
const uint8* cyc_instruction;
const uint8* cyc_exception;
@ -1012,6 +1021,8 @@ char* m68ki_disassemble_quick(unsigned int pc, unsigned int cpu_type);
/* ---------------------------- Read Immediate ---------------------------- */
extern uint pmmu_translate_addr(uint addr_in);
/* Handles all immediate reads, does address error check, function code setting,
* and prefetching if they are enabled in m68kconf.h
*/
@ -1019,6 +1030,14 @@ static inline uint m68ki_read_imm_16(void)
{
m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_PROGRAM); /* auto-disable (see m68kcpu.h) */
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);
#endif
#endif
#if M68K_EMULATE_PREFETCH
{
uint result;
@ -1047,6 +1066,13 @@ 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);
#endif
#endif
#if M68K_EMULATE_PREFETCH
uint temp_val;
@ -1077,8 +1103,6 @@ static inline uint m68ki_read_imm_32(void)
#endif /* M68K_EMULATE_PREFETCH */
}
/* ------------------------- Top level read/write ------------------------- */
/* Handles all memory accesses (except for immediate reads if they are
@ -1091,6 +1115,12 @@ static inline uint m68ki_read_8_fc(uint address, uint fc)
{
(void)fc;
m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */
#if M68K_EMULATE_PMMU
if (PMMU_ENABLED)
address = pmmu_translate_addr(address);
#endif
return m68k_read_memory_8(ADDRESS_68K(address));
}
static inline uint m68ki_read_16_fc(uint address, uint fc)
@ -1098,6 +1128,12 @@ static inline uint m68ki_read_16_fc(uint address, uint fc)
(void)fc;
m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */
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);
#endif
return m68k_read_memory_16(ADDRESS_68K(address));
}
static inline uint m68ki_read_32_fc(uint address, uint fc)
@ -1105,6 +1141,12 @@ static inline uint m68ki_read_32_fc(uint address, uint fc)
(void)fc;
m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */
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);
#endif
return m68k_read_memory_32(ADDRESS_68K(address));
}
@ -1112,6 +1154,12 @@ static inline void m68ki_write_8_fc(uint address, uint fc, uint value)
{
(void)fc;
m68ki_set_fc(fc); /* auto-disable (see m68kcpu.h) */
#if M68K_EMULATE_PMMU
if (PMMU_ENABLED)
address = pmmu_translate_addr(address);
#endif
m68k_write_memory_8(ADDRESS_68K(address), value);
}
static inline void m68ki_write_16_fc(uint address, uint fc, uint value)
@ -1119,6 +1167,12 @@ 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_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);
#endif
m68k_write_memory_16(ADDRESS_68K(address), value);
}
static inline void m68ki_write_32_fc(uint address, uint fc, uint value)
@ -1126,6 +1180,12 @@ 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_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);
#endif
m68k_write_memory_32(ADDRESS_68K(address), value);
}
@ -1135,11 +1195,16 @@ 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_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);
#endif
m68k_write_memory_32_pd(ADDRESS_68K(address), value);
}
#endif
/* --------------------- Effective Address Calculation -------------------- */
/* The program counter relative addressing modes cause operands to be
@ -1417,8 +1482,6 @@ static inline void m68ki_branch_32(uint offset)
m68ki_pc_changed(REG_PC);
}
/* ---------------------------- Status Register --------------------------- */
/* Set the S flag and change the active stack pointer.

View file

@ -38,6 +38,14 @@
#include <string.h>
#include "m68k.h"
#ifndef uint32
#define uint32 uint
#endif
#ifndef uint16
#define uint16 unsigned short
#endif
#ifndef DECL_SPEC
#define DECL_SPEC
#endif
@ -156,6 +164,7 @@ uint peek_imm_32(void);
/* make signed integers 100% portably */
static int make_int_8(int value);
static int make_int_16(int value);
static int make_int_32(int value);
/* make a string of a hex value */
static char* make_signed_hex_str_8(uint val);
@ -243,6 +252,12 @@ static const char *const g_mmuregs[8] =
"tc", "drp", "srp", "crp", "cal", "val", "sccr", "acr"
};
static const char *const g_mmucond[16] =
{
"bs", "bc", "ls", "lc", "ss", "sc", "as", "ac",
"ws", "wc", "is", "ic", "gs", "gc", "cs", "cc"
};
/* ======================================================================== */
/* =========================== UTILITY FUNCTIONS ========================== */
/* ======================================================================== */
@ -326,6 +341,10 @@ static int make_int_16(int value)
return (value & 0x8000) ? value | ~0xffff : value & 0xffff;
}
static int make_int_32(int value)
{
return (value & 0x80000000) ? value | ~0xffffffff : value & 0xffffffff;
}
/* Get string representation of hex values */
static char* make_signed_hex_str_8(uint val)
@ -1676,8 +1695,8 @@ static void d68040_fpu(void)
};
char mnemonic[40];
uint w2, src, dst_reg;
LIMIT_CPU_TYPES(M68040_PLUS);
uint32 w2, src, dst_reg;
LIMIT_CPU_TYPES(M68030_PLUS);
w2 = read_imm_16();
src = (w2 >> 10) & 0x7;
@ -2555,6 +2574,7 @@ static void d68000_pea(void)
sprintf(g_dasm_str, "pea %s", get_ea_mode_str_32(g_cpu_ir));
}
// this is a 68040-specific form of PFLUSH
static void d68040_pflush(void)
{
LIMIT_CPU_TYPES(M68040_PLUS);
@ -3011,7 +3031,16 @@ static void d68020_unpk_mm(void)
}
static void d68030_pmove(void)
// PFLUSH: 001xxx0xxxxxxxxx
// PLOAD: 001000x0000xxxxx
// PVALID1: 0010100000000000
// PVALID2: 0010110000000xxx
// PMOVE 1: 010xxxx000000000
// PMOVE 2: 011xxxx0000xxx00
// PMOVE 3: 011xxxx000000000
// PTEST: 100xxxxxxxxxxxxx
// PFLUSHR: 1010000000000000
static void d68851_p000(void)
{
char* str;
uint modes = read_imm_16();
@ -3019,6 +3048,48 @@ 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) // PFLUSH
{
sprintf(g_dasm_str, "pflushr %x, %x, %s", modes & 0x1f, (modes>>5)&0xf, str);
return;
}
if (modes == 0xa000) // PFLUSHR
{
sprintf(g_dasm_str, "pflushr %s", str);
}
if (modes == 0x2800) // PVALID (FORMAT 1)
{
sprintf(g_dasm_str, "pvalid VAL, %s", str);
return;
}
if ((modes & 0xfff8) == 0x2c00) // PVALID (FORMAT 2)
{
sprintf(g_dasm_str, "pvalid A%d, %s", modes & 0xf, str);
return;
}
if ((modes & 0xe000) == 0x8000) // PTEST
{
sprintf(g_dasm_str, "ptest #%d, %s", modes & 0x1f, str);
return;
}
switch ((modes>>13) & 0x7)
{
case 0: // MC68030/040 form with FD bit
@ -3064,6 +3135,33 @@ static void d68030_pmove(void)
}
}
static void d68851_pbcc16(void)
{
uint32 temp_pc = g_cpu_pc;
sprintf(g_dasm_str, "pb%s %x", g_mmucond[g_cpu_ir&0xf], temp_pc + make_int_16(read_imm_16()));
}
static void d68851_pbcc32(void)
{
uint32 temp_pc = g_cpu_pc;
sprintf(g_dasm_str, "pb%s %x", g_mmucond[g_cpu_ir&0xf], temp_pc + make_int_32(read_imm_32()));
}
static void d68851_pdbcc(void)
{
uint32 temp_pc = g_cpu_pc;
uint16 modes = read_imm_16();
sprintf(g_dasm_str, "pb%s %x", g_mmucond[modes&0xf], temp_pc + make_int_16(read_imm_16()));
}
// PScc: 0000000000xxxxxx
static void d68851_p001(void)
{
sprintf(g_dasm_str, "MMU 001 group");
}
/* ======================================================================== */
/* ======================= INSTRUCTION TABLE BUILDER ====================== */
@ -3387,7 +3485,11 @@ static const opcode_struct g_opcode_info[] =
{d68000_unlk , 0xfff8, 0x4e58, 0x000},
{d68020_unpk_rr , 0xf1f8, 0x8180, 0x000},
{d68020_unpk_mm , 0xf1f8, 0x8188, 0x000},
{d68030_pmove , 0xffc0, 0xf000, 0x278},
{d68851_p000 , 0xffc0, 0xf000, 0x000},
{d68851_pbcc16 , 0xffc0, 0xf080, 0x000},
{d68851_pbcc32 , 0xffc0, 0xf0c0, 0x000},
{d68851_pdbcc , 0xfff8, 0xf048, 0x000},
{d68851_p001 , 0xffc0, 0xf040, 0x000},
{0, 0, 0, 0}
};
@ -3523,11 +3625,14 @@ unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_
g_cpu_type = TYPE_68020;
g_address_mask = 0xffffffff;
break;
case M68K_CPU_TYPE_68EC030:
case M68K_CPU_TYPE_68030:
g_cpu_type = TYPE_68030;
g_address_mask = 0xffffffff;
break;
case M68K_CPU_TYPE_68040:
case M68K_CPU_TYPE_68EC040:
case M68K_CPU_TYPE_68LC040:
g_cpu_type = TYPE_68040;
g_address_mask = 0xffffffff;
break;
@ -3720,6 +3825,7 @@ unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cp
case M68K_CPU_TYPE_68EC020:
case M68K_CPU_TYPE_68020:
case M68K_CPU_TYPE_68030:
case M68K_CPU_TYPE_68EC030:
if(g_instruction_table[instruction] == d68040_cinv)
return 0;
if(g_instruction_table[instruction] == d68040_cpush)
@ -3734,10 +3840,10 @@ unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cp
return 0;
if(g_instruction_table[instruction] == d68040_move16_al_ai)
return 0;
if(g_instruction_table[instruction] == d68040_pflush)
return 0;
// Fallthrough
case M68K_CPU_TYPE_68040:
case M68K_CPU_TYPE_68EC040:
case M68K_CPU_TYPE_68LC040:
if(g_instruction_table[instruction] == d68020_cpbcc_16)
return 0;
if(g_instruction_table[instruction] == d68020_cpbcc_32)
@ -3758,6 +3864,8 @@ unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cp
return 0;
if(g_instruction_table[instruction] == d68020_cptrapcc_32)
return 0;
if(g_instruction_table[instruction] == d68040_pflush)
return 0;
}
if(cpu_type != M68K_CPU_TYPE_68020 && cpu_type != M68K_CPU_TYPE_68EC020 &&
(g_instruction_table[instruction] == d68020_callm ||
@ -3767,7 +3875,7 @@ unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cp
return 1;
}
// f028 2215 0008
/* ======================================================================== */
/* ============================== END OF FILE ============================= */

319
m68kfpu.c
View file

@ -1,20 +1,10 @@
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include "m68kcpu.h"
#include <stdarg.h>
extern void exit(int);
#if defined(_MSC_VER)
# define NORETURN __declspec(noreturn)
#elif defined(__clang__) || defined(__GNUC__)
# define NORETURN __attribute__((noreturn))
#else
# define NORETURN
#endif
// TODO: Remove this and replace with a non-fatal signaling mechanism
static NORETURN void fatalerror(char *format, ...) {
static void fatalerror(char *format, ...) {
va_list ap;
va_start(ap,format);
fprintf(stderr,format,ap);
@ -98,6 +88,11 @@ static uint8 READ_EA_8(int ea)
{
return REG_D[reg];
}
case 2: // (An)
{
uint32 ea = REG_A[reg];
return m68ki_read_8(ea);
}
case 5: // (d16, An)
{
uint32 ea = EA_AY_DI_8();
@ -112,6 +107,11 @@ static uint8 READ_EA_8(int ea)
{
switch (reg)
{
case 0: // (xxx).W
{
uint32 ea = (uint32)OPER_I_16();
return m68ki_read_8(ea);
}
case 1: // (xxx).L
{
uint32 d1 = OPER_I_16();
@ -163,6 +163,11 @@ static uint16 READ_EA_16(int ea)
{
switch (reg)
{
case 0: // (xxx).W
{
uint32 ea = (uint32)OPER_I_16();
return m68ki_read_16(ea);
}
case 1: // (xxx).L
{
uint32 d1 = OPER_I_16();
@ -220,6 +225,11 @@ static uint32 READ_EA_32(int ea)
{
switch (reg)
{
case 0: // (xxx).W
{
uint32 ea = (uint32)OPER_I_16();
return m68ki_read_32(ea);
}
case 1: // (xxx).L
{
uint32 d1 = OPER_I_16();
@ -245,74 +255,6 @@ static uint32 READ_EA_32(int ea)
return 0;
}
static void WRITE_EA_32(int ea, uint32 data)
{
int mode = (ea >> 3) & 0x7;
int reg = (ea & 0x7);
switch (mode)
{
case 0: // Dn
{
REG_D[reg] = data;
break;
}
case 2: // (An)
{
uint32 ea = REG_A[reg];
m68ki_write_32(ea, data);
break;
}
case 3: // (An)+
{
uint32 ea = EA_AY_PI_32();
m68ki_write_32(ea, data);
break;
}
case 4: // -(An)
{
uint32 ea = EA_AY_PD_32();
m68ki_write_32(ea, data);
break;
}
case 5: // (d16, An)
{
uint32 ea = EA_AY_DI_32();
m68ki_write_32(ea, data);
break;
}
case 6: // (An) + (Xn) + d8
{
uint32 ea = EA_AY_IX_32();
m68ki_write_32(ea, data);
break;
}
case 7:
{
switch (reg)
{
case 1: // (xxx).L
{
uint32 d1 = OPER_I_16();
uint32 d2 = OPER_I_16();
uint32 ea = (d1 << 16) | d2;
m68ki_write_32(ea, data);
break;
}
case 2: // (d16, PC)
{
uint32 ea = EA_PCDI_32();
m68ki_write_32(ea, data);
break;
}
default: fatalerror("MC68040: WRITE_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
}
break;
}
default: fatalerror("MC68040: WRITE_EA_32: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
}
}
static uint64 READ_EA_64(int ea)
{
int mode = (ea >> 3) & 0x7;
@ -370,6 +312,215 @@ static uint64 READ_EA_64(int ea)
return 0;
}
static void WRITE_EA_8(int ea, uint8 data)
{
int mode = (ea >> 3) & 0x7;
int reg = (ea & 0x7);
switch (mode)
{
case 0: // Dn
{
REG_D[reg] = data;
break;
}
case 2: // (An)
{
uint32 ea = REG_A[reg];
m68ki_write_8(ea, data);
break;
}
case 3: // (An)+
{
uint32 ea = EA_AY_PI_8();
m68ki_write_8(ea, data);
break;
}
case 4: // -(An)
{
uint32 ea = EA_AY_PD_8();
m68ki_write_8(ea, data);
break;
}
case 5: // (d16, An)
{
uint32 ea = EA_AY_DI_8();
m68ki_write_8(ea, data);
break;
}
case 6: // (An) + (Xn) + d8
{
uint32 ea = EA_AY_IX_8();
m68ki_write_8(ea, data);
break;
}
case 7:
{
switch (reg)
{
case 1: // (xxx).B
{
uint32 d1 = OPER_I_16();
uint32 d2 = OPER_I_16();
uint32 ea = (d1 << 16) | d2;
m68ki_write_8(ea, data);
break;
}
case 2: // (d16, PC)
{
uint32 ea = EA_PCDI_16();
m68ki_write_8(ea, data);
break;
}
default: fatalerror("MC68040: WRITE_EA_8: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
}
break;
}
default: fatalerror("MC68040: WRITE_EA_8: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
}
}
static void WRITE_EA_16(int ea, uint16 data)
{
int mode = (ea >> 3) & 0x7;
int reg = (ea & 0x7);
switch (mode)
{
case 0: // Dn
{
REG_D[reg] = data;
break;
}
case 2: // (An)
{
uint32 ea = REG_A[reg];
m68ki_write_16(ea, data);
break;
}
case 3: // (An)+
{
uint32 ea = EA_AY_PI_16();
m68ki_write_16(ea, data);
break;
}
case 4: // -(An)
{
uint32 ea = EA_AY_PD_16();
m68ki_write_16(ea, data);
break;
}
case 5: // (d16, An)
{
uint32 ea = EA_AY_DI_16();
m68ki_write_16(ea, data);
break;
}
case 6: // (An) + (Xn) + d8
{
uint32 ea = EA_AY_IX_16();
m68ki_write_16(ea, data);
break;
}
case 7:
{
switch (reg)
{
case 1: // (xxx).W
{
uint32 d1 = OPER_I_16();
uint32 d2 = OPER_I_16();
uint32 ea = (d1 << 16) | d2;
m68ki_write_16(ea, data);
break;
}
case 2: // (d16, PC)
{
uint32 ea = EA_PCDI_16();
m68ki_write_16(ea, data);
break;
}
default: fatalerror("MC68040: WRITE_EA_16: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
}
break;
}
default: fatalerror("MC68040: WRITE_EA_16: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
}
}
static void WRITE_EA_32(int ea, uint32 data)
{
int mode = (ea >> 3) & 0x7;
int reg = (ea & 0x7);
switch (mode)
{
case 0: // Dn
{
REG_D[reg] = data;
break;
}
case 1: // An
{
REG_A[reg] = data;
break;
}
case 2: // (An)
{
uint32 ea = REG_A[reg];
m68ki_write_32(ea, data);
break;
}
case 3: // (An)+
{
uint32 ea = EA_AY_PI_32();
m68ki_write_32(ea, data);
break;
}
case 4: // -(An)
{
uint32 ea = EA_AY_PD_32();
m68ki_write_32(ea, data);
break;
}
case 5: // (d16, An)
{
uint32 ea = EA_AY_DI_32();
m68ki_write_32(ea, data);
break;
}
case 6: // (An) + (Xn) + d8
{
uint32 ea = EA_AY_IX_32();
m68ki_write_32(ea, data);
break;
}
case 7:
{
switch (reg)
{
case 1: // (xxx).L
{
uint32 d1 = OPER_I_16();
uint32 d2 = OPER_I_16();
uint32 ea = (d1 << 16) | d2;
m68ki_write_32(ea, data);
break;
}
case 2: // (d16, PC)
{
uint32 ea = EA_PCDI_32();
m68ki_write_32(ea, data);
break;
}
default: fatalerror("MC68040: WRITE_EA_32: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC);
}
break;
}
default: fatalerror("MC68040: WRITE_EA_32: unhandled mode %d, reg %d, data %08X at %08X\n", mode, reg, data, REG_PC);
}
}
static void WRITE_EA_64(int ea, uint64 data)
{
int mode = (ea >> 3) & 0x7;
@ -381,7 +532,7 @@ static void WRITE_EA_64(int ea, uint64 data)
{
uint32 ea = REG_A[reg];
m68ki_write_32(ea, (uint32)(data >> 32));
m68ki_write_32(ea, (uint32)(data));
m68ki_write_32(ea+4, (uint32)(data));
break;
}
case 4: // -(An)
@ -627,7 +778,8 @@ static void fmove_reg_mem(uint16 w2)
}
case 4: // Word Integer
{
fatalerror("fmove_reg_mem: word integer store unimplemented at %08X\n", REG_PC-4);
sint16 d = (sint16)(REG_FP[src].f);
WRITE_EA_16(ea, d);
break;
}
case 5: // Double-precision Real
@ -638,7 +790,8 @@ static void fmove_reg_mem(uint16 w2)
}
case 6: // Byte Integer
{
fatalerror("fmove_reg_mem: byte integer store unimplemented at %08X\n", REG_PC-4);
sint8 d = (sint16)(REG_FP[src].f);
WRITE_EA_8(ea, d);
break;
}
case 7: // Packed-decimal Real with Dynamic K-factor

View file

@ -3,7 +3,7 @@
/* ======================================================================== */
/*
* MUSASHI
* Version 4.10
* Version 4.55
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright Karl Stenerud. All rights reserved.
@ -56,7 +56,7 @@
*/
static const char g_version[] = "4.10";
static const char g_version[] = "4.55";
/* ======================================================================== */
/* =============================== INCLUDES =============================== */
@ -762,7 +762,7 @@ void write_body(FILE* filep, body_struct* body, replace_struct* replace)
}
/* Found a directive with no matching replace string */
if(!found)
error_exit("Unknown " ID_BASE " directive");
error_exit("Unknown " ID_BASE " directive [%s]", output);
}
fprintf(filep, "%s\n", output);
}

321
m68kmmu.h Normal file
View file

@ -0,0 +1,321 @@
/*
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
*/
uint pmmu_translate_addr(uint addr_in)
{
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;
resolved = 0;
addr_out = addr_in;
// if SRP is enabled and we're in supervisor mode, use it
if ((m68ki_cpu.mmu_tc & 0x02000000) && (m68ki_get_sr() & 0x2000))
{
root_aptr = m68ki_cpu.mmu_srp_aptr;
root_limit = m68ki_cpu.mmu_srp_limit;
}
else // else use the CRP
{
root_aptr = m68ki_cpu.mmu_crp_aptr;
root_limit = m68ki_cpu.mmu_crp_limit;
}
// 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;
// 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);
// 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;
// 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;
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;
}
// 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;
// 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 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 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)
{
// get table C offset and pointer
tofs = (addr_in<<(is+abits+bbits))>>(32-cbits);
tptr = tbl_entry & 0xfffffff0;
switch (tbmode)
{
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);
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);
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);
break;
case 1: // termination descriptor
tbl_entry &= 0xffffff00;
shift = is+abits+bbits;
addr_out = ((addr_in<<shift)>>shift) + tbl_entry;
resolved = 1;
break;
}
}
if (!resolved)
{
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, REG_PC);
break;
case 1: // termination descriptor
tbl_entry &= 0xffffff00;
shift = is+abits+bbits+cbits;
addr_out = ((addr_in<<shift)>>shift) + tbl_entry;
resolved = 1;
break;
}
}
// fprintf(stderr,"PMMU: [%08x] => [%08x]\n", addr_in, addr_out);
return addr_out;
}
/*
m68881_mmu_ops: COP 0 MMU opcode handling
*/
void m68881_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");
return;
}
else if ((m68ki_cpu.ir & 0xffc0) == 0xf080)
{
fprintf(stderr,"680x0: unhandled PBcc\n");
return;
}
else // the rest are 1111000xxxXXXXXX where xxx is the instruction family
{
switch ((m68ki_cpu.ir>>9) & 0x7)
{
case 0:
modes = OPER_I_16();
if ((modes & 0xfde0) == 0x2000) // PLOAD
{
fprintf(stderr,"680x0: unhandled PLOAD\n");
return;
}
else if ((modes & 0xe200) == 0x2000) // PFLUSH
{
fprintf(stderr,"680x0: unhandled PFLUSH PC=%x\n", REG_PC);
return;
}
else if (modes == 0xa000) // PFLUSHR
{
fprintf(stderr,"680x0: unhandled PFLUSHR\n");
return;
}
else if (modes == 0x2800) // PVALID (FORMAT 1)
{
fprintf(stderr,"680x0: unhandled PVALID1\n");
return;
}
else if ((modes & 0xfff8) == 0x2c00) // PVALID (FORMAT 2)
{
fprintf(stderr,"680x0: unhandled PVALID2\n");
return;
}
else if ((modes & 0xe000) == 0x8000) // PTEST
{
fprintf(stderr,"680x0: unhandled PTEST\n");
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;
}
}
break;
default:
fprintf(stderr,"680x0: unknown PMMU instruction group %d\n", (m68ki_cpu.ir>>9) & 0x7);
break;
}
}
}

View file

@ -147,11 +147,23 @@ To enable separate immediate reads:
unsigned int m68k_read_immediate_16(unsigned int address);
unsigned int m68k_read_immediate_32(unsigned int address);
Now you also have the pcrelative stuff:
unsigned int m68k_read_pcrelative_8(unsigned int address);
unsigned int m68k_read_pcrelative_16(unsigned int address);
unsigned int m68k_read_pcrelative_32(unsigned int address);
- If you need to know the current PC (for banking and such), set
M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to
your routine.
- In the unlikely case where you need to emulate some PMMU in the immediate
reads and/or pcrealtive stuff, you'll need to explicitely call the
translation address mechanism from your user functions this way :
if (PMMU_ENABLED)
address = pmmu_translate_addr(address);
(this is handled automatically by normal memory accesses).
ADDRESS SPACES:
--------------