| 1 | /*\r |
| 2 | * PicoDrive\r |
| 3 | * (c) Copyright Dave, 2004\r |
| 4 | * (C) notaz, 2006-2009\r |
| 5 | *\r |
| 6 | * This work is licensed under the terms of MAME license.\r |
| 7 | * See COPYING file in the top-level directory.\r |
| 8 | */\r |
| 9 | \r |
| 10 | #include "pico_int.h"\r |
| 11 | #define NEED_DMA_SOURCE\r |
| 12 | #include "memory.h"\r |
| 13 | \r |
| 14 | int line_base_cycles;\r |
| 15 | extern const unsigned char hcounts_32[];\r |
| 16 | extern const unsigned char hcounts_40[];\r |
| 17 | \r |
| 18 | #ifndef UTYPES_DEFINED\r |
| 19 | typedef unsigned char u8;\r |
| 20 | typedef unsigned short u16;\r |
| 21 | typedef unsigned int u32;\r |
| 22 | #define UTYPES_DEFINED\r |
| 23 | #endif\r |
| 24 | \r |
| 25 | int (*PicoDmaHook)(unsigned int source, int len, unsigned short **base, unsigned int *mask) = NULL;\r |
| 26 | \r |
| 27 | static __inline void AutoIncrement(void)\r |
| 28 | {\r |
| 29 | Pico.video.addr=(unsigned short)(Pico.video.addr+Pico.video.reg[0xf]);\r |
| 30 | }\r |
| 31 | \r |
| 32 | static NOINLINE void VideoWrite128(u32 a, u16 d)\r |
| 33 | {\r |
| 34 | // nasty\r |
| 35 | a = ((a & 2) >> 1) | ((a & 0x400) >> 9) | (a & 0x3FC) | ((a & 0x1F800) >> 1);\r |
| 36 | ((u8 *)Pico.vram)[a] = d;\r |
| 37 | }\r |
| 38 | \r |
| 39 | static void VideoWrite(u16 d)\r |
| 40 | {\r |
| 41 | unsigned int a=Pico.video.addr;\r |
| 42 | \r |
| 43 | switch (Pico.video.type)\r |
| 44 | {\r |
| 45 | case 1: if(a&1) d=(u16)((d<<8)|(d>>8)); // If address is odd, bytes are swapped (which game needs this?)\r |
| 46 | Pico.vram [(a>>1)&0x7fff]=d;\r |
| 47 | if (a - ((unsigned)(Pico.video.reg[5]&0x7f) << 9) < 0x400)\r |
| 48 | Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r |
| 49 | break;\r |
| 50 | case 3: Pico.m.dirtyPal = 1;\r |
| 51 | Pico.cram [(a>>1)&0x003f]=d; break; // wraps (Desert Strike)\r |
| 52 | case 5: Pico.vsram[(a>>1)&0x003f]=d; break;\r |
| 53 | case 0x81:\r |
| 54 | a |= Pico.video.addr_u << 16;\r |
| 55 | VideoWrite128(a, d);\r |
| 56 | break;\r |
| 57 | //default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, Pico.video.type); break;\r |
| 58 | }\r |
| 59 | \r |
| 60 | AutoIncrement();\r |
| 61 | }\r |
| 62 | \r |
| 63 | static unsigned int VideoRead(void)\r |
| 64 | {\r |
| 65 | unsigned int a=0,d=0;\r |
| 66 | \r |
| 67 | a=Pico.video.addr; a>>=1;\r |
| 68 | \r |
| 69 | switch (Pico.video.type)\r |
| 70 | {\r |
| 71 | case 0: d=Pico.vram [a&0x7fff]; break;\r |
| 72 | case 8: d=Pico.cram [a&0x003f]; break;\r |
| 73 | case 4: d=Pico.vsram[a&0x003f]; break;\r |
| 74 | default:elprintf(EL_ANOMALY, "VDP read with bad type %i", Pico.video.type); break;\r |
| 75 | }\r |
| 76 | \r |
| 77 | AutoIncrement();\r |
| 78 | return d;\r |
| 79 | }\r |
| 80 | \r |
| 81 | static int GetDmaLength(void)\r |
| 82 | {\r |
| 83 | struct PicoVideo *pvid=&Pico.video;\r |
| 84 | int len=0;\r |
| 85 | // 16-bit words to transfer:\r |
| 86 | len =pvid->reg[0x13];\r |
| 87 | len|=pvid->reg[0x14]<<8;\r |
| 88 | len = ((len - 1) & 0xffff) + 1;\r |
| 89 | return len;\r |
| 90 | }\r |
| 91 | \r |
| 92 | static void DmaSlow(int len, unsigned int source)\r |
| 93 | {\r |
| 94 | u32 inc = Pico.video.reg[0xf];\r |
| 95 | u32 a = Pico.video.addr;\r |
| 96 | u16 *r, *base = NULL;\r |
| 97 | u32 mask = 0x1ffff;\r |
| 98 | \r |
| 99 | elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%u] @ %06x",\r |
| 100 | Pico.video.type, source, a, len, inc, (Pico.video.status&8)||!(Pico.video.reg[1]&0x40),\r |
| 101 | SekCyclesDone(), SekPc);\r |
| 102 | \r |
| 103 | Pico.m.dma_xfers += len;\r |
| 104 | if (Pico.m.dma_xfers < len) // lame 16bit var\r |
| 105 | Pico.m.dma_xfers = ~0;\r |
| 106 | SekCyclesBurnRun(CheckDMA());\r |
| 107 | \r |
| 108 | if ((source & 0xe00000) == 0xe00000) { // Ram\r |
| 109 | base = (u16 *)Pico.ram;\r |
| 110 | mask = 0xffff;\r |
| 111 | }\r |
| 112 | else if (PicoAHW & PAHW_MCD)\r |
| 113 | {\r |
| 114 | u8 r3 = Pico_mcd->s68k_regs[3];\r |
| 115 | elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", r3);\r |
| 116 | if (source < 0x20000) { // Bios area\r |
| 117 | base = (u16 *)Pico_mcd->bios;\r |
| 118 | } else if ((source & 0xfc0000) == 0x200000) { // Word Ram\r |
| 119 | if (!(r3 & 4)) { // 2M mode\r |
| 120 | base = (u16 *)(Pico_mcd->word_ram2M + (source & 0x20000));\r |
| 121 | } else {\r |
| 122 | if (source < 0x220000) { // 1M mode\r |
| 123 | int bank = r3 & 1;\r |
| 124 | base = (u16 *)(Pico_mcd->word_ram1M[bank]);\r |
| 125 | } else {\r |
| 126 | DmaSlowCell(source - 2, a, len, inc);\r |
| 127 | return;\r |
| 128 | }\r |
| 129 | }\r |
| 130 | source -= 2;\r |
| 131 | } else if ((source & 0xfe0000) == 0x020000) { // Prg Ram\r |
| 132 | base = (u16 *)Pico_mcd->prg_ram_b[r3 >> 6];\r |
| 133 | source -= 2; // XXX: test\r |
| 134 | }\r |
| 135 | }\r |
| 136 | else\r |
| 137 | {\r |
| 138 | // if we have DmaHook, let it handle ROM because of possible DMA delay\r |
| 139 | u32 source2;\r |
| 140 | if (PicoDmaHook && (source2 = PicoDmaHook(source, len, &base, &mask)))\r |
| 141 | source = source2;\r |
| 142 | else // Rom\r |
| 143 | base = m68k_dma_source(source);\r |
| 144 | }\r |
| 145 | if (!base) {\r |
| 146 | elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", Pico.video.type, source, a);\r |
| 147 | return;\r |
| 148 | }\r |
| 149 | \r |
| 150 | // operate in words\r |
| 151 | source >>= 1;\r |
| 152 | mask >>= 1;\r |
| 153 | \r |
| 154 | switch (Pico.video.type)\r |
| 155 | {\r |
| 156 | case 1: // vram\r |
| 157 | r = Pico.vram;\r |
| 158 | if (inc == 2 && !(a & 1) && a + len * 2 < 0x10000\r |
| 159 | && !(((source + len - 1) ^ source) & ~mask))\r |
| 160 | {\r |
| 161 | // most used DMA mode\r |
| 162 | memcpy((char *)r + a, base + (source & mask), len * 2);\r |
| 163 | a += len * 2;\r |
| 164 | }\r |
| 165 | else\r |
| 166 | {\r |
| 167 | for(; len; len--)\r |
| 168 | {\r |
| 169 | u16 d = base[source++ & mask];\r |
| 170 | if(a & 1) d=(d<<8)|(d>>8);\r |
| 171 | r[a >> 1] = d;\r |
| 172 | // AutoIncrement\r |
| 173 | a = (u16)(a + inc);\r |
| 174 | }\r |
| 175 | }\r |
| 176 | Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r |
| 177 | break;\r |
| 178 | \r |
| 179 | case 3: // cram\r |
| 180 | Pico.m.dirtyPal = 1;\r |
| 181 | r = Pico.cram;\r |
| 182 | for (; len; len--)\r |
| 183 | {\r |
| 184 | r[(a / 2) & 0x3f] = base[source++ & mask];\r |
| 185 | // AutoIncrement\r |
| 186 | a += inc;\r |
| 187 | }\r |
| 188 | break;\r |
| 189 | \r |
| 190 | case 5: // vsram\r |
| 191 | r = Pico.vsram;\r |
| 192 | for (; len; len--)\r |
| 193 | {\r |
| 194 | r[(a / 2) & 0x3f] = base[source++ & mask];\r |
| 195 | // AutoIncrement\r |
| 196 | a += inc;\r |
| 197 | }\r |
| 198 | break;\r |
| 199 | \r |
| 200 | case 0x81: // vram 128k\r |
| 201 | a |= Pico.video.addr_u << 16;\r |
| 202 | for(; len; len--)\r |
| 203 | {\r |
| 204 | VideoWrite128(a, base[source++ & mask]);\r |
| 205 | // AutoIncrement\r |
| 206 | a = (a + inc) & 0x1ffff;\r |
| 207 | }\r |
| 208 | Pico.video.addr_u = a >> 16;\r |
| 209 | break;\r |
| 210 | \r |
| 211 | default:\r |
| 212 | if (Pico.video.type != 0 || (EL_LOGMASK & EL_VDPDMA))\r |
| 213 | elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", Pico.video.type);\r |
| 214 | break;\r |
| 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 inc=Pico.video.reg[0xf];\r |
| 225 | int source;\r |
| 226 | elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());\r |
| 227 | \r |
| 228 | Pico.m.dma_xfers += len;\r |
| 229 | if (Pico.m.dma_xfers < len)\r |
| 230 | Pico.m.dma_xfers = ~0;\r |
| 231 | Pico.video.status |= 2; // dma busy\r |
| 232 | \r |
| 233 | source =Pico.video.reg[0x15];\r |
| 234 | source|=Pico.video.reg[0x16]<<8;\r |
| 235 | \r |
| 236 | for (; len; len--)\r |
| 237 | {\r |
| 238 | vr[a] = vr[source++ & 0xffff];\r |
| 239 | // AutoIncrement\r |
| 240 | a=(u16)(a+inc);\r |
| 241 | }\r |
| 242 | // remember addr\r |
| 243 | Pico.video.addr=a;\r |
| 244 | Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r |
| 245 | }\r |
| 246 | \r |
| 247 | static NOINLINE void DmaFill(int data)\r |
| 248 | {\r |
| 249 | unsigned short a=Pico.video.addr;\r |
| 250 | unsigned char *vr=(unsigned char *) Pico.vram;\r |
| 251 | unsigned char high = (unsigned char) (data >> 8);\r |
| 252 | unsigned char inc=Pico.video.reg[0xf];\r |
| 253 | int source;\r |
| 254 | int len, l;\r |
| 255 | \r |
| 256 | len = GetDmaLength();\r |
| 257 | elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());\r |
| 258 | \r |
| 259 | Pico.m.dma_xfers += len;\r |
| 260 | if (Pico.m.dma_xfers < len) // lame 16bit var\r |
| 261 | Pico.m.dma_xfers = ~0;\r |
| 262 | Pico.video.status |= 2; // dma busy\r |
| 263 | \r |
| 264 | switch (Pico.video.type)\r |
| 265 | {\r |
| 266 | case 1: // vram\r |
| 267 | for (l = len; l; l--) {\r |
| 268 | // Write upper byte to adjacent address\r |
| 269 | // (here we are byteswapped, so address is already 'adjacent')\r |
| 270 | vr[a] = high;\r |
| 271 | \r |
| 272 | // Increment address register\r |
| 273 | a = (u16)(a + inc);\r |
| 274 | }\r |
| 275 | break;\r |
| 276 | case 3: // cram\r |
| 277 | case 5: { // vsram\r |
| 278 | // TODO: needs fifo; anyone using these?\r |
| 279 | static int once;\r |
| 280 | if (!once++)\r |
| 281 | elprintf(EL_STATUS|EL_ANOMALY|EL_VDPDMA, "TODO: cram/vsram fill");\r |
| 282 | }\r |
| 283 | default:\r |
| 284 | a += len * inc;\r |
| 285 | break;\r |
| 286 | }\r |
| 287 | \r |
| 288 | // remember addr\r |
| 289 | Pico.video.addr = a;\r |
| 290 | // register update\r |
| 291 | Pico.video.reg[0x13] = Pico.video.reg[0x14] = 0;\r |
| 292 | source = Pico.video.reg[0x15];\r |
| 293 | source |= Pico.video.reg[0x16] << 8;\r |
| 294 | source += len;\r |
| 295 | Pico.video.reg[0x15] = source;\r |
| 296 | Pico.video.reg[0x16] = source >> 8;\r |
| 297 | \r |
| 298 | Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r |
| 299 | }\r |
| 300 | \r |
| 301 | static NOINLINE void CommandDma(void)\r |
| 302 | {\r |
| 303 | struct PicoVideo *pvid=&Pico.video;\r |
| 304 | u32 len, method;\r |
| 305 | u32 source;\r |
| 306 | \r |
| 307 | if ((pvid->reg[1]&0x10)==0) return; // DMA not enabled\r |
| 308 | \r |
| 309 | len = GetDmaLength();\r |
| 310 | source =Pico.video.reg[0x15];\r |
| 311 | source|=Pico.video.reg[0x16] << 8;\r |
| 312 | source|=Pico.video.reg[0x17] << 16;\r |
| 313 | \r |
| 314 | method=pvid->reg[0x17]>>6;\r |
| 315 | if (method < 2)\r |
| 316 | DmaSlow(len, source << 1); // 68000 to VDP\r |
| 317 | else if (method == 3)\r |
| 318 | DmaCopy(len); // VRAM Copy\r |
| 319 | else\r |
| 320 | return;\r |
| 321 | \r |
| 322 | source += len;\r |
| 323 | Pico.video.reg[0x13] = Pico.video.reg[0x14] = 0;\r |
| 324 | Pico.video.reg[0x15] = source;\r |
| 325 | Pico.video.reg[0x16] = source >> 8;\r |
| 326 | }\r |
| 327 | \r |
| 328 | static NOINLINE void CommandChange(void)\r |
| 329 | {\r |
| 330 | struct PicoVideo *pvid = &Pico.video;\r |
| 331 | unsigned int cmd, addr;\r |
| 332 | \r |
| 333 | cmd = pvid->command;\r |
| 334 | \r |
| 335 | // Get type of transfer 0xc0000030 (v/c/vsram read/write)\r |
| 336 | pvid->type = (u8)(((cmd >> 2) & 0xc) | (cmd >> 30));\r |
| 337 | if (pvid->type == 1) // vram\r |
| 338 | pvid->type |= pvid->reg[1] & 0x80; // 128k\r |
| 339 | \r |
| 340 | // Get address 0x3fff0003\r |
| 341 | addr = (cmd >> 16) & 0x3fff;\r |
| 342 | addr |= (cmd << 14) & 0xc000;\r |
| 343 | pvid->addr = (u16)addr;\r |
| 344 | pvid->addr_u = (u8)((cmd >> 2) & 1);\r |
| 345 | }\r |
| 346 | \r |
| 347 | static void DrawSync(int blank_on)\r |
| 348 | {\r |
| 349 | if (Pico.m.scanline < 224 && !(PicoOpt & POPT_ALT_RENDERER) &&\r |
| 350 | !PicoSkipFrame && Pico.est.DrawScanline <= Pico.m.scanline) {\r |
| 351 | //elprintf(EL_ANOMALY, "sync");\r |
| 352 | PicoDrawSync(Pico.m.scanline, blank_on);\r |
| 353 | }\r |
| 354 | }\r |
| 355 | \r |
| 356 | PICO_INTERNAL_ASM void PicoVideoWrite(unsigned int a,unsigned short d)\r |
| 357 | {\r |
| 358 | struct PicoVideo *pvid=&Pico.video;\r |
| 359 | \r |
| 360 | //if (Pico.m.scanline < 224)\r |
| 361 | // elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x", a, d);\r |
| 362 | a&=0x1c;\r |
| 363 | \r |
| 364 | switch (a)\r |
| 365 | {\r |
| 366 | case 0x00: // Data port 0 or 2\r |
| 367 | // try avoiding the sync..\r |
| 368 | if (Pico.m.scanline < 224 && (pvid->reg[1]&0x40) &&\r |
| 369 | !(!pvid->pending &&\r |
| 370 | ((pvid->command & 0xc00000f0) == 0x40000010 && Pico.vsram[pvid->addr>>1] == d))\r |
| 371 | )\r |
| 372 | DrawSync(0);\r |
| 373 | \r |
| 374 | if (pvid->pending) {\r |
| 375 | CommandChange();\r |
| 376 | pvid->pending=0;\r |
| 377 | }\r |
| 378 | \r |
| 379 | // preliminary FIFO emulation for Chaos Engine, The (E)\r |
| 380 | if (!(pvid->status&8) && (pvid->reg[1]&0x40) && !(PicoOpt&POPT_DIS_VDP_FIFO)) // active display?\r |
| 381 | {\r |
| 382 | pvid->status&=~0x200; // FIFO no longer empty\r |
| 383 | pvid->lwrite_cnt++;\r |
| 384 | if (pvid->lwrite_cnt >= 4) pvid->status|=0x100; // FIFO full\r |
| 385 | if (pvid->lwrite_cnt > 4) {\r |
| 386 | SekCyclesBurnRun(32); // penalty // 488/12-8\r |
| 387 | }\r |
| 388 | elprintf(EL_ASVDP, "VDP data write: %04x [%06x] {%i} #%i @ %06x", d, Pico.video.addr,\r |
| 389 | Pico.video.type, pvid->lwrite_cnt, SekPc);\r |
| 390 | }\r |
| 391 | VideoWrite(d);\r |
| 392 | \r |
| 393 | if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[0x17]>>6)==2)\r |
| 394 | DmaFill(d);\r |
| 395 | \r |
| 396 | break;\r |
| 397 | \r |
| 398 | case 0x04: // Control (command) port 4 or 6\r |
| 399 | if (pvid->pending)\r |
| 400 | {\r |
| 401 | // Low word of command:\r |
| 402 | pvid->command &= 0xffff0000;\r |
| 403 | pvid->command |= d;\r |
| 404 | pvid->pending = 0;\r |
| 405 | CommandChange();\r |
| 406 | // Check for dma:\r |
| 407 | if (d & 0x80) {\r |
| 408 | DrawSync(0);\r |
| 409 | CommandDma();\r |
| 410 | }\r |
| 411 | }\r |
| 412 | else\r |
| 413 | {\r |
| 414 | if ((d&0xc000)==0x8000)\r |
| 415 | {\r |
| 416 | // Register write:\r |
| 417 | int num=(d>>8)&0x1f;\r |
| 418 | int dold=pvid->reg[num];\r |
| 419 | int blank_on = 0;\r |
| 420 | pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)\r |
| 421 | if (num > 0x0a && !(pvid->reg[1]&4)) {\r |
| 422 | elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);\r |
| 423 | return;\r |
| 424 | }\r |
| 425 | \r |
| 426 | if (num == 1 && !(d&0x40) && SekCyclesDone() - line_base_cycles <= 488-390)\r |
| 427 | blank_on = 1;\r |
| 428 | DrawSync(blank_on);\r |
| 429 | pvid->reg[num]=(unsigned char)d;\r |
| 430 | switch (num)\r |
| 431 | {\r |
| 432 | case 0x00:\r |
| 433 | elprintf(EL_INTSW, "hint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x10)>>4,\r |
| 434 | (d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);\r |
| 435 | goto update_irq;\r |
| 436 | case 0x01:\r |
| 437 | elprintf(EL_INTSW, "vint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x20)>>5,\r |
| 438 | (d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);\r |
| 439 | goto update_irq;\r |
| 440 | case 0x05:\r |
| 441 | //elprintf(EL_STATUS, "spritep moved to %04x", (unsigned)(Pico.video.reg[5]&0x7f) << 9);\r |
| 442 | if (d^dold) Pico.est.rendstatus |= PDRAW_SPRITES_MOVED;\r |
| 443 | break;\r |
| 444 | case 0x0c:\r |
| 445 | // renderers should update their palettes if sh/hi mode is changed\r |
| 446 | if ((d^dold)&8) Pico.m.dirtyPal = 2;\r |
| 447 | break;\r |
| 448 | }\r |
| 449 | return;\r |
| 450 | \r |
| 451 | update_irq:\r |
| 452 | #ifndef EMU_CORE_DEBUG\r |
| 453 | // update IRQ level\r |
| 454 | if (!SekShouldInterrupt()) // hack\r |
| 455 | {\r |
| 456 | int lines, pints, irq=0;\r |
| 457 | lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);\r |
| 458 | pints = (pvid->pending_ints&lines);\r |
| 459 | if (pints & 0x20) irq = 6;\r |
| 460 | else if (pints & 0x10) irq = 4;\r |
| 461 | SekInterrupt(irq); // update line\r |
| 462 | \r |
| 463 | if (irq) SekEndRun(24); // make it delayed\r |
| 464 | }\r |
| 465 | #endif\r |
| 466 | }\r |
| 467 | else\r |
| 468 | {\r |
| 469 | // High word of command:\r |
| 470 | pvid->command&=0x0000ffff;\r |
| 471 | pvid->command|=d<<16;\r |
| 472 | pvid->pending=1;\r |
| 473 | }\r |
| 474 | }\r |
| 475 | break;\r |
| 476 | \r |
| 477 | // case 0x08: // 08 0a - HV counter - lock up\r |
| 478 | // case 0x0c: // 0c 0e - HV counter - lock up\r |
| 479 | // case 0x10: // 10 12 - PSG - handled by caller\r |
| 480 | // case 0x14: // 14 16 - PSG - handled by caller\r |
| 481 | // case 0x18: // 18 1a - no effect?\r |
| 482 | case 0x1c: // 1c 1e - debug\r |
| 483 | pvid->debug = d;\r |
| 484 | pvid->debug_p = 0;\r |
| 485 | if (d & (1 << 6)) {\r |
| 486 | pvid->debug_p |= PVD_KILL_A | PVD_KILL_B;\r |
| 487 | pvid->debug_p |= PVD_KILL_S_LO | PVD_KILL_S_HI;\r |
| 488 | }\r |
| 489 | switch ((d >> 7) & 3) {\r |
| 490 | case 1:\r |
| 491 | pvid->debug_p &= ~(PVD_KILL_S_LO | PVD_KILL_S_HI);\r |
| 492 | pvid->debug_p |= PVD_FORCE_S;\r |
| 493 | break;\r |
| 494 | case 2:\r |
| 495 | pvid->debug_p &= ~PVD_KILL_A;\r |
| 496 | pvid->debug_p |= PVD_FORCE_A;\r |
| 497 | break;\r |
| 498 | case 3:\r |
| 499 | pvid->debug_p &= ~PVD_KILL_B;\r |
| 500 | pvid->debug_p |= PVD_FORCE_B;\r |
| 501 | break;\r |
| 502 | }\r |
| 503 | break;\r |
| 504 | }\r |
| 505 | }\r |
| 506 | \r |
| 507 | PICO_INTERNAL_ASM unsigned int PicoVideoRead(unsigned int a)\r |
| 508 | {\r |
| 509 | a&=0x1c;\r |
| 510 | \r |
| 511 | if (a==0x04) // control port\r |
| 512 | {\r |
| 513 | struct PicoVideo *pv=&Pico.video;\r |
| 514 | unsigned int d;\r |
| 515 | d=pv->status;\r |
| 516 | //if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)\r |
| 517 | if (SekCyclesDone() - line_base_cycles >= 488-88)\r |
| 518 | d|=0x0004; // H-Blank (Sonic3 vs)\r |
| 519 | \r |
| 520 | d |= ((pv->reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled\r |
| 521 | d |= (pv->pending_ints&0x20)<<2; // V-int pending?\r |
| 522 | if (d&0x100) pv->status&=~0x100; // FIFO no longer full\r |
| 523 | \r |
| 524 | pv->pending = 0; // ctrl port reads clear write-pending flag (Charles MacDonald)\r |
| 525 | \r |
| 526 | elprintf(EL_SR, "SR read: %04x @ %06x", d, SekPc);\r |
| 527 | return d;\r |
| 528 | }\r |
| 529 | \r |
| 530 | // H-counter info (based on Generator):\r |
| 531 | // frame:\r |
| 532 | // | <- hblank? -> |\r |
| 533 | // start <416> hint <36> hdisplay <38> end // CPU cycles\r |
| 534 | // |---------...---------|------------|-------------|\r |
| 535 | // 0 B6 E4 FF // 40 cells\r |
| 536 | // 0 93 E8 FF // 32 cells\r |
| 537 | \r |
| 538 | // Gens (?) v-render\r |
| 539 | // start <hblank=84> hint hdisplay <404> |\r |
| 540 | // |---------------------|--------------------------|\r |
| 541 | // E4 (hc[0x43]==0) 07 B1 // 40\r |
| 542 | // E8 (hc[0x45]==0) 05 91 // 32\r |
| 543 | \r |
| 544 | // check: Sonic 3D Blast bonus, Cannon Fodder, Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune\r |
| 545 | if ((a&0x1c)==0x08)\r |
| 546 | {\r |
| 547 | unsigned int d;\r |
| 548 | \r |
| 549 | d = (SekCyclesDone() - line_base_cycles) & 0x1ff; // FIXME\r |
| 550 | if (Pico.video.reg[12]&1)\r |
| 551 | d = hcounts_40[d];\r |
| 552 | else d = hcounts_32[d];\r |
| 553 | \r |
| 554 | elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, Pico.video.v_counter, SekCyclesDone(), SekPc);\r |
| 555 | return d | (Pico.video.v_counter << 8);\r |
| 556 | }\r |
| 557 | \r |
| 558 | if (a==0x00) // data port\r |
| 559 | {\r |
| 560 | return VideoRead();\r |
| 561 | }\r |
| 562 | \r |
| 563 | return 0;\r |
| 564 | }\r |
| 565 | \r |
| 566 | unsigned char PicoVideoRead8DataH(void)\r |
| 567 | {\r |
| 568 | return VideoRead() >> 8;\r |
| 569 | }\r |
| 570 | \r |
| 571 | unsigned char PicoVideoRead8DataL(void)\r |
| 572 | {\r |
| 573 | return VideoRead();\r |
| 574 | }\r |
| 575 | \r |
| 576 | // FIXME: broken mess\r |
| 577 | unsigned char PicoVideoRead8CtlH(void)\r |
| 578 | {\r |
| 579 | u8 d = (u8)(Pico.video.status >> 8);\r |
| 580 | if (d & 1)\r |
| 581 | Pico.video.status &= ~0x100; // FIFO no longer full\r |
| 582 | Pico.video.pending = 0;\r |
| 583 | elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);\r |
| 584 | return d;\r |
| 585 | }\r |
| 586 | \r |
| 587 | unsigned char PicoVideoRead8CtlL(void)\r |
| 588 | {\r |
| 589 | u8 d = (u8)Pico.video.status;\r |
| 590 | //if (PicoOpt&POPT_ALT_RENDERER) d|=0x0020; // sprite collision (Shadow of the Beast)\r |
| 591 | d |= ((Pico.video.reg[1]&0x40)^0x40) >> 3; // set V-Blank if display is disabled\r |
| 592 | d |= (Pico.video.pending_ints&0x20)<<2; // V-int pending?\r |
| 593 | if (SekCyclesDone() - line_base_cycles >= 488-88) d |= 4; // H-Blank\r |
| 594 | Pico.video.pending = 0;\r |
| 595 | elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);\r |
| 596 | return d;\r |
| 597 | }\r |
| 598 | \r |
| 599 | unsigned char PicoVideoRead8HV_H(void)\r |
| 600 | {\r |
| 601 | elprintf(EL_HVCNT, "vcounter: %02x [%u] @ %06x", Pico.video.v_counter, SekCyclesDone(), SekPc);\r |
| 602 | return Pico.video.v_counter;\r |
| 603 | }\r |
| 604 | \r |
| 605 | // FIXME: broken\r |
| 606 | unsigned char PicoVideoRead8HV_L(void)\r |
| 607 | {\r |
| 608 | u32 d = (SekCyclesDone() - line_base_cycles) & 0x1ff; // FIXME\r |
| 609 | if (Pico.video.reg[12]&1)\r |
| 610 | d = hcounts_40[d];\r |
| 611 | else d = hcounts_32[d];\r |
| 612 | elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r |
| 613 | return d;\r |
| 614 | }\r |
| 615 | \r |
| 616 | // vim:shiftwidth=2:ts=2:expandtab\r |