fix a corner case with h-int
[picodrive.git] / pico / pico_cmn.c
1 /*
2  * common code for base/cd/32x
3  * (C) notaz, 2007-2009,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 #define CYCLES_M68K_LINE     488 // suitable for both PAL/NTSC
10 #define CYCLES_M68K_VINT_LAG 112
11
12 // pad delay (for 6 button pads)
13 #define PAD_DELAY() { \
14   if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; \
15   if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; \
16 }
17
18 // CPUS_RUN
19 #ifndef CPUS_RUN
20 #define CPUS_RUN(m68k_cycles) \
21   SekRunM68k(m68k_cycles)
22 #endif
23
24 // sync m68k to Pico.t.m68c_aim
25 static void SekSyncM68k(void)
26 {
27   int cyc_do;
28   pprof_start(m68k);
29   pevt_log_m68k_o(EVT_RUN_START);
30
31   while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0) {
32     Pico.t.m68c_cnt += cyc_do;
33
34 #if defined(EMU_C68K)
35     PicoCpuCM68k.cycles = cyc_do;
36     CycloneRun(&PicoCpuCM68k);
37     Pico.t.m68c_cnt -= PicoCpuCM68k.cycles;
38 #elif defined(EMU_M68K)
39     Pico.t.m68c_cnt += m68k_execute(cyc_do) - cyc_do;
40 #elif defined(EMU_F68K)
41     Pico.t.m68c_cnt += fm68k_emulate(&PicoCpuFM68k, cyc_do, 0) - cyc_do;
42 #endif
43   }
44
45   SekCyclesLeft = 0;
46
47   SekTrace(0);
48   pevt_log_m68k_o(EVT_RUN_END);
49   pprof_end(m68k);
50 }
51
52 static __inline void SekRunM68k(int cyc)
53 {
54   Pico.t.m68c_aim += cyc;
55   cyc = Pico.t.m68c_aim - Pico.t.m68c_cnt;
56   if (cyc <= 0)
57     return;
58   Pico.t.m68c_cnt += cyc >> 6; // refresh slowdowns
59   SekSyncM68k();
60 }
61
62 static void do_hint(struct PicoVideo *pv)
63 {
64   pv->pending_ints |= 0x10;
65   if (pv->reg[0] & 0x10) {
66     elprintf(EL_INTS, "hint: @ %06x [%u]", SekPc, SekCyclesDone());
67     SekInterrupt(4);
68   }
69 }
70
71 static void do_timing_hacks_as(struct PicoVideo *pv, int vdp_slots)
72 {
73   pv->lwrite_cnt += vdp_slots - Pico.m.dma_xfers * 2; // wrong *2
74   if (pv->lwrite_cnt > vdp_slots)
75     pv->lwrite_cnt = vdp_slots;
76   else if (pv->lwrite_cnt < 0)
77     pv->lwrite_cnt = 0;
78   if (Pico.m.dma_xfers)
79     SekCyclesBurn(CheckDMA());
80 }
81
82 static void do_timing_hacks_vb(void)
83 {
84   if (unlikely(Pico.m.dma_xfers))
85     SekCyclesBurn(CheckDMA());
86 }
87
88 static int PicoFrameHints(void)
89 {
90   struct PicoVideo *pv = &Pico.video;
91   int line_sample = Pico.m.pal ? 68 : 93;
92   int vdp_slots = (Pico.video.reg[12] & 1) ? 18 : 16;
93   int lines, y, lines_vis, skip;
94   int vcnt_wrap, vcnt_adj;
95   unsigned int cycles;
96   int hint; // Hint counter
97
98   pevt_log_m68k_o(EVT_FRAME_START);
99
100   if ((PicoIn.opt&POPT_ALT_RENDERER) && !PicoIn.skipFrame && (pv->reg[1]&0x40)) { // fast rend., display enabled
101     // draw a frame just after vblank in alternative render mode
102     // yes, this will cause 1 frame lag, but this is inaccurate mode anyway.
103     PicoFrameFull();
104 #ifdef DRAW_FINISH_FUNC
105     DRAW_FINISH_FUNC();
106 #endif
107     skip = 1;
108   }
109   else skip=PicoIn.skipFrame;
110
111   Pico.t.m68c_frame_start = Pico.t.m68c_aim;
112   pv->v_counter = Pico.m.scanline = 0;
113   z80_resetCycles();
114   PsndStartFrame();
115
116   hint = pv->hint_cnt;
117   pv->status |= PVS_ACTIVE;
118
119   for (y = 0; ; y++)
120   {
121     pv->v_counter = Pico.m.scanline = y;
122     if ((pv->reg[12]&6) == 6) { // interlace mode 2
123       pv->v_counter <<= 1;
124       pv->v_counter |= pv->v_counter >> 8;
125       pv->v_counter &= 0xff;
126     }
127
128     if ((y == 224 && !(pv->reg[1] & 8)) || y == 240)
129       break;
130
131     PAD_DELAY();
132
133     // H-Interrupts:
134     if (--hint < 0)
135     {
136       hint = pv->reg[10]; // Reload H-Int counter
137       do_hint(pv);
138     }
139
140     // decide if we draw this line
141     if (!skip && (PicoIn.opt & POPT_ALT_RENDERER))
142     {
143       // find the right moment for frame renderer, when display is no longer blanked
144       if ((pv->reg[1]&0x40) || y > 100) {
145         PicoFrameFull();
146 #ifdef DRAW_FINISH_FUNC
147         DRAW_FINISH_FUNC();
148 #endif
149         skip = 1;
150       }
151     }
152
153     // get samples from sound chips
154     if ((y == 224 || y == line_sample) && PicoIn.sndOut)
155     {
156       cycles = SekCyclesDone();
157
158       if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80))
159         PicoSyncZ80(cycles);
160 #ifdef PICO_CD
161       if (PicoIn.AHW & PAHW_MCD)
162         pcd_sync_s68k(cycles, 0);
163 #endif
164 #ifdef PICO_32X
165       p32x_sync_sh2s(cycles);
166 #endif
167       PsndGetSamples(y);
168     }
169
170     // Run scanline:
171     Pico.t.m68c_line_start = Pico.t.m68c_aim;
172     do_timing_hacks_as(pv, vdp_slots);
173     CPUS_RUN(CYCLES_M68K_LINE);
174
175     if (PicoLineHook) PicoLineHook();
176     pevt_log_m68k_o(EVT_NEXT_LINE);
177   }
178
179   lines_vis = (pv->reg[1] & 8) ? 240 : 224;
180   if (y == lines_vis)
181     pv->status &= ~PVS_ACTIVE;
182
183   if (!skip)
184   {
185     if (Pico.est.DrawScanline < y)
186       PicoDrawSync(y - 1, 0);
187 #ifdef DRAW_FINISH_FUNC
188     DRAW_FINISH_FUNC();
189 #endif
190   }
191
192   // VDP FIFO
193   pv->lwrite_cnt = 0;
194   Pico.video.status |= SR_EMPT;
195
196   memcpy(PicoIn.padInt, PicoIn.pad, sizeof(PicoIn.padInt));
197   PAD_DELAY();
198
199   // Last H-Int (normally):
200   if (--hint < 0)
201   {
202     hint = pv->reg[10]; // Reload H-Int counter
203     do_hint(pv);
204   }
205
206   pv->status |= SR_VB | PVS_VB2; // go into vblank
207
208   // the following SekRun is there for several reasons:
209   // there must be a delay after vblank bit is set and irq is asserted (Mazin Saga)
210   // also delay between F bit (bit 7) is set in SR and IRQ happens (Ex-Mutants)
211   // also delay between last H-int and V-int (Golden Axe 3)
212   Pico.t.m68c_line_start = Pico.t.m68c_aim;
213   do_timing_hacks_vb();
214   CPUS_RUN(CYCLES_M68K_VINT_LAG);
215
216   pv->status |= SR_F;
217   pv->pending_ints |= 0x20;
218   if (pv->reg[1] & 0x20) {
219     Pico.t.m68c_aim = Pico.t.m68c_cnt + 11; // HACK
220     SekSyncM68k();
221     elprintf(EL_INTS, "vint: @ %06x [%u]", SekPc, SekCyclesDone());
222     SekInterrupt(6);
223   }
224
225   cycles = SekCyclesDone();
226   if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80)) {
227     PicoSyncZ80(cycles);
228     elprintf(EL_INTS, "zint");
229     z80_int();
230   }
231
232 #ifdef PICO_CD
233   if (PicoIn.AHW & PAHW_MCD)
234     pcd_sync_s68k(cycles, 0);
235 #endif
236 #ifdef PICO_32X
237   p32x_sync_sh2s(cycles);
238   p32x_start_blank();
239 #endif
240
241   // get samples from sound chips
242   if (y == 224 && PicoIn.sndOut)
243     PsndGetSamples(y);
244
245   // Run scanline:
246   CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG);
247
248   if (PicoLineHook) PicoLineHook();
249   pevt_log_m68k_o(EVT_NEXT_LINE);
250
251   if (Pico.m.pal) {
252     lines = 313;
253     vcnt_wrap = 0x103;
254     vcnt_adj = 57;
255   }
256   else {
257     lines = 262;
258     vcnt_wrap = 0xEB;
259     vcnt_adj = 6;
260   }
261
262   for (y++; y < lines - 1; y++)
263   {
264     pv->v_counter = Pico.m.scanline = y;
265     if (y >= vcnt_wrap)
266       pv->v_counter -= vcnt_adj;
267     if ((pv->reg[12]&6) == 6)
268       pv->v_counter = (pv->v_counter << 1) | 1;
269     pv->v_counter &= 0xff;
270
271     PAD_DELAY();
272
273     if (unlikely(pv->status & PVS_ACTIVE) && --hint < 0)
274     {
275       hint = pv->reg[10]; // Reload H-Int counter
276       do_hint(pv);
277     }
278
279     // Run scanline:
280     Pico.t.m68c_line_start = Pico.t.m68c_aim;
281     do_timing_hacks_vb();
282     CPUS_RUN(CYCLES_M68K_LINE);
283
284     if (PicoLineHook) PicoLineHook();
285     pevt_log_m68k_o(EVT_NEXT_LINE);
286   }
287
288   if (unlikely(PicoIn.overclockM68k)) {
289     unsigned int l = PicoIn.overclockM68k * lines / 100;
290     while (l-- > 0) {
291       Pico.t.m68c_cnt -= CYCLES_M68K_LINE;
292       do_timing_hacks_vb();
293       SekSyncM68k();
294     }
295   }
296
297   pv->status &= ~(SR_VB | PVS_VB2);
298   pv->status |= ((pv->reg[1] >> 3) ^ SR_VB) & SR_VB; // forced blanking
299
300   // last scanline
301   Pico.m.scanline = y;
302   pv->v_counter = 0xff;
303   pv->lwrite_cnt = 0;
304
305   PAD_DELAY();
306
307   if (unlikely(pv->status & PVS_ACTIVE)) {
308     if (--hint < 0) {
309       hint = pv->reg[10]; // Reload H-Int counter
310       do_hint(pv);
311     }
312   }
313   else
314     hint = pv->reg[10];
315
316   // Run scanline:
317   Pico.t.m68c_line_start = Pico.t.m68c_aim;
318   do_timing_hacks_as(pv, vdp_slots);
319   CPUS_RUN(CYCLES_M68K_LINE);
320
321   if (PicoLineHook) PicoLineHook();
322   pevt_log_m68k_o(EVT_NEXT_LINE);
323
324   // sync cpus
325   cycles = SekCyclesDone();
326   if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80))
327     PicoSyncZ80(cycles);
328   if (PicoIn.sndOut && ym2612.dacen && Pico.snd.dac_line < lines)
329     PsndDoDAC(lines - 1);
330   if (PicoIn.sndOut && Pico.snd.psg_line < lines)
331     PsndDoPSG(lines - 1);
332
333 #ifdef PICO_CD
334   if (PicoIn.AHW & PAHW_MCD)
335     pcd_sync_s68k(cycles, 0);
336 #endif
337 #ifdef PICO_32X
338   p32x_sync_sh2s(cycles);
339 #endif
340   timers_cycle();
341
342   pv->hint_cnt = hint;
343
344   return 0;
345 }
346
347 #undef PAD_DELAY
348 #undef CPUS_RUN
349
350 // vim:shiftwidth=2:ts=2:expandtab