cc68a136 |
1 | // This is part of Pico Library\r |
2 | \r |
3 | // (c) Copyright 2004 Dave, All rights reserved.\r |
6cadc2da |
4 | // (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas\r |
cc68a136 |
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 "PicoInt.h"\r |
fa1e5e29 |
11 | #include "cd/gfx_cd.h"\r |
cc68a136 |
12 | \r |
13 | extern const unsigned char hcounts_32[];\r |
14 | extern const unsigned char hcounts_40[];\r |
15 | extern const unsigned short vcounts[];\r |
16 | extern int rendstatus;\r |
17 | \r |
eff55556 |
18 | #ifndef UTYPES_DEFINED\r |
ab0607f7 |
19 | typedef unsigned char u8;\r |
cc68a136 |
20 | typedef unsigned short u16;\r |
eff55556 |
21 | typedef unsigned int u32;\r |
22 | #define UTYPES_DEFINED\r |
23 | #endif\r |
cc68a136 |
24 | \r |
5de27868 |
25 | int (*PicoDmaHook)(unsigned int source, int len, unsigned short **srcp, unsigned short **limitp) = NULL;\r |
cc68a136 |
26 | \r |
69996cb7 |
27 | static __inline void AutoIncrement(void)\r |
cc68a136 |
28 | {\r |
29 | Pico.video.addr=(unsigned short)(Pico.video.addr+Pico.video.reg[0xf]);\r |
30 | }\r |
31 | \r |
32 | static void VideoWrite(u16 d)\r |
33 | {\r |
34 | unsigned int a=Pico.video.addr;\r |
35 | \r |
36 | switch (Pico.video.type)\r |
37 | {\r |
38 | case 1: if(a&1) d=(u16)((d<<8)|(d>>8)); // If address is odd, bytes are swapped (which game needs this?)\r |
39 | Pico.vram [(a>>1)&0x7fff]=d;\r |
40 | rendstatus|=0x10; break;\r |
41 | case 3: Pico.m.dirtyPal = 1;\r |
cc68a136 |
42 | Pico.cram [(a>>1)&0x003f]=d; break; // wraps (Desert Strike)\r |
43 | case 5: Pico.vsram[(a>>1)&0x003f]=d; break;\r |
d26dc685 |
44 | //default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, Pico.video.type); break;\r |
cc68a136 |
45 | }\r |
46 | \r |
47 | //dprintf("w[%i] @ %04x, inc=%i [%i|%i]", Pico.video.type, a, Pico.video.reg[0xf], Pico.m.scanline, SekCyclesDone());\r |
48 | AutoIncrement();\r |
49 | }\r |
50 | \r |
69996cb7 |
51 | static unsigned int VideoRead(void)\r |
cc68a136 |
52 | {\r |
53 | unsigned int a=0,d=0;\r |
54 | \r |
55 | a=Pico.video.addr; a>>=1;\r |
56 | \r |
57 | switch (Pico.video.type)\r |
58 | {\r |
59 | case 0: d=Pico.vram [a&0x7fff]; break;\r |
60 | case 8: d=Pico.cram [a&0x003f]; break;\r |
61 | case 4: d=Pico.vsram[a&0x003f]; break;\r |
69996cb7 |
62 | default:elprintf(EL_ANOMALY, "VDP read with bad type %i", Pico.video.type); break;\r |
cc68a136 |
63 | }\r |
4f672280 |
64 | \r |
cc68a136 |
65 | AutoIncrement();\r |
66 | return d;\r |
67 | }\r |
68 | \r |
69996cb7 |
69 | static int GetDmaLength(void)\r |
cc68a136 |
70 | {\r |
71 | struct PicoVideo *pvid=&Pico.video;\r |
72 | int len=0;\r |
73 | // 16-bit words to transfer:\r |
74 | len =pvid->reg[0x13];\r |
75 | len|=pvid->reg[0x14]<<8;\r |
76 | // Charles MacDonald:\r |
77 | if(!len) len = 0xffff;\r |
78 | return len;\r |
79 | }\r |
80 | \r |
81 | static void DmaSlow(int len)\r |
82 | {\r |
83 | u16 *pd=0, *pdend, *r;\r |
84 | unsigned int a=Pico.video.addr, a2, d;\r |
85 | unsigned char inc=Pico.video.reg[0xf];\r |
672ad671 |
86 | unsigned int source;\r |
cc68a136 |
87 | \r |
88 | source =Pico.video.reg[0x15]<<1;\r |
89 | source|=Pico.video.reg[0x16]<<9;\r |
90 | source|=Pico.video.reg[0x17]<<17;\r |
91 | \r |
69996cb7 |
92 | elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%i] @ %06x",\r |
312e9ce1 |
93 | Pico.video.type, source, a, len, inc, (Pico.video.status&8)||!(Pico.video.reg[1]&0x40),\r |
69996cb7 |
94 | SekCyclesDone(), SekPc);\r |
cc68a136 |
95 | \r |
672ad671 |
96 | if(Pico.m.scanline != -1) {\r |
69996cb7 |
97 | Pico.m.dma_xfers += len;\r |
2d0b15bb |
98 | if ((PicoMCD&1) && (PicoOpt & 0x2000)) SekCyclesBurn(CheckDMA());\r |
7336a99a |
99 | else SekSetCyclesLeftNoMCD(SekCyclesLeftNoMCD - CheckDMA());\r |
672ad671 |
100 | } else {\r |
101 | // be approximate in non-accurate mode\r |
102 | SekSetCyclesLeft(SekCyclesLeft - (len*(((488<<8)/167))>>8));\r |
103 | }\r |
104 | \r |
ab0607f7 |
105 | if ((source&0xe00000)==0xe00000) { // Ram\r |
106 | pd=(u16 *)(Pico.ram+(source&0xfffe));\r |
107 | pdend=(u16 *)(Pico.ram+0x10000);\r |
f8ef8ff7 |
108 | } else if (PicoMCD & 1) {\r |
69996cb7 |
109 | elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", Pico_mcd->s68k_regs[3]);\r |
ab0607f7 |
110 | if(source<0x20000) { // Bios area\r |
111 | pd=(u16 *)(Pico_mcd->bios+(source&~1));\r |
112 | pdend=(u16 *)(Pico_mcd->bios+0x20000);\r |
fa1e5e29 |
113 | } else if ((source&0xfc0000)==0x200000) { // Word Ram\r |
114 | source -= 2;\r |
ab0607f7 |
115 | if (!(Pico_mcd->s68k_regs[3]&4)) { // 2M mode\r |
fa1e5e29 |
116 | pd=(u16 *)(Pico_mcd->word_ram2M+(source&0x3fffe));\r |
117 | pdend=(u16 *)(Pico_mcd->word_ram2M+0x40000);\r |
ab0607f7 |
118 | } else {\r |
fa1e5e29 |
119 | if (source < 0x220000) { // 1M mode\r |
120 | int bank = Pico_mcd->s68k_regs[3]&1;\r |
121 | pd=(u16 *)(Pico_mcd->word_ram1M[bank]+(source&0x1fffe));\r |
122 | pdend=(u16 *)(Pico_mcd->word_ram1M[bank]+0x20000);\r |
69996cb7 |
123 | } else {\r |
fa1e5e29 |
124 | DmaSlowCell(source, a, len, inc);\r |
125 | return;\r |
69996cb7 |
126 | }\r |
ab0607f7 |
127 | }\r |
128 | } else if ((source&0xfe0000)==0x020000) { // Prg Ram\r |
129 | u8 *prg_ram = Pico_mcd->prg_ram_b[Pico_mcd->s68k_regs[3]>>6];\r |
130 | pd=(u16 *)(prg_ram+(source&0x1fffe));\r |
131 | pdend=(u16 *)(prg_ram+0x20000);\r |
132 | } else {\r |
03e4f2a3 |
133 | elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: FIXME: unsupported src", Pico.video.type, source, a);\r |
ab0607f7 |
134 | return;\r |
135 | }\r |
672ad671 |
136 | } else {\r |
50483b53 |
137 | // if we have DmaHook, let it handle ROM because of possible DMA delay\r |
138 | if (PicoDmaHook && PicoDmaHook(source, len, &pd, &pdend));\r |
139 | else if (source<Pico.romsize) { // Rom\r |
ab0607f7 |
140 | pd=(u16 *)(Pico.rom+(source&~1));\r |
141 | pdend=(u16 *)(Pico.rom+Pico.romsize);\r |
f8ef8ff7 |
142 | }\r |
f8ef8ff7 |
143 | else {\r |
03e4f2a3 |
144 | elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", Pico.video.type, source, a);\r |
ab0607f7 |
145 | return;\r |
146 | }\r |
147 | }\r |
148 | \r |
149 | // overflow protection, might break something..\r |
150 | if (len > pdend - pd) {\r |
151 | len = pdend - pd;\r |
69996cb7 |
152 | elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow overflow");\r |
672ad671 |
153 | }\r |
cc68a136 |
154 | \r |
155 | switch (Pico.video.type)\r |
156 | {\r |
157 | case 1: // vram\r |
158 | r = Pico.vram;\r |
cea65903 |
159 | if (inc == 2 && !(a&1) && a+len*2 < 0x10000)\r |
cc68a136 |
160 | {\r |
cea65903 |
161 | // most used DMA mode\r |
fa1e5e29 |
162 | memcpy16(r + (a>>1), pd, len);\r |
163 | a += len*2;\r |
cea65903 |
164 | }\r |
165 | else\r |
166 | {\r |
167 | for(; len; len--)\r |
168 | {\r |
169 | d=*pd++;\r |
170 | if(a&1) d=(d<<8)|(d>>8);\r |
171 | r[a>>1] = (u16)d; // will drop the upper bits\r |
172 | // AutoIncrement\r |
173 | a=(u16)(a+inc);\r |
174 | // didn't src overlap?\r |
175 | //if(pd >= pdend) pd-=0x8000; // should be good for RAM, bad for ROM\r |
176 | }\r |
cc68a136 |
177 | }\r |
178 | rendstatus|=0x10;\r |
179 | break;\r |
4f672280 |
180 | \r |
cc68a136 |
181 | case 3: // cram\r |
cc68a136 |
182 | Pico.m.dirtyPal = 1;\r |
183 | r = Pico.cram;\r |
184 | for(a2=a&0x7f; len; len--)\r |
185 | {\r |
0a051f55 |
186 | r[a2>>1] = (u16)*pd++; // bit 0 is ignored\r |
cc68a136 |
187 | // AutoIncrement\r |
188 | a2+=inc;\r |
189 | // didn't src overlap?\r |
ab0607f7 |
190 | //if(pd >= pdend) pd-=0x8000;\r |
cc68a136 |
191 | // good dest?\r |
192 | if(a2 >= 0x80) break; // Todds Adventures in Slime World / Andre Agassi tennis\r |
193 | }\r |
194 | a=(a&0xff00)|a2;\r |
195 | break;\r |
196 | \r |
197 | case 5: // vsram[a&0x003f]=d;\r |
198 | r = Pico.vsram;\r |
199 | for(a2=a&0x7f; len; len--)\r |
200 | {\r |
201 | r[a2>>1] = (u16)*pd++;\r |
202 | // AutoIncrement\r |
203 | a2+=inc;\r |
204 | // didn't src overlap?\r |
ab0607f7 |
205 | //if(pd >= pdend) pd-=0x8000;\r |
cc68a136 |
206 | // good dest?\r |
207 | if(a2 >= 0x80) break;\r |
208 | }\r |
209 | a=(a&0xff00)|a2;\r |
210 | break;\r |
69996cb7 |
211 | \r |
212 | default:\r |
213 | elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", Pico.video.type);\r |
214 | break;\r |
cc68a136 |
215 | }\r |
216 | // remember addr\r |
217 | Pico.video.addr=(u16)a;\r |
218 | }\r |
219 | \r |
220 | static void DmaCopy(int len)\r |
221 | {\r |
222 | u16 a=Pico.video.addr;\r |
223 | unsigned char *vr = (unsigned char *) Pico.vram;\r |
224 | unsigned char *vrs;\r |
225 | unsigned char inc=Pico.video.reg[0xf];\r |
226 | int source;\r |
69996cb7 |
227 | elprintf(EL_VDPDMA, "DmaCopy len %i [%i]", len, SekCyclesDone());\r |
4f672280 |
228 | \r |
69996cb7 |
229 | Pico.m.dma_xfers += len;\r |
8c1952f0 |
230 | if(Pico.m.scanline != -1)\r |
231 | Pico.video.status|=2; // dma busy\r |
cc68a136 |
232 | \r |
233 | source =Pico.video.reg[0x15];\r |
234 | source|=Pico.video.reg[0x16]<<8;\r |
235 | vrs=vr+source;\r |
236 | \r |
237 | if(source+len > 0x10000) len=0x10000-source; // clip??\r |
238 | \r |
239 | for(;len;len--)\r |
240 | {\r |
241 | vr[a] = *vrs++;\r |
242 | // AutoIncrement\r |
243 | a=(u16)(a+inc);\r |
244 | }\r |
245 | // remember addr\r |
246 | Pico.video.addr=a;\r |
247 | rendstatus|=0x10;\r |
248 | }\r |
249 | \r |
250 | // check: Contra, Megaman\r |
251 | // note: this is still inaccurate\r |
252 | static void DmaFill(int data)\r |
253 | {\r |
254 | int len;\r |
255 | unsigned short a=Pico.video.addr;\r |
256 | unsigned char *vr=(unsigned char *) Pico.vram;\r |
257 | unsigned char high = (unsigned char) (data >> 8);\r |
258 | unsigned char inc=Pico.video.reg[0xf];\r |
4f672280 |
259 | \r |
cc68a136 |
260 | len=GetDmaLength();\r |
69996cb7 |
261 | elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%i]", len, inc, SekCyclesDone());\r |
4f672280 |
262 | \r |
69996cb7 |
263 | Pico.m.dma_xfers += len;\r |
8c1952f0 |
264 | if(Pico.m.scanline != -1)\r |
265 | Pico.video.status|=2; // dma busy (in accurate mode)\r |
cc68a136 |
266 | \r |
267 | // from Charles MacDonald's genvdp.txt:\r |
268 | // Write lower byte to address specified\r |
269 | vr[a] = (unsigned char) data;\r |
270 | a=(u16)(a+inc);\r |
271 | \r |
272 | if(!inc) len=1;\r |
273 | \r |
274 | for(;len;len--) {\r |
275 | // Write upper byte to adjacent address\r |
276 | // (here we are byteswapped, so address is already 'adjacent')\r |
277 | vr[a] = high;\r |
278 | \r |
279 | // Increment address register\r |
280 | a=(u16)(a+inc);\r |
281 | }\r |
282 | // remember addr\r |
283 | Pico.video.addr=a;\r |
284 | // update length\r |
285 | Pico.video.reg[0x13] = Pico.video.reg[0x14] = 0; // Dino Dini's Soccer (E) (by Haze)\r |
286 | \r |
287 | rendstatus|=0x10;\r |
288 | }\r |
289 | \r |
69996cb7 |
290 | static void CommandDma(void)\r |
cc68a136 |
291 | {\r |
292 | struct PicoVideo *pvid=&Pico.video;\r |
293 | int len=0,method=0;\r |
294 | \r |
295 | if ((pvid->reg[1]&0x10)==0) return; // DMA not enabled\r |
296 | \r |
297 | len=GetDmaLength();\r |
298 | \r |
299 | method=pvid->reg[0x17]>>6;\r |
300 | if (method< 2) DmaSlow(len); // 68000 to VDP\r |
301 | if (method==3) DmaCopy(len); // VRAM Copy\r |
302 | }\r |
303 | \r |
69996cb7 |
304 | static void CommandChange(void)\r |
cc68a136 |
305 | {\r |
306 | struct PicoVideo *pvid=&Pico.video;\r |
307 | unsigned int cmd=0,addr=0;\r |
308 | \r |
309 | cmd=pvid->command;\r |
310 | \r |
311 | // Get type of transfer 0xc0000030 (v/c/vsram read/write)\r |
312 | pvid->type=(unsigned char)(((cmd>>2)&0xc)|(cmd>>30));\r |
313 | \r |
314 | // Get address 0x3fff0003\r |
315 | addr =(cmd>>16)&0x3fff;\r |
316 | addr|=(cmd<<14)&0xc000;\r |
317 | pvid->addr=(unsigned short)addr;\r |
cc68a136 |
318 | \r |
319 | // Check for dma:\r |
320 | if (cmd&0x80) CommandDma();\r |
321 | }\r |
322 | \r |
eff55556 |
323 | PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d)\r |
cc68a136 |
324 | {\r |
325 | struct PicoVideo *pvid=&Pico.video;\r |
326 | \r |
327 | a&=0x1c;\r |
328 | \r |
329 | if (a==0x00) // Data port 0 or 2\r |
4f672280 |
330 | {\r |
69996cb7 |
331 | if (pvid->pending) {\r |
332 | CommandChange();\r |
333 | pvid->pending=0;\r |
334 | }\r |
cc68a136 |
335 | \r |
336 | // If a DMA fill has been set up, do it\r |
337 | if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[0x17]>>6)==2)\r |
338 | {\r |
339 | DmaFill(d);\r |
340 | }\r |
341 | else\r |
342 | {\r |
69996cb7 |
343 | // preliminary FIFO emulation for Chaos Engine, The (E)\r |
1dceadae |
344 | if(!(pvid->status&8) && (pvid->reg[1]&0x40) && Pico.m.scanline!=-1 && !(PicoOpt&0x10000)) // active display, accurate mode?\r |
69996cb7 |
345 | {\r |
346 | pvid->status&=~0x200; // FIFO no longer empty\r |
347 | pvid->lwrite_cnt++;\r |
348 | if (pvid->lwrite_cnt >= 4) pvid->status|=0x100; // FIFO full\r |
349 | if (pvid->lwrite_cnt > 4) {\r |
350 | SekCyclesBurn(32); // penalty // 488/12-8\r |
351 | if (SekCycleCnt>=SekCycleAim) SekEndRun(0);\r |
352 | }\r |
353 | elprintf(EL_ASVDP, "VDP data write: %04x {%i} #%i @ %06x", d, Pico.video.type, pvid->lwrite_cnt, SekPc);\r |
354 | }\r |
cc68a136 |
355 | VideoWrite(d);\r |
356 | }\r |
357 | return;\r |
358 | }\r |
359 | \r |
360 | if (a==0x04) // Control (command) port 4 or 6\r |
361 | {\r |
cc68a136 |
362 | if(pvid->pending)\r |
363 | {\r |
364 | // Low word of command:\r |
365 | pvid->command&=0xffff0000;\r |
366 | pvid->command|=d;\r |
367 | pvid->pending=0;\r |
368 | CommandChange();\r |
369 | } else {\r |
370 | if((d&0xc000)==0x8000)\r |
371 | {\r |
372 | // Register write:\r |
373 | int num=(d>>8)&0x1f;\r |
0ffefdb8 |
374 | int dold=pvid->reg[num];\r |
375 | if (num > 0x0a && !(pvid->reg[1]&4)) {\r |
376 | elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);\r |
377 | } else\r |
378 | pvid->reg[num]=(unsigned char)d;\r |
379 | if (num==00) elprintf(EL_INTSW, "hint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x10)>>4,\r |
69996cb7 |
380 | (d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);\r |
0ffefdb8 |
381 | if (num==01) elprintf(EL_INTSW, "vint_onoff: %i->%i [%i] pend=%i @ %06x", (dold&0x20)>>5,\r |
69996cb7 |
382 | (d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);\r |
0ffefdb8 |
383 | if (num == 5 && (d^dold)) rendstatus|=1;\r |
384 | // renderers should update their palettes if sh/hi mode is changed\r |
385 | else if (num == 0xc && ((d^dold)&8)) Pico.m.dirtyPal = 2;\r |
03e4f2a3 |
386 | #ifndef EMU_CORE_DEBUG\r |
cc68a136 |
387 | // update IRQ level (Lemmings, Wiz 'n' Liz intro, ... )\r |
388 | // may break if done improperly:\r |
69996cb7 |
389 | // International Superstar Soccer Deluxe (crash), Street Racer (logos), Burning Force (gfx),\r |
03e4f2a3 |
390 | // Fatal Rewind (crash), Sesame Street Counting Cafe\r |
0ffefdb8 |
391 | else if (num < 2)\r |
03e4f2a3 |
392 | {\r |
393 | if (!SekShouldInterrupt) // hack\r |
394 | {\r |
395 | int lines, pints, irq=0;\r |
cc68a136 |
396 | lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);\r |
397 | pints = (pvid->pending_ints&lines);\r |
03e4f2a3 |
398 | if(pints & 0x20) irq = 6;\r |
399 | else if(pints & 0x10) irq = 4;\r |
400 | SekInterrupt(irq); // update line\r |
401 | \r |
402 | if (irq) SekEndRun(24); // make it delayed\r |
cc68a136 |
403 | }\r |
cc68a136 |
404 | }\r |
cc68a136 |
405 | #endif\r |
cc68a136 |
406 | pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)\r |
407 | } else {\r |
408 | // High word of command:\r |
409 | pvid->command&=0x0000ffff;\r |
410 | pvid->command|=d<<16;\r |
411 | pvid->pending=1;\r |
412 | }\r |
413 | }\r |
414 | }\r |
415 | }\r |
416 | \r |
eff55556 |
417 | PICO_INTERNAL_ASM unsigned int PicoVideoRead(unsigned int a)\r |
cc68a136 |
418 | {\r |
419 | unsigned int d=0;\r |
4f672280 |
420 | \r |
cc68a136 |
421 | a&=0x1c;\r |
422 | \r |
69996cb7 |
423 | \r |
cc68a136 |
424 | if (a==0x00) // data port\r |
425 | {\r |
426 | d=VideoRead();\r |
427 | goto end;\r |
428 | }\r |
429 | \r |
430 | if (a==0x04) // control port\r |
431 | {\r |
69996cb7 |
432 | struct PicoVideo *pv=&Pico.video;\r |
433 | d=pv->status;\r |
434 | if (PicoOpt&0x10) d|=0x0020; // sprite collision (Shadow of the Beast)\r |
435 | if (!(pv->reg[1]&0x40)) d|=0x0008; // set V-Blank if display is disabled\r |
436 | if (SekCyclesLeft < 84+4) d|=0x0004; // H-Blank (Sonic3 vs)\r |
437 | \r |
438 | d|=(pv->pending_ints&0x20)<<2; // V-int pending?\r |
439 | if (d&0x100) pv->status&=~0x100; // FIFO no longer full\r |
cc68a136 |
440 | \r |
69996cb7 |
441 | pv->pending=0; // ctrl port reads clear write-pending flag (Charles MacDonald)\r |
cc68a136 |
442 | \r |
69996cb7 |
443 | elprintf(EL_SR, "SR read: %04x @ %06x", d, SekPc);\r |
cc68a136 |
444 | goto end;\r |
445 | }\r |
446 | \r |
447 | // H-counter info (based on Generator):\r |
448 | // frame:\r |
449 | // | <- hblank? -> |\r |
450 | // start <416> hint <36> hdisplay <38> end // CPU cycles\r |
451 | // |---------...---------|------------|-------------|\r |
452 | // 0 B6 E4 FF // 40 cells\r |
453 | // 0 93 E8 FF // 32 cells\r |
454 | \r |
455 | // Gens (?) v-render\r |
456 | // start <hblank=84> hint hdisplay <404> |\r |
457 | // |---------------------|--------------------------|\r |
458 | // E4 (hc[0x43]==0) 07 B1 // 40\r |
459 | // E8 (hc[0x45]==0) 05 91 // 32\r |
4f672280 |
460 | \r |
cc68a136 |
461 | // check: Sonic 3D Blast bonus, Cannon Fodder, Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune\r |
462 | if ((a&0x1c)==0x08)\r |
463 | {\r |
464 | unsigned int hc;\r |
465 | \r |
466 | if(Pico.m.scanline != -1) {\r |
467 | int lineCycles=(488-SekCyclesLeft)&0x1ff;\r |
468 | d=Pico.m.scanline; // V-Counter\r |
469 | \r |
470 | if(Pico.video.reg[12]&1)\r |
471 | hc=hcounts_40[lineCycles];\r |
472 | else hc=hcounts_32[lineCycles];\r |
473 | \r |
8c1952f0 |
474 | //if(lineCycles > 488-12) d++; // Wheel of Fortune\r |
cc68a136 |
475 | } else {\r |
476 | // get approximate V-Counter\r |
477 | d=vcounts[SekCyclesDone()>>8];\r |
478 | hc = Pico.m.rotate&0xff;\r |
479 | }\r |
480 | \r |
481 | if(Pico.m.pal) {\r |
482 | if(d >= 0x103) d-=56; // based on Gens\r |
483 | } else {\r |
484 | if(d >= 0xEB) d-=6;\r |
485 | }\r |
486 | \r |
487 | if((Pico.video.reg[12]&6) == 6) {\r |
488 | // interlace mode 2 (Combat Cars (UE) [!])\r |
489 | d <<= 1;\r |
490 | if (d&0xf00) d|= 1;\r |
491 | }\r |
492 | \r |
69996cb7 |
493 | elprintf(EL_HVCNT, "hv: %02x %02x (%i) @ %06x", hc, d, SekCyclesDone(), SekPc);\r |
cc68a136 |
494 | d&=0xff; d<<=8;\r |
495 | d|=hc;\r |
496 | goto end;\r |
497 | }\r |
498 | \r |
499 | end:\r |
500 | \r |
501 | return d;\r |
502 | }\r |