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