mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-04 14:57:45 -04:00

- PicoDrive was originally released by fDave with simple "free for non-commercial use / For commercial use, separate licencing terms must be obtained" license and I kept it in my releases. - in 2011, fDave re-released his code (same that I used as base many years ago) dual licensed with GPLv2 and MAME licenses: https://code.google.com/p/cyclone68000/ Based on the above I now proclaim that the whole source code is licensed under the MAME license as more elaborate form of "for non-commercial use". If that raises any doubt, I announce that all my modifications (which is the vast majority of code by now) is licensed under the MAME license, as it reads in COPYING file in this commit. This does not affect ym2612.c/sn76496.c that were MAME licensed already from the beginning.
447 lines
12 KiB
C
447 lines
12 KiB
C
/*
|
|
* PicoDrive
|
|
* (c) Copyright Dave, 2004
|
|
* (C) notaz, 2006-2009
|
|
*
|
|
* This work is licensed under the terms of MAME license.
|
|
* See COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "pico_int.h"
|
|
#include "memory.h"
|
|
|
|
|
|
int SekCycleCnt=0; // cycles done in this frame
|
|
int SekCycleAim=0; // cycle aim
|
|
unsigned int SekCycleCntT=0;
|
|
|
|
|
|
/* context */
|
|
// Cyclone 68000
|
|
#ifdef EMU_C68K
|
|
struct Cyclone PicoCpuCM68k;
|
|
#endif
|
|
// MUSASHI 68000
|
|
#ifdef EMU_M68K
|
|
m68ki_cpu_core PicoCpuMM68k;
|
|
#endif
|
|
// FAME 68000
|
|
#ifdef EMU_F68K
|
|
M68K_CONTEXT PicoCpuFM68k;
|
|
#endif
|
|
|
|
|
|
/* callbacks */
|
|
#ifdef EMU_C68K
|
|
// interrupt acknowledgment
|
|
static int SekIntAck(int level)
|
|
{
|
|
// try to emulate VDP's reaction to 68000 int ack
|
|
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
PicoCpuCM68k.irq = 0;
|
|
return CYCLONE_INT_ACK_AUTOVECTOR;
|
|
}
|
|
|
|
static void SekResetAck(void)
|
|
{
|
|
elprintf(EL_ANOMALY, "Reset encountered @ %06x", SekPc);
|
|
}
|
|
|
|
static int SekUnrecognizedOpcode()
|
|
{
|
|
unsigned int pc;
|
|
pc = SekPc;
|
|
elprintf(EL_ANOMALY, "Unrecognized Opcode @ %06x", pc);
|
|
// see if we are still in a mapped region
|
|
pc &= 0x00ffffff;
|
|
if (map_flag_set(m68k_read16_map[pc >> M68K_MEM_SHIFT])) {
|
|
elprintf(EL_STATUS|EL_ANOMALY, "m68k crash @%06x", pc);
|
|
PicoCpuCM68k.cycles = 0;
|
|
PicoCpuCM68k.state_flags |= 1;
|
|
return 1;
|
|
}
|
|
#ifdef EMU_M68K // debugging cyclone
|
|
{
|
|
extern int have_illegal;
|
|
have_illegal = 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef EMU_M68K
|
|
static int SekIntAckM68K(int level)
|
|
{
|
|
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
CPU_INT_LEVEL = 0;
|
|
return M68K_INT_ACK_AUTOVECTOR;
|
|
}
|
|
|
|
static int SekTasCallback(void)
|
|
{
|
|
return 0; // no writeback
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef EMU_F68K
|
|
static void SekIntAckF68K(unsigned level)
|
|
{
|
|
if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }
|
|
PicoCpuFM68k.interrupts[0] = 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
PICO_INTERNAL void SekInit(void)
|
|
{
|
|
#ifdef EMU_C68K
|
|
CycloneInit();
|
|
memset(&PicoCpuCM68k,0,sizeof(PicoCpuCM68k));
|
|
PicoCpuCM68k.IrqCallback=SekIntAck;
|
|
PicoCpuCM68k.ResetCallback=SekResetAck;
|
|
PicoCpuCM68k.UnrecognizedCallback=SekUnrecognizedOpcode;
|
|
PicoCpuCM68k.flags=4; // Z set
|
|
#endif
|
|
#ifdef EMU_M68K
|
|
{
|
|
void *oldcontext = m68ki_cpu_p;
|
|
m68k_set_context(&PicoCpuMM68k);
|
|
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
|
|
m68k_init();
|
|
m68k_set_int_ack_callback(SekIntAckM68K);
|
|
m68k_set_tas_instr_callback(SekTasCallback);
|
|
//m68k_pulse_reset();
|
|
m68k_set_context(oldcontext);
|
|
}
|
|
#endif
|
|
#ifdef EMU_F68K
|
|
{
|
|
void *oldcontext = g_m68kcontext;
|
|
g_m68kcontext = &PicoCpuFM68k;
|
|
memset(&PicoCpuFM68k, 0, sizeof(PicoCpuFM68k));
|
|
fm68k_init();
|
|
PicoCpuFM68k.iack_handler = SekIntAckF68K;
|
|
PicoCpuFM68k.sr = 0x2704; // Z flag
|
|
g_m68kcontext = oldcontext;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// Reset the 68000:
|
|
PICO_INTERNAL int SekReset(void)
|
|
{
|
|
if (Pico.rom==NULL) return 1;
|
|
|
|
#ifdef EMU_C68K
|
|
CycloneReset(&PicoCpuCM68k);
|
|
#endif
|
|
#ifdef EMU_M68K
|
|
m68k_set_context(&PicoCpuMM68k); // if we ever reset m68k, we always need it's context to be set
|
|
m68ki_cpu.sp[0]=0;
|
|
m68k_set_irq(0);
|
|
m68k_pulse_reset();
|
|
REG_USP = 0; // ?
|
|
#endif
|
|
#ifdef EMU_F68K
|
|
{
|
|
g_m68kcontext = &PicoCpuFM68k;
|
|
fm68k_reset();
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void SekStepM68k(void)
|
|
{
|
|
SekCycleAim=SekCycleCnt+1;
|
|
#if defined(EMU_CORE_DEBUG)
|
|
SekCycleCnt+=CM_compareRun(1, 0);
|
|
#elif defined(EMU_C68K)
|
|
PicoCpuCM68k.cycles=1;
|
|
CycloneRun(&PicoCpuCM68k);
|
|
SekCycleCnt+=1-PicoCpuCM68k.cycles;
|
|
#elif defined(EMU_M68K)
|
|
SekCycleCnt+=m68k_execute(1);
|
|
#elif defined(EMU_F68K)
|
|
SekCycleCnt+=fm68k_emulate(1, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
PICO_INTERNAL void SekSetRealTAS(int use_real)
|
|
{
|
|
#ifdef EMU_C68K
|
|
CycloneSetRealTAS(use_real);
|
|
#endif
|
|
#ifdef EMU_F68K
|
|
// TODO
|
|
#endif
|
|
}
|
|
|
|
// Pack the cpu into a common format:
|
|
// XXX: rename
|
|
PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
|
|
{
|
|
unsigned int pc=0;
|
|
|
|
#if defined(EMU_C68K)
|
|
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
|
|
memcpy(cpu,context->d,0x40);
|
|
pc=context->pc-context->membase;
|
|
*(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
|
|
*(unsigned int *)(cpu+0x48)=context->osp;
|
|
cpu[0x4c] = context->irq;
|
|
cpu[0x4d] = context->state_flags & 1;
|
|
#elif defined(EMU_M68K)
|
|
void *oldcontext = m68ki_cpu_p;
|
|
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
|
|
memcpy(cpu,m68ki_cpu_p->dar,0x40);
|
|
pc=m68ki_cpu_p->pc;
|
|
*(unsigned int *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
|
|
*(unsigned int *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
|
|
cpu[0x4c] = CPU_INT_LEVEL>>8;
|
|
cpu[0x4d] = CPU_STOPPED;
|
|
m68k_set_context(oldcontext);
|
|
#elif defined(EMU_F68K)
|
|
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
|
|
memcpy(cpu,context->dreg,0x40);
|
|
pc=context->pc;
|
|
*(unsigned int *)(cpu+0x44)=context->sr;
|
|
*(unsigned int *)(cpu+0x48)=context->asp;
|
|
cpu[0x4c] = context->interrupts[0];
|
|
cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
|
|
#endif
|
|
|
|
*(unsigned int *)(cpu+0x40)=pc;
|
|
}
|
|
|
|
PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub)
|
|
{
|
|
#if defined(EMU_C68K)
|
|
struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
|
|
CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
|
|
context->osp=*(unsigned int *)(cpu+0x48);
|
|
memcpy(context->d,cpu,0x40);
|
|
context->membase = 0;
|
|
context->pc = *(unsigned int *)(cpu+0x40);
|
|
CycloneUnpack(context, NULL); // rebase PC
|
|
context->irq = cpu[0x4c];
|
|
context->state_flags = 0;
|
|
if (cpu[0x4d])
|
|
context->state_flags |= 1;
|
|
#elif defined(EMU_M68K)
|
|
void *oldcontext = m68ki_cpu_p;
|
|
m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
|
|
m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
|
|
memcpy(m68ki_cpu_p->dar,cpu,0x40);
|
|
m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
|
|
m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
|
|
CPU_INT_LEVEL = cpu[0x4c] << 8;
|
|
CPU_STOPPED = cpu[0x4d];
|
|
m68k_set_context(oldcontext);
|
|
#elif defined(EMU_F68K)
|
|
M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
|
|
memcpy(context->dreg,cpu,0x40);
|
|
context->pc =*(unsigned int *)(cpu+0x40);
|
|
context->sr =*(unsigned int *)(cpu+0x44);
|
|
context->asp=*(unsigned int *)(cpu+0x48);
|
|
context->interrupts[0] = cpu[0x4c];
|
|
context->execinfo &= ~FM68K_HALTED;
|
|
if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* idle loop detection, not to be used in CD mode */
|
|
#ifdef EMU_C68K
|
|
#include "cpu/Cyclone/tools/idle.h"
|
|
#endif
|
|
|
|
static unsigned short **idledet_ptrs = NULL;
|
|
static int idledet_count = 0, idledet_bads = 0;
|
|
int idledet_start_frame = 0;
|
|
|
|
#if 0
|
|
#define IDLE_STATS 1
|
|
unsigned int idlehit_addrs[128], idlehit_counts[128];
|
|
|
|
void SekRegisterIdleHit(unsigned int pc)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 127 && idlehit_addrs[i]; i++) {
|
|
if (idlehit_addrs[i] == pc) {
|
|
idlehit_counts[i]++;
|
|
return;
|
|
}
|
|
}
|
|
idlehit_addrs[i] = pc;
|
|
idlehit_counts[i] = 1;
|
|
idlehit_addrs[i+1] = 0;
|
|
}
|
|
#endif
|
|
|
|
void SekInitIdleDet(void)
|
|
{
|
|
unsigned short **tmp = realloc(idledet_ptrs, 0x200*4);
|
|
if (tmp == NULL) {
|
|
free(idledet_ptrs);
|
|
idledet_ptrs = NULL;
|
|
}
|
|
else
|
|
idledet_ptrs = tmp;
|
|
idledet_count = idledet_bads = 0;
|
|
idledet_start_frame = Pico.m.frame_count + 360;
|
|
#ifdef IDLE_STATS
|
|
idlehit_addrs[0] = 0;
|
|
#endif
|
|
|
|
#ifdef EMU_C68K
|
|
CycloneInitIdle();
|
|
#endif
|
|
#ifdef EMU_F68K
|
|
fm68k_emulate(0, 0, 1);
|
|
#endif
|
|
}
|
|
|
|
int SekIsIdleCode(unsigned short *dst, int bytes)
|
|
{
|
|
// printf("SekIsIdleCode %04x %i\n", *dst, bytes);
|
|
switch (bytes)
|
|
{
|
|
case 2:
|
|
if ((*dst & 0xf000) != 0x6000) // not another branch
|
|
return 1;
|
|
break;
|
|
case 4:
|
|
if ( (*dst & 0xfff8) == 0x4a10 || // tst.b ($aX) // there should be no need to wait
|
|
(*dst & 0xfff8) == 0x4a28 || // tst.b ($xxxx,a0) // for byte change anywhere
|
|
(*dst & 0xff3f) == 0x4a38 || // tst.x ($xxxx.w); tas ($xxxx.w)
|
|
(*dst & 0xc1ff) == 0x0038 || // move.x ($xxxx.w), dX
|
|
(*dst & 0xf13f) == 0xb038) // cmp.x ($xxxx.w), dX
|
|
return 1;
|
|
break;
|
|
case 6:
|
|
if ( ((dst[1] & 0xe0) == 0xe0 && ( // RAM and
|
|
*dst == 0x4a39 || // tst.b ($xxxxxxxx)
|
|
*dst == 0x4a79 || // tst.w ($xxxxxxxx)
|
|
*dst == 0x4ab9 || // tst.l ($xxxxxxxx)
|
|
(*dst & 0xc1ff) == 0x0039 || // move.x ($xxxxxxxx), dX
|
|
(*dst & 0xf13f) == 0xb039))||// cmp.x ($xxxxxxxx), dX
|
|
*dst == 0x0838 || // btst $X, ($xxxx.w) [6 byte op]
|
|
(*dst & 0xffbf) == 0x0c38) // cmpi.{b,w} $X, ($xxxx.w)
|
|
return 1;
|
|
break;
|
|
case 8:
|
|
if ( ((dst[2] & 0xe0) == 0xe0 && ( // RAM and
|
|
*dst == 0x0839 || // btst $X, ($xxxxxxxx.w) [8 byte op]
|
|
(*dst & 0xffbf) == 0x0c39))||// cmpi.{b,w} $X, ($xxxxxxxx)
|
|
*dst == 0x0cb8) // cmpi.l $X, ($xxxx.w)
|
|
return 1;
|
|
break;
|
|
case 12:
|
|
if ((*dst & 0xf1f8) == 0x3010 && // move.w (aX), dX
|
|
(dst[1]&0xf100) == 0x0000 && // arithmetic
|
|
(dst[3]&0xf100) == 0x0000) // arithmetic
|
|
return 1;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SekRegisterIdlePatch(unsigned int pc, int oldop, int newop, void *ctx)
|
|
{
|
|
int is_main68k = 1;
|
|
u16 *target;
|
|
uptr v;
|
|
|
|
#if defined(EMU_C68K)
|
|
struct Cyclone *cyc = ctx;
|
|
is_main68k = cyc == &PicoCpuCM68k;
|
|
pc -= cyc->membase;
|
|
#elif defined(EMU_F68K)
|
|
is_main68k = ctx == &PicoCpuFM68k;
|
|
#endif
|
|
pc &= ~0xff000000;
|
|
elprintf(EL_IDLE, "idle: patch %06x %04x %04x %c %c #%i", pc, oldop, newop,
|
|
(newop&0x200)?'n':'y', is_main68k?'m':'s', idledet_count);
|
|
|
|
// XXX: probably shouldn't patch RAM too
|
|
v = m68k_read16_map[pc >> M68K_MEM_SHIFT];
|
|
if (!(v & 0x80000000))
|
|
target = (u16 *)((v << 1) + pc);
|
|
else {
|
|
if (++idledet_bads > 128)
|
|
return 2; // remove detector
|
|
return 1; // don't patch
|
|
}
|
|
|
|
if (idledet_count >= 0x200 && (idledet_count & 0x1ff) == 0) {
|
|
unsigned short **tmp = realloc(idledet_ptrs, (idledet_count+0x200)*4);
|
|
if (tmp == NULL)
|
|
return 1;
|
|
idledet_ptrs = tmp;
|
|
}
|
|
|
|
idledet_ptrs[idledet_count++] = target;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void SekFinishIdleDet(void)
|
|
{
|
|
#ifdef EMU_C68K
|
|
CycloneFinishIdle();
|
|
#endif
|
|
#ifdef EMU_F68K
|
|
fm68k_emulate(0, 0, 2);
|
|
#endif
|
|
while (idledet_count > 0)
|
|
{
|
|
unsigned short *op = idledet_ptrs[--idledet_count];
|
|
if ((*op & 0xfd00) == 0x7100)
|
|
*op &= 0xff, *op |= 0x6600;
|
|
else if ((*op & 0xfd00) == 0x7500)
|
|
*op &= 0xff, *op |= 0x6700;
|
|
else if ((*op & 0xfd00) == 0x7d00)
|
|
*op &= 0xff, *op |= 0x6000;
|
|
else
|
|
elprintf(EL_STATUS|EL_IDLE, "idle: don't know how to restore %04x", *op);
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(EMU_M68K) && M68K_INSTRUCTION_HOOK == OPT_SPECIFY_HANDLER
|
|
static unsigned char op_flags[0x400000/2] = { 0, };
|
|
static int atexit_set = 0;
|
|
|
|
static void make_idc(void)
|
|
{
|
|
FILE *f = fopen("idc.idc", "w");
|
|
int i;
|
|
if (!f) return;
|
|
fprintf(f, "#include <idc.idc>\nstatic main() {\n");
|
|
for (i = 0; i < 0x400000/2; i++)
|
|
if (op_flags[i] != 0)
|
|
fprintf(f, " MakeCode(0x%06x);\n", i*2);
|
|
fprintf(f, "}\n");
|
|
fclose(f);
|
|
}
|
|
|
|
void instruction_hook(void)
|
|
{
|
|
if (!atexit_set) {
|
|
atexit(make_idc);
|
|
atexit_set = 1;
|
|
}
|
|
if (REG_PC < 0x400000)
|
|
op_flags[REG_PC/2] = 1;
|
|
}
|
|
#endif
|