cd sync improvements, part2
[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 m68k_cycle_mult;
15
16 void (*PicoMCDopenTray)(void) = NULL;
17 void (*PicoMCDcloseTray)(void) = NULL;
18
19
20 PICO_INTERNAL void PicoInitMCD(void)
21 {
22   SekInitS68k();
23   Init_CD_Driver();
24 }
25
26 PICO_INTERNAL void PicoExitMCD(void)
27 {
28   End_CD_Driver();
29 }
30
31 PICO_INTERNAL void PicoPowerMCD(void)
32 {
33   int fmt_size = sizeof(formatted_bram);
34   memset(Pico_mcd->prg_ram,    0, sizeof(Pico_mcd->prg_ram));
35   memset(Pico_mcd->word_ram2M, 0, sizeof(Pico_mcd->word_ram2M));
36   memset(Pico_mcd->pcm_ram,    0, sizeof(Pico_mcd->pcm_ram));
37   memset(Pico_mcd->bram, 0, sizeof(Pico_mcd->bram));
38   memcpy(Pico_mcd->bram + sizeof(Pico_mcd->bram) - fmt_size, formatted_bram, fmt_size);
39 }
40
41 PICO_INTERNAL int PicoResetMCD(void)
42 {
43   memset(Pico_mcd->s68k_regs, 0, sizeof(Pico_mcd->s68k_regs));
44   memset(&Pico_mcd->pcm, 0, sizeof(Pico_mcd->pcm));
45   memset(&Pico_mcd->m, 0, sizeof(Pico_mcd->m));
46
47   memset(Pico_mcd->bios + 0x70, 0xff, 4); // reset hint vector (simplest way to implement reg6)
48   Pico_mcd->m.state_flags = 0;
49   Pico_mcd->s68k_regs[3] = 1; // 2M word RAM mode with m68k access after reset
50
51   Reset_CD();
52   LC89510_Reset();
53   gfx_cd_reset();
54 #ifdef _ASM_CD_MEMORY_C
55   //PicoMemResetCDdecode(1); // don't have to call this in 2M mode
56 #endif
57
58   // use SRam.data for RAM cart
59   if (PicoOpt & POPT_EN_MCD_RAMCART) {
60     if (SRam.data == NULL)
61       SRam.data = calloc(1, 0x12000);
62   }
63   else if (SRam.data != NULL) {
64     free(SRam.data);
65     SRam.data = NULL;
66   }
67   SRam.start = SRam.end = 0; // unused
68
69   pcd_event_schedule(0, PCD_EVENT_CDC, 12500000/75);
70
71   return 0;
72 }
73
74 static __inline void SekRunS68k(unsigned int to)
75 {
76   int cyc_do;
77
78   SekCycleAimS68k = to;
79   if ((cyc_do = SekCycleAimS68k - SekCycleCntS68k) <= 0)
80     return;
81
82   SekCycleCntS68k += cyc_do;
83 #if defined(EMU_C68K)
84   PicoCpuCS68k.cycles = cyc_do;
85   CycloneRun(&PicoCpuCS68k);
86   SekCycleCntS68k -= PicoCpuCS68k.cycles;
87 #elif defined(EMU_M68K)
88   m68k_set_context(&PicoCpuMS68k);
89   SekCycleCntS68k += m68k_execute(cyc_do) - cyc_do;
90   m68k_set_context(&PicoCpuMM68k);
91 #elif defined(EMU_F68K)
92   g_m68kcontext = &PicoCpuFS68k;
93   SekCycleCntS68k += fm68k_emulate(cyc_do, 0, 0) - cyc_do;
94   g_m68kcontext = &PicoCpuFM68k;
95 #endif
96 }
97
98
99 unsigned int pcd_cycles_m68k_to_s68k(unsigned int c)
100 {
101   return (long long)c * m68k_cycle_mult >> 16;
102 }
103
104 /* events */
105 static void pcd_cdc_event(unsigned int now)
106 {
107   // 75Hz CDC update
108   Check_CD_Command();
109   pcd_event_schedule(now, PCD_EVENT_CDC, 12500000/75);
110 }
111
112 static void pcd_int3_timer_event(unsigned int now)
113 {
114   if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN3) {
115     elprintf(EL_INTS|EL_CD, "s68k: timer irq 3");
116     SekInterruptS68k(3);
117   }
118
119   if (Pico_mcd->s68k_regs[0x31] != 0)
120     pcd_event_schedule(now, PCD_EVENT_TIMER3,
121       Pico_mcd->s68k_regs[0x31] * 384);
122 }
123
124 static void pcd_gfx_event(unsigned int now)
125 {
126   // update gfx chip
127   if (Pico_mcd->rot_comp.Reg_58 & 0x8000) {
128     Pico_mcd->rot_comp.Reg_58 &= 0x7fff;
129     Pico_mcd->rot_comp.Reg_64  = 0;
130     if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
131       elprintf(EL_INTS  |EL_CD, "s68k: gfx_cd irq 1");
132       SekInterruptS68k(1);
133     }
134   }
135 }
136
137 static void pcd_dma_event(unsigned int now)
138 {
139   int ddx = Pico_mcd->s68k_regs[4] & 7;
140         Update_CDC_TRansfer(ddx);
141 }
142
143 typedef void (event_cb)(unsigned int now);
144
145 /* times are in s68k (12.5MHz) cycles */
146 unsigned int pcd_event_times[PCD_EVENT_COUNT];
147 static unsigned int event_time_next;
148 static event_cb *pcd_event_cbs[PCD_EVENT_COUNT] = {
149   [PCD_EVENT_CDC]      = pcd_cdc_event,
150   [PCD_EVENT_TIMER3]   = pcd_int3_timer_event,
151   [PCD_EVENT_GFX]      = pcd_gfx_event,
152   [PCD_EVENT_DMA]      = pcd_dma_event,
153 };
154
155 void pcd_event_schedule(unsigned int now, enum pcd_event event, int after)
156 {
157   unsigned int when;
158
159   when = now + after;
160   if (when == 0) {
161     // event cancelled
162     pcd_event_times[event] = 0;
163     return;
164   }
165
166   when |= 1;
167
168   elprintf(EL_CD, "cd: new event #%u %u->%u", event, now, when);
169   pcd_event_times[event] = when;
170
171   if (event_time_next == 0 || CYCLES_GT(event_time_next, when))
172     event_time_next = when;
173 }
174
175 void pcd_event_schedule_s68k(enum pcd_event event, int after)
176 {
177   if (SekCyclesLeftS68k > after)
178     SekEndRunS68k(after);
179
180   pcd_event_schedule(SekCyclesDoneS68k(), event, after);
181 }
182
183 static void pcd_run_events(unsigned int until)
184 {
185   int oldest, oldest_diff, time;
186   int i, diff;
187
188   while (1) {
189     oldest = -1, oldest_diff = 0x7fffffff;
190
191     for (i = 0; i < PCD_EVENT_COUNT; i++) {
192       if (pcd_event_times[i]) {
193         diff = pcd_event_times[i] - until;
194         if (diff < oldest_diff) {
195           oldest_diff = diff;
196           oldest = i;
197         }
198       }
199     }
200
201     if (oldest_diff <= 0) {
202       time = pcd_event_times[oldest];
203       pcd_event_times[oldest] = 0;
204       elprintf(EL_CD, "cd: run event #%d %u", oldest, time);
205       pcd_event_cbs[oldest](time);
206     }
207     else if (oldest_diff < 0x7fffffff) {
208       event_time_next = pcd_event_times[oldest];
209       break;
210     }
211     else {
212       event_time_next = 0;
213       break;
214     }
215   }
216
217   if (oldest != -1)
218     elprintf(EL_CD, "cd: next event #%d at %u",
219       oldest, event_time_next);
220 }
221
222 int pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync)
223 {
224   #define now SekCycleCntS68k
225   unsigned int s68k_target =
226     (unsigned long long)m68k_target * m68k_cycle_mult >> 16;
227   unsigned int target;
228
229   elprintf(EL_CD, "s68k sync to %u, %u->%u",
230     m68k_target, now, s68k_target);
231
232   if ((Pico_mcd->m.busreq & 3) != 1) { /* busreq/reset */
233     SekCycleCntS68k = SekCycleAimS68k = s68k_target;
234     pcd_run_events(m68k_target);
235     return 0;
236   }
237
238   while (CYCLES_GT(s68k_target, now)) {
239     if (event_time_next && CYCLES_GE(now, event_time_next))
240       pcd_run_events(now);
241
242     target = s68k_target;
243     if (event_time_next && CYCLES_GT(target, event_time_next))
244       target = event_time_next;
245
246     SekRunS68k(target);
247     if (m68k_poll_sync && Pico_mcd->m.m68k_poll_cnt == 0)
248       break;
249   }
250
251   return s68k_target - now;
252   #undef now
253 }
254
255 static void SekSyncM68k(void);
256
257 static void pcd_run_cpus(int m68k_cycles)
258 {
259   SekCycleAim += m68k_cycles;
260   if (Pico_mcd->m.m68k_poll_cnt >= 16 && !SekShouldInterrupt()) {
261     int s68k_left = pcd_sync_s68k(SekCycleAim, 1);
262     if (s68k_left <= 0) {
263       elprintf(EL_CDPOLL, "m68k poll [%02x] %d @%06x",
264         Pico_mcd->m.m68k_poll_a, Pico_mcd->m.m68k_poll_cnt, SekPc);
265       SekCycleCnt = SekCycleAim;
266       return;
267     }
268     SekCycleCnt = SekCycleAim - (s68k_left * 40220 >> 16);
269   }
270
271   SekSyncM68k();
272 }
273
274 #define PICO_CD
275 #define CPUS_RUN(m68k_cycles) \
276   pcd_run_cpus(m68k_cycles)
277
278 #include "../pico_cmn.c"
279
280
281 PICO_INTERNAL void PicoFrameMCD(void)
282 {
283   if (!(PicoOpt&POPT_ALT_RENDERER))
284     PicoFrameStart();
285
286   // ~1.63 for NTSC, ~1.645 for PAL
287   if (Pico.m.pal)
288     m68k_cycle_mult = ((12500000ull << 16) / (50*312*488));
289   else
290     m68k_cycle_mult = ((12500000ull << 16) / (60*262*488)) + 1;
291
292   PicoFrameHints();
293 }
294
295 void pcd_state_loaded(void)
296 {
297   unsigned int cycles;
298   int diff;
299
300   pcd_state_loaded_mem();
301
302   // old savestates..
303   cycles = pcd_cycles_m68k_to_s68k(SekCycleAim);
304   diff = cycles - SekCycleAimS68k;
305   if (diff < -1000 || diff > 1000) {
306     SekCycleCntS68k = SekCycleAimS68k = cycles;
307   }
308   if (pcd_event_times[PCD_EVENT_CDC] == 0) {
309     pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_CDC, 12500000/75);
310
311     if (Pico_mcd->s68k_regs[0x31])
312       pcd_event_schedule(SekCycleAimS68k, PCD_EVENT_TIMER3,
313         Pico_mcd->s68k_regs[0x31] * 384);
314
315     if (Pico_mcd->rot_comp.Reg_58 & 0x8000) {
316       Pico_mcd->rot_comp.Reg_58 &= 0x7fff;
317       Pico_mcd->rot_comp.Reg_64  = 0;
318       if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1)
319         SekInterruptS68k(1);
320     }
321     if (Pico_mcd->scd.Status_CDC & 0x08)
322             Update_CDC_TRansfer(Pico_mcd->s68k_regs[4] & 7);
323   }
324 }
325
326 // vim:shiftwidth=2:ts=2:expandtab