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