bugfixes related to mmap usage for ROM
[picodrive.git] / pico / sek.c
... / ...
CommitLineData
1// This is part of Pico Library\r
2\r
3// (c) Copyright 2004 Dave, All rights reserved.\r
4// (c) Copyright 2006 notaz, All rights reserved.\r
5// Free for non-commercial use.\r
6\r
7// For commercial use, separate licencing terms must be obtained.\r
8\r
9\r
10#include "pico_int.h"\r
11#include "memory.h"\r
12\r
13\r
14int SekCycleCnt=0; // cycles done in this frame\r
15int SekCycleAim=0; // cycle aim\r
16unsigned int SekCycleCntT=0;\r
17\r
18\r
19/* context */\r
20// Cyclone 68000\r
21#ifdef EMU_C68K\r
22struct Cyclone PicoCpuCM68k;\r
23#endif\r
24// MUSASHI 68000\r
25#ifdef EMU_M68K\r
26m68ki_cpu_core PicoCpuMM68k;\r
27#endif\r
28// FAME 68000\r
29#ifdef EMU_F68K\r
30M68K_CONTEXT PicoCpuFM68k;\r
31#endif\r
32\r
33\r
34/* callbacks */\r
35#ifdef EMU_C68K\r
36// interrupt acknowledgment\r
37static int SekIntAck(int level)\r
38{\r
39 // try to emulate VDP's reaction to 68000 int ack\r
40 if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
41 else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
42 PicoCpuCM68k.irq = 0;\r
43 return CYCLONE_INT_ACK_AUTOVECTOR;\r
44}\r
45\r
46static void SekResetAck(void)\r
47{\r
48 elprintf(EL_ANOMALY, "Reset encountered @ %06x", SekPc);\r
49}\r
50\r
51static int SekUnrecognizedOpcode()\r
52{\r
53 unsigned int pc, op;\r
54 pc = SekPc;\r
55 op = PicoCpuCM68k.read16(pc);\r
56 elprintf(EL_ANOMALY, "Unrecognized Opcode %04x @ %06x", op, pc);\r
57 // see if we are not executing trash\r
58 if (pc < 0x200 || (pc > Pico.romsize+4 && (pc&0xe00000)!=0xe00000)) {\r
59 PicoCpuCM68k.cycles = 0;\r
60 PicoCpuCM68k.state_flags |= 1;\r
61 return 1;\r
62 }\r
63#ifdef EMU_M68K // debugging cyclone\r
64 {\r
65 extern int have_illegal;\r
66 have_illegal = 1;\r
67 }\r
68#endif\r
69 return 0;\r
70}\r
71#endif\r
72\r
73\r
74#ifdef EMU_M68K\r
75static int SekIntAckM68K(int level)\r
76{\r
77 if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
78 else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
79 CPU_INT_LEVEL = 0;\r
80 return M68K_INT_ACK_AUTOVECTOR;\r
81}\r
82\r
83static int SekTasCallback(void)\r
84{\r
85 return 0; // no writeback\r
86}\r
87#endif\r
88\r
89\r
90#ifdef EMU_F68K\r
91static void SekIntAckF68K(unsigned level)\r
92{\r
93 if (level == 4) { Pico.video.pending_ints = 0; elprintf(EL_INTS, "hack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
94 else if(level == 6) { Pico.video.pending_ints &= ~0x20; elprintf(EL_INTS, "vack: @ %06x [%i]", SekPc, SekCycleCnt); }\r
95 PicoCpuFM68k.interrupts[0] = 0;\r
96}\r
97#endif\r
98\r
99\r
100PICO_INTERNAL void SekInit(void)\r
101{\r
102#ifdef EMU_C68K\r
103 CycloneInit();\r
104 memset(&PicoCpuCM68k,0,sizeof(PicoCpuCM68k));\r
105 PicoCpuCM68k.IrqCallback=SekIntAck;\r
106 PicoCpuCM68k.ResetCallback=SekResetAck;\r
107 PicoCpuCM68k.UnrecognizedCallback=SekUnrecognizedOpcode;\r
108 PicoCpuCM68k.flags=4; // Z set\r
109#endif\r
110#ifdef EMU_M68K\r
111 {\r
112 void *oldcontext = m68ki_cpu_p;\r
113 m68k_set_context(&PicoCpuMM68k);\r
114 m68k_set_cpu_type(M68K_CPU_TYPE_68000);\r
115 m68k_init();\r
116 m68k_set_int_ack_callback(SekIntAckM68K);\r
117 m68k_set_tas_instr_callback(SekTasCallback);\r
118 //m68k_pulse_reset();\r
119 m68k_set_context(oldcontext);\r
120 }\r
121#endif\r
122#ifdef EMU_F68K\r
123 {\r
124 void *oldcontext = g_m68kcontext;\r
125 g_m68kcontext = &PicoCpuFM68k;\r
126 memset(&PicoCpuFM68k, 0, sizeof(PicoCpuFM68k));\r
127 fm68k_init();\r
128 PicoCpuFM68k.iack_handler = SekIntAckF68K;\r
129 PicoCpuFM68k.sr = 0x2704; // Z flag\r
130 g_m68kcontext = oldcontext;\r
131 }\r
132#endif\r
133}\r
134\r
135\r
136// Reset the 68000:\r
137PICO_INTERNAL int SekReset(void)\r
138{\r
139 if (Pico.rom==NULL) return 1;\r
140\r
141#ifdef EMU_C68K\r
142 CycloneReset(&PicoCpuCM68k);\r
143#endif\r
144#ifdef EMU_M68K\r
145 m68k_set_context(&PicoCpuMM68k); // if we ever reset m68k, we always need it's context to be set\r
146 m68ki_cpu.sp[0]=0;\r
147 m68k_set_irq(0);\r
148 m68k_pulse_reset();\r
149 REG_USP = 0; // ?\r
150#endif\r
151#ifdef EMU_F68K\r
152 {\r
153 g_m68kcontext = &PicoCpuFM68k;\r
154 fm68k_reset();\r
155 }\r
156#endif\r
157\r
158 return 0;\r
159}\r
160\r
161void SekStepM68k(void)\r
162{\r
163 SekCycleAim=SekCycleCnt+1;\r
164#if defined(EMU_CORE_DEBUG)\r
165 SekCycleCnt+=CM_compareRun(1, 0);\r
166#elif defined(EMU_C68K)\r
167 PicoCpuCM68k.cycles=1;\r
168 CycloneRun(&PicoCpuCM68k);\r
169 SekCycleCnt+=1-PicoCpuCM68k.cycles;\r
170#elif defined(EMU_M68K)\r
171 SekCycleCnt+=m68k_execute(1);\r
172#elif defined(EMU_F68K)\r
173 SekCycleCnt+=fm68k_emulate(1, 0, 0);\r
174#endif\r
175}\r
176\r
177PICO_INTERNAL void SekSetRealTAS(int use_real)\r
178{\r
179#ifdef EMU_C68K\r
180 CycloneSetRealTAS(use_real);\r
181#endif\r
182#ifdef EMU_F68K\r
183 // TODO\r
184#endif\r
185}\r
186\r
187\r
188/* idle loop detection, not to be used in CD mode */\r
189#ifdef EMU_C68K\r
190#include "cpu/Cyclone/tools/idle.h"\r
191#endif\r
192\r
193static unsigned short **idledet_ptrs = NULL;\r
194static int idledet_count = 0, idledet_bads = 0;\r
195int idledet_start_frame = 0;\r
196\r
197#if 0\r
198#define IDLE_STATS 1\r
199unsigned int idlehit_addrs[128], idlehit_counts[128];\r
200\r
201void SekRegisterIdleHit(unsigned int pc)\r
202{\r
203 int i;\r
204 for (i = 0; i < 127 && idlehit_addrs[i]; i++) {\r
205 if (idlehit_addrs[i] == pc) {\r
206 idlehit_counts[i]++;\r
207 return;\r
208 }\r
209 }\r
210 idlehit_addrs[i] = pc;\r
211 idlehit_counts[i] = 1;\r
212 idlehit_addrs[i+1] = 0;\r
213}\r
214#endif\r
215\r
216void SekInitIdleDet(void)\r
217{\r
218 unsigned short **tmp = realloc(idledet_ptrs, 0x200*4);\r
219 if (tmp == NULL) {\r
220 free(idledet_ptrs);\r
221 idledet_ptrs = NULL;\r
222 }\r
223 else\r
224 idledet_ptrs = tmp;\r
225 idledet_count = idledet_bads = 0;\r
226 idledet_start_frame = Pico.m.frame_count + 360;\r
227#ifdef IDLE_STATS\r
228 idlehit_addrs[0] = 0;\r
229#endif\r
230\r
231#ifdef EMU_C68K\r
232 CycloneInitIdle();\r
233#endif\r
234#ifdef EMU_F68K\r
235 fm68k_emulate(0, 0, 1);\r
236#endif\r
237}\r
238\r
239int SekIsIdleCode(unsigned short *dst, int bytes)\r
240{\r
241 // printf("SekIsIdleCode %04x %i\n", *dst, bytes);\r
242 switch (bytes)\r
243 {\r
244 case 2:\r
245 if ((*dst & 0xf000) != 0x6000) // not another branch\r
246 return 1;\r
247 break;\r
248 case 4:\r
249 if ( (*dst & 0xfff8) == 0x4a10 || // tst.b ($aX) // there should be no need to wait\r
250 (*dst & 0xfff8) == 0x4a28 || // tst.b ($xxxx,a0) // for byte change anywhere\r
251 (*dst & 0xff3f) == 0x4a38 || // tst.x ($xxxx.w); tas ($xxxx.w)\r
252 (*dst & 0xc1ff) == 0x0038 || // move.x ($xxxx.w), dX\r
253 (*dst & 0xf13f) == 0xb038) // cmp.x ($xxxx.w), dX\r
254 return 1;\r
255 break;\r
256 case 6:\r
257 if ( ((dst[1] & 0xe0) == 0xe0 && ( // RAM and\r
258 *dst == 0x4a39 || // tst.b ($xxxxxxxx)\r
259 *dst == 0x4a79 || // tst.w ($xxxxxxxx)\r
260 *dst == 0x4ab9 || // tst.l ($xxxxxxxx)\r
261 (*dst & 0xc1ff) == 0x0039 || // move.x ($xxxxxxxx), dX\r
262 (*dst & 0xf13f) == 0xb039))||// cmp.x ($xxxxxxxx), dX\r
263 *dst == 0x0838 || // btst $X, ($xxxx.w) [6 byte op]\r
264 (*dst & 0xffbf) == 0x0c38) // cmpi.{b,w} $X, ($xxxx.w)\r
265 return 1;\r
266 break;\r
267 case 8:\r
268 if ( ((dst[2] & 0xe0) == 0xe0 && ( // RAM and\r
269 *dst == 0x0839 || // btst $X, ($xxxxxxxx.w) [8 byte op]\r
270 (*dst & 0xffbf) == 0x0c39))||// cmpi.{b,w} $X, ($xxxxxxxx)\r
271 *dst == 0x0cb8) // cmpi.l $X, ($xxxx.w)\r
272 return 1;\r
273 break;\r
274 case 12:\r
275 if ((*dst & 0xf1f8) == 0x3010 && // move.w (aX), dX\r
276 (dst[1]&0xf100) == 0x0000 && // arithmetic\r
277 (dst[3]&0xf100) == 0x0000) // arithmetic\r
278 return 1;\r
279 break;\r
280 }\r
281\r
282 return 0;\r
283}\r
284\r
285int SekRegisterIdlePatch(unsigned int pc, int oldop, int newop, void *ctx)\r
286{\r
287 int is_main68k = 1;\r
288 u16 *target;\r
289 uptr v;\r
290\r
291#if defined(EMU_C68K)\r
292 struct Cyclone *cyc = ctx;\r
293 is_main68k = cyc == &PicoCpuCM68k;\r
294 pc -= cyc->membase;\r
295#elif defined(EMU_F68K)\r
296 is_main68k = ctx == &PicoCpuFM68k;\r
297#endif\r
298 pc &= ~0xff000000;\r
299 elprintf(EL_IDLE, "idle: patch %06x %04x %04x %c %c #%i", pc, oldop, newop,\r
300 (newop&0x200)?'n':'y', is_main68k?'m':'s', idledet_count);\r
301\r
302 // XXX: probably shouldn't patch RAM too\r
303 v = m68k_read16_map[pc >> M68K_MEM_SHIFT];\r
304 if (!(v & 0x80000000))\r
305 target = (u16 *)((v << 1) + pc);\r
306 else {\r
307 if (++idledet_bads > 128)\r
308 return 2; // remove detector\r
309 return 1; // don't patch\r
310 }\r
311\r
312 if (idledet_count >= 0x200 && (idledet_count & 0x1ff) == 0) {\r
313 unsigned short **tmp = realloc(idledet_ptrs, (idledet_count+0x200)*4);\r
314 if (tmp == NULL)\r
315 return 1;\r
316 idledet_ptrs = tmp;\r
317 }\r
318\r
319 idledet_ptrs[idledet_count++] = target;\r
320\r
321 return 0;\r
322}\r
323\r
324void SekFinishIdleDet(void)\r
325{\r
326#ifdef EMU_C68K\r
327 CycloneFinishIdle();\r
328#endif\r
329#ifdef EMU_F68K\r
330 fm68k_emulate(0, 0, 2);\r
331#endif\r
332 while (idledet_count > 0)\r
333 {\r
334 unsigned short *op = idledet_ptrs[--idledet_count];\r
335 if ((*op & 0xfd00) == 0x7100)\r
336 *op &= 0xff, *op |= 0x6600;\r
337 else if ((*op & 0xfd00) == 0x7500)\r
338 *op &= 0xff, *op |= 0x6700;\r
339 else if ((*op & 0xfd00) == 0x7d00)\r
340 *op &= 0xff, *op |= 0x6000;\r
341 else\r
342 elprintf(EL_STATUS|EL_IDLE, "idle: don't know how to restore %04x", *op);\r
343 }\r
344}\r
345\r
346\r
347#if defined(EMU_M68K) && M68K_INSTRUCTION_HOOK == OPT_SPECIFY_HANDLER\r
348static unsigned char op_flags[0x400000/2] = { 0, };\r
349static int atexit_set = 0;\r
350\r
351static void make_idc(void)\r
352{\r
353 FILE *f = fopen("idc.idc", "w");\r
354 int i;\r
355 if (!f) return;\r
356 fprintf(f, "#include <idc.idc>\nstatic main() {\n");\r
357 for (i = 0; i < 0x400000/2; i++)\r
358 if (op_flags[i] != 0)\r
359 fprintf(f, " MakeCode(0x%06x);\n", i*2);\r
360 fprintf(f, "}\n");\r
361 fclose(f);\r
362}\r
363\r
364void instruction_hook(void)\r
365{\r
366 if (!atexit_set) {\r
367 atexit(make_idc);\r
368 atexit_set = 1;\r
369 }\r
370 if (REG_PC < 0x400000)\r
371 op_flags[REG_PC/2] = 1;\r
372}\r
373#endif\r