Commit | Line | Data |
---|---|---|
cff531af | 1 | /* |
2 | * PicoDrive | |
ae214f1c | 3 | * (C) notaz, 2007,2013 |
cff531af | 4 | * |
5 | * This work is licensed under the terms of MAME license. | |
6 | * See COPYING file in the top-level directory. | |
7 | */ | |
cc68a136 | 8 | |
efcba75f | 9 | #include "../pico_int.h" |
43e6eaad | 10 | #include "../sound/ym2612.h" |
cc68a136 | 11 | |
76276b0b | 12 | extern unsigned char formatted_bram[4*0x10]; |
ae214f1c | 13 | |
a6523294 | 14 | static unsigned int mcd_m68k_cycle_mult; |
15 | static unsigned int mcd_m68k_cycle_base; | |
16 | static unsigned int mcd_s68k_cycle_base; | |
89fa852d | 17 | |
721cd396 | 18 | void (*PicoMCDopenTray)(void) = NULL; |
d687ef50 | 19 | void (*PicoMCDcloseTray)(void) = NULL; |
89fa852d | 20 | |
cc68a136 | 21 | |
2aa27095 | 22 | PICO_INTERNAL void PicoInitMCD(void) |
cc68a136 | 23 | { |
24 | SekInitS68k(); | |
cc68a136 | 25 | } |
26 | ||
eff55556 | 27 | PICO_INTERNAL void PicoExitMCD(void) |
cc68a136 | 28 | { |
cc68a136 | 29 | } |
30 | ||
1cb1584b | 31 | PICO_INTERNAL void PicoPowerMCD(void) |
32 | { | |
ed06ffd3 | 33 | int fmt_size; |
8e4e84c2 | 34 | SekCycleCntS68k = SekCycleAimS68k = 0; |
35 | ||
ed06ffd3 | 36 | fmt_size = sizeof(formatted_bram); |
1cb1584b | 37 | memset(Pico_mcd->prg_ram, 0, sizeof(Pico_mcd->prg_ram)); |
38 | memset(Pico_mcd->word_ram2M, 0, sizeof(Pico_mcd->word_ram2M)); | |
39 | memset(Pico_mcd->pcm_ram, 0, sizeof(Pico_mcd->pcm_ram)); | |
40 | memset(Pico_mcd->bram, 0, sizeof(Pico_mcd->bram)); | |
4fb43555 | 41 | memcpy(Pico_mcd->bram + sizeof(Pico_mcd->bram) - fmt_size, |
42 | formatted_bram, fmt_size); | |
51a902ae | 43 | memset(Pico_mcd->s68k_regs, 0, sizeof(Pico_mcd->s68k_regs)); |
4f265db7 | 44 | memset(&Pico_mcd->pcm, 0, sizeof(Pico_mcd->pcm)); |
5c69a605 | 45 | memset(&Pico_mcd->m, 0, sizeof(Pico_mcd->m)); |
51a902ae | 46 | |
3f23709e | 47 | cdc_init(); |
274fcc35 | 48 | gfx_init(); |
d0132772 | 49 | |
4fb43555 | 50 | // cold reset state (tested) |
51 | Pico_mcd->m.state_flags = PCD_ST_S68K_RST; | |
52 | Pico_mcd->m.busreq = 2; // busreq on, s68k in reset | |
53 | Pico_mcd->s68k_regs[3] = 1; // 2M word RAM mode, m68k access | |
4fb43555 | 54 | memset(Pico_mcd->bios + 0x70, 0xff, 4); |
55 | } | |
cc68a136 | 56 | |
d0132772 | 57 | void pcd_soft_reset(void) |
4fb43555 | 58 | { |
7b3ddc11 | 59 | elprintf(EL_CD, "cd: soft reset"); |
d0132772 | 60 | |
3f23709e | 61 | Pico_mcd->m.s68k_pend_ints = 0; |
62 | cdc_reset(); | |
274fcc35 | 63 | cdd_reset(); |
3aa1e148 | 64 | #ifdef _ASM_CD_MEMORY_C |
00bd648e | 65 | //PicoMemResetCDdecode(1); // don't have to call this in 2M mode |
4ff2d527 | 66 | #endif |
cc68a136 | 67 | |
7b3ddc11 | 68 | memset(&Pico_mcd->s68k_regs[0x38], 0, 9); |
69 | Pico_mcd->s68k_regs[0x38+9] = 0x0f; // default checksum | |
70 | ||
d0132772 | 71 | pcd_event_schedule_s68k(PCD_EVENT_CDC, 12500000/75); |
72 | ||
73 | // TODO: test if register state/timers change | |
74 | } | |
75 | ||
76 | PICO_INTERNAL int PicoResetMCD(void) | |
77 | { | |
78 | // reset button doesn't affect MCD hardware | |
79 | ||
6cadc2da | 80 | // use SRam.data for RAM cart |
af37bca8 | 81 | if (PicoOpt & POPT_EN_MCD_RAMCART) { |
d6114368 | 82 | if (SRam.data == NULL) |
83 | SRam.data = calloc(1, 0x12000); | |
84 | } | |
85 | else if (SRam.data != NULL) { | |
86 | free(SRam.data); | |
87 | SRam.data = NULL; | |
88 | } | |
b542be46 | 89 | SRam.start = SRam.end = 0; // unused |
6cadc2da | 90 | |
cc68a136 | 91 | return 0; |
92 | } | |
93 | ||
6901d0e4 | 94 | static void SekRunM68kOnce(void) |
95 | { | |
96 | int cyc_do; | |
97 | pevt_log_m68k_o(EVT_RUN_START); | |
98 | ||
99 | if ((cyc_do = SekCycleAim - SekCycleCnt) > 0) { | |
100 | SekCycleCnt += cyc_do; | |
101 | ||
102 | #if defined(EMU_C68K) | |
103 | PicoCpuCM68k.cycles = cyc_do; | |
104 | CycloneRun(&PicoCpuCM68k); | |
105 | SekCycleCnt -= PicoCpuCM68k.cycles; | |
106 | #elif defined(EMU_M68K) | |
107 | SekCycleCnt += m68k_execute(cyc_do) - cyc_do; | |
108 | #elif defined(EMU_F68K) | |
109 | SekCycleCnt += fm68k_emulate(cyc_do, 0) - cyc_do; | |
110 | #endif | |
111 | } | |
112 | ||
113 | SekCyclesLeft = 0; | |
114 | ||
115 | SekTrace(0); | |
116 | pevt_log_m68k_o(EVT_RUN_END); | |
117 | } | |
118 | ||
119 | static void SekRunS68k(unsigned int to) | |
cc68a136 | 120 | { |
121 | int cyc_do; | |
ae214f1c | 122 | |
123 | SekCycleAimS68k = to; | |
124 | if ((cyc_do = SekCycleAimS68k - SekCycleCntS68k) <= 0) | |
125 | return; | |
126 | ||
30e8aac4 | 127 | if (SekShouldInterrupt()) |
128 | Pico_mcd->m.s68k_poll_a = 0; | |
129 | ||
ae214f1c | 130 | SekCycleCntS68k += cyc_do; |
131 | #if defined(EMU_C68K) | |
132 | PicoCpuCS68k.cycles = cyc_do; | |
3aa1e148 | 133 | CycloneRun(&PicoCpuCS68k); |
ae214f1c | 134 | SekCycleCntS68k -= PicoCpuCS68k.cycles; |
b837b69b | 135 | #elif defined(EMU_M68K) |
3aa1e148 | 136 | m68k_set_context(&PicoCpuMS68k); |
ae214f1c | 137 | SekCycleCntS68k += m68k_execute(cyc_do) - cyc_do; |
ed4402a7 | 138 | m68k_set_context(&PicoCpuMM68k); |
3aa1e148 | 139 | #elif defined(EMU_F68K) |
ae214f1c | 140 | g_m68kcontext = &PicoCpuFS68k; |
99ade2ee | 141 | SekCycleCntS68k += fm68k_emulate(cyc_do, 0) - cyc_do; |
ae214f1c | 142 | g_m68kcontext = &PicoCpuFM68k; |
cc68a136 | 143 | #endif |
144 | } | |
145 | ||
8e4e84c2 | 146 | static void pcd_set_cycle_mult(void) |
147 | { | |
148 | // ~1.63 for NTSC, ~1.645 for PAL | |
149 | if (Pico.m.pal) | |
a6523294 | 150 | mcd_m68k_cycle_mult = ((12500000ull << 16) / (50*312*488)); |
8e4e84c2 | 151 | else |
a6523294 | 152 | mcd_m68k_cycle_mult = ((12500000ull << 16) / (60*262*488)) + 1; |
8e4e84c2 | 153 | } |
68cba51e | 154 | |
ae214f1c | 155 | unsigned int pcd_cycles_m68k_to_s68k(unsigned int c) |
8022f53d | 156 | { |
a6523294 | 157 | return (long long)c * mcd_m68k_cycle_mult >> 16; |
8022f53d | 158 | } |
ae214f1c | 159 | |
160 | /* events */ | |
161 | static void pcd_cdc_event(unsigned int now) | |
68cba51e | 162 | { |
ae214f1c | 163 | // 75Hz CDC update |
274fcc35 | 164 | cdd_update(); |
165 | ||
166 | /* check if a new CDD command has been processed */ | |
167 | if (!(Pico_mcd->s68k_regs[0x4b] & 0xf0)) | |
168 | { | |
169 | /* reset CDD command wait flag */ | |
170 | Pico_mcd->s68k_regs[0x4b] = 0xf0; | |
171 | ||
172 | if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) { | |
173 | elprintf(EL_INTS|EL_CD, "s68k: cdd irq 4"); | |
174 | SekInterruptS68k(4); | |
175 | } | |
176 | } | |
177 | ||
ae214f1c | 178 | pcd_event_schedule(now, PCD_EVENT_CDC, 12500000/75); |
179 | } | |
7336a99a | 180 | |
ae214f1c | 181 | static void pcd_int3_timer_event(unsigned int now) |
182 | { | |
183 | if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN3) { | |
184 | elprintf(EL_INTS|EL_CD, "s68k: timer irq 3"); | |
185 | SekInterruptS68k(3); | |
186 | } | |
7336a99a | 187 | |
ae214f1c | 188 | if (Pico_mcd->s68k_regs[0x31] != 0) |
189 | pcd_event_schedule(now, PCD_EVENT_TIMER3, | |
190 | Pico_mcd->s68k_regs[0x31] * 384); | |
191 | } | |
192 | ||
ae214f1c | 193 | static void pcd_dma_event(unsigned int now) |
194 | { | |
3f23709e | 195 | cdc_dma_update(); |
ae214f1c | 196 | } |
68cba51e | 197 | |
ae214f1c | 198 | typedef void (event_cb)(unsigned int now); |
199 | ||
200 | /* times are in s68k (12.5MHz) cycles */ | |
201 | unsigned int pcd_event_times[PCD_EVENT_COUNT]; | |
202 | static unsigned int event_time_next; | |
203 | static event_cb *pcd_event_cbs[PCD_EVENT_COUNT] = { | |
ed06ffd3 T |
204 | pcd_cdc_event, |
205 | pcd_int3_timer_event, | |
206 | gfx_update, | |
207 | pcd_dma_event, | |
ae214f1c | 208 | }; |
209 | ||
210 | void pcd_event_schedule(unsigned int now, enum pcd_event event, int after) | |
bf098bc5 | 211 | { |
ae214f1c | 212 | unsigned int when; |
213 | ||
214 | when = now + after; | |
215 | if (when == 0) { | |
216 | // event cancelled | |
217 | pcd_event_times[event] = 0; | |
218 | return; | |
219 | } | |
bf098bc5 | 220 | |
ae214f1c | 221 | when |= 1; |
bf098bc5 | 222 | |
ae214f1c | 223 | elprintf(EL_CD, "cd: new event #%u %u->%u", event, now, when); |
224 | pcd_event_times[event] = when; | |
bf098bc5 | 225 | |
ae214f1c | 226 | if (event_time_next == 0 || CYCLES_GT(event_time_next, when)) |
227 | event_time_next = when; | |
bf098bc5 | 228 | } |
229 | ||
ae214f1c | 230 | void pcd_event_schedule_s68k(enum pcd_event event, int after) |
4f265db7 | 231 | { |
ae214f1c | 232 | if (SekCyclesLeftS68k > after) |
233 | SekEndRunS68k(after); | |
4f265db7 | 234 | |
ae214f1c | 235 | pcd_event_schedule(SekCyclesDoneS68k(), event, after); |
236 | } | |
4f265db7 | 237 | |
ae214f1c | 238 | static void pcd_run_events(unsigned int until) |
239 | { | |
240 | int oldest, oldest_diff, time; | |
241 | int i, diff; | |
242 | ||
243 | while (1) { | |
244 | oldest = -1, oldest_diff = 0x7fffffff; | |
245 | ||
246 | for (i = 0; i < PCD_EVENT_COUNT; i++) { | |
247 | if (pcd_event_times[i]) { | |
248 | diff = pcd_event_times[i] - until; | |
249 | if (diff < oldest_diff) { | |
250 | oldest_diff = diff; | |
251 | oldest = i; | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | if (oldest_diff <= 0) { | |
257 | time = pcd_event_times[oldest]; | |
258 | pcd_event_times[oldest] = 0; | |
259 | elprintf(EL_CD, "cd: run event #%d %u", oldest, time); | |
260 | pcd_event_cbs[oldest](time); | |
261 | } | |
262 | else if (oldest_diff < 0x7fffffff) { | |
263 | event_time_next = pcd_event_times[oldest]; | |
264 | break; | |
265 | } | |
266 | else { | |
267 | event_time_next = 0; | |
268 | break; | |
269 | } | |
270 | } | |
4f265db7 | 271 | |
ae214f1c | 272 | if (oldest != -1) |
273 | elprintf(EL_CD, "cd: next event #%d at %u", | |
274 | oldest, event_time_next); | |
4f265db7 | 275 | } |
276 | ||
08769494 | 277 | int pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync) |
ae214f1c | 278 | { |
279 | #define now SekCycleCntS68k | |
a6523294 | 280 | unsigned int s68k_target; |
ae214f1c | 281 | unsigned int target; |
b837b69b | 282 | |
a6523294 | 283 | target = m68k_target - mcd_m68k_cycle_base; |
284 | s68k_target = mcd_s68k_cycle_base + | |
285 | ((unsigned long long)target * mcd_m68k_cycle_mult >> 16); | |
286 | ||
08769494 | 287 | elprintf(EL_CD, "s68k sync to %u, %u->%u", |
288 | m68k_target, now, s68k_target); | |
ae214f1c | 289 | |
4fb43555 | 290 | if (Pico_mcd->m.busreq != 1) { /* busreq/reset */ |
ae214f1c | 291 | SekCycleCntS68k = SekCycleAimS68k = s68k_target; |
292 | pcd_run_events(m68k_target); | |
08769494 | 293 | return 0; |
ae214f1c | 294 | } |
295 | ||
296 | while (CYCLES_GT(s68k_target, now)) { | |
297 | if (event_time_next && CYCLES_GE(now, event_time_next)) | |
298 | pcd_run_events(now); | |
299 | ||
300 | target = s68k_target; | |
301 | if (event_time_next && CYCLES_GT(target, event_time_next)) | |
302 | target = event_time_next; | |
303 | ||
304 | SekRunS68k(target); | |
08769494 | 305 | if (m68k_poll_sync && Pico_mcd->m.m68k_poll_cnt == 0) |
306 | break; | |
ae214f1c | 307 | } |
08769494 | 308 | |
309 | return s68k_target - now; | |
ae214f1c | 310 | #undef now |
c987bb5c | 311 | } |
ae214f1c | 312 | |
ba6e8bfd | 313 | #define pcd_run_cpus_normal pcd_run_cpus |
314 | //#define pcd_run_cpus_lockstep pcd_run_cpus | |
315 | ||
08769494 | 316 | static void SekSyncM68k(void); |
317 | ||
fa8fb754 | 318 | void pcd_run_cpus_normal(int m68k_cycles) |
08769494 | 319 | { |
320 | SekCycleAim += m68k_cycles; | |
30e8aac4 | 321 | if (SekShouldInterrupt() || Pico_mcd->m.m68k_poll_cnt < 12) |
cc5ffc3c | 322 | Pico_mcd->m.m68k_poll_cnt = 0; |
323 | else if (Pico_mcd->m.m68k_poll_cnt >= 16) { | |
08769494 | 324 | int s68k_left = pcd_sync_s68k(SekCycleAim, 1); |
325 | if (s68k_left <= 0) { | |
ba6e8bfd | 326 | elprintf(EL_CDPOLL, "m68k poll [%02x] x%d @%06x", |
08769494 | 327 | Pico_mcd->m.m68k_poll_a, Pico_mcd->m.m68k_poll_cnt, SekPc); |
328 | SekCycleCnt = SekCycleAim; | |
329 | return; | |
330 | } | |
331 | SekCycleCnt = SekCycleAim - (s68k_left * 40220 >> 16); | |
332 | } | |
333 | ||
6901d0e4 | 334 | while (CYCLES_GT(SekCycleAim, SekCycleCnt)) { |
335 | SekRunM68kOnce(); | |
336 | if (Pico_mcd->m.need_sync) { | |
337 | Pico_mcd->m.need_sync = 0; | |
338 | pcd_sync_s68k(SekCycleCnt, 0); | |
339 | } | |
340 | } | |
08769494 | 341 | } |
342 | ||
fa8fb754 | 343 | void pcd_run_cpus_lockstep(int m68k_cycles) |
ba6e8bfd | 344 | { |
345 | unsigned int target = SekCycleAim + m68k_cycles; | |
346 | do { | |
347 | SekCycleAim += 8; | |
348 | SekSyncM68k(); | |
349 | pcd_sync_s68k(SekCycleAim, 0); | |
350 | } while (CYCLES_GT(target, SekCycleAim)); | |
cc5ffc3c | 351 | |
352 | SekCycleAim = target; | |
ba6e8bfd | 353 | } |
354 | ||
ae214f1c | 355 | #define PICO_CD |
356 | #define CPUS_RUN(m68k_cycles) \ | |
08769494 | 357 | pcd_run_cpus(m68k_cycles) |
ae214f1c | 358 | |
efcba75f | 359 | #include "../pico_cmn.c" |
cc68a136 | 360 | |
361 | ||
a6523294 | 362 | void pcd_prepare_frame(void) |
363 | { | |
364 | pcd_set_cycle_mult(); | |
365 | ||
366 | // need this because we can't have direct mapping between | |
367 | // master<->slave cycle counters because of overflows | |
368 | mcd_m68k_cycle_base = SekCycleAim; | |
369 | mcd_s68k_cycle_base = SekCycleAimS68k; | |
370 | } | |
371 | ||
2aa27095 | 372 | PICO_INTERNAL void PicoFrameMCD(void) |
cc68a136 | 373 | { |
a6523294 | 374 | PicoFrameStart(); |
cc68a136 | 375 | |
a6523294 | 376 | pcd_prepare_frame(); |
bf5fbbb4 | 377 | PicoFrameHints(); |
cc68a136 | 378 | } |
379 | ||
ae214f1c | 380 | void pcd_state_loaded(void) |
381 | { | |
382 | unsigned int cycles; | |
383 | int diff; | |
384 | ||
8e4e84c2 | 385 | pcd_set_cycle_mult(); |
ae214f1c | 386 | pcd_state_loaded_mem(); |
387 | ||
33be04ca | 388 | memset(Pico_mcd->pcm_mixbuf, 0, sizeof(Pico_mcd->pcm_mixbuf)); |
389 | Pico_mcd->pcm_mixbuf_dirty = 0; | |
390 | Pico_mcd->pcm_mixpos = 0; | |
021e47b3 | 391 | Pico_mcd->pcm_regs_dirty = 1; |
33be04ca | 392 | |
ae214f1c | 393 | // old savestates.. |
394 | cycles = pcd_cycles_m68k_to_s68k(SekCycleAim); | |
395 | diff = cycles - SekCycleAimS68k; | |
396 | if (diff < -1000 || diff > 1000) { | |
397 | SekCycleCntS68k = SekCycleAimS68k = cycles; | |
398 | } | |
399 | if (pcd_event_times[PCD_EVENT_CDC] == 0) { | |
400 | pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_CDC, 12500000/75); | |
401 | ||
402 | if (Pico_mcd->s68k_regs[0x31]) | |
403 | pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_TIMER3, | |
404 | Pico_mcd->s68k_regs[0x31] * 384); | |
ae214f1c | 405 | } |
334d9fb6 | 406 | |
407 | diff = cycles - Pico_mcd->pcm.update_cycles; | |
408 | if ((unsigned int)diff > 12500000/50) | |
33be04ca | 409 | Pico_mcd->pcm.update_cycles = cycles; |
8e4e84c2 | 410 | |
411 | // reschedule | |
412 | event_time_next = 0; | |
413 | pcd_run_events(SekCycleCntS68k); | |
ae214f1c | 414 | } |
cc68a136 | 415 | |
ae214f1c | 416 | // vim:shiftwidth=2:ts=2:expandtab |