platform ps2, handle audio similar to psp
[picodrive.git] / pico / videoport.c
CommitLineData
cff531af 1/*\r
2 * PicoDrive\r
3 * (c) Copyright Dave, 2004\r
4 * (C) notaz, 2006-2009\r
7bf552b5 5 * (C) irixxxx, 2020-2024\r
cff531af 6 *\r
7 * This work is licensed under the terms of MAME license.\r
8 * See COPYING file in the top-level directory.\r
9 */\r
cc68a136 10\r
efcba75f 11#include "pico_int.h"\r
0c7d1ba3 12#define NEED_DMA_SOURCE\r
7feeb880 13#include "memory.h"\r
cc68a136 14\r
1886ac5f 15\r
16enum { clkdiv = 2 }; // CPU clock granularity: one of 1,2,4,8\r
17\r
18// VDP Slot timing, taken from http://gendev.spritesmind.net/\r
19// forum/viewtopic.php?f=22&t=851&sid=d5701a71396ee7f700c74fb7cd85cb09\r
f61d0a45 20// http://plutiedev.com/mirror/kabuto-hardware-notes\r
21// Thank you very much for the great work, Nemesis, Kabuto!\r
1886ac5f 22\r
df18e715 23// Slot clock is sysclock/20 for h32 and sysclock/16 for h40.\r
7263343d 24// One scanline is 63.7us/64.3us (ntsc/pal) long which is ~488.57 68k cycles.\r
25// Approximate by 488 for VDP.\r
6fd16ed6 26// 1 slot is 20/7 = 2.857 68k cycles in h32, and 16/7 = 2.286 in h40. That's\r
c1812e1a 27// 171 slots in h32, and ~214 (really 193 plus 17 prolonged in HSYNC) in h40.\r
df18e715 28enum { slcpu = 488 };\r
1886ac5f 29\r
30// VDP has a slot counter running from 0x00 to 0xff every scanline, but it has\r
31// a gap depending on the video mode. The slot in which a horizontal interrupt\r
32// is generated also depends on the video mode.\r
7263343d 33// NB Kabuto says gapend40 is 0xe4. That's technically correct, since slots 0xb6\r
34// and 0xe4 are only half slots. Ignore 0xe4 here and make 0xb6 a full slot.\r
df18e715 35enum { hint32 = 0x85, gapstart32 = 0x94, gapend32 = 0xe9};\r
36enum { hint40 = 0xa5, gapstart40 = 0xb7, gapend40 = 0xe5};\r
37\r
c1812e1a 38// Basic timing in h32: 38 slots (~108.5 cycles) from hint to VDP output start\r
39// at slot 0x00. vint takes place on the 1st VBLANK line in slot 0x01 (~111.5).\r
40// Rendering takes 128 slots (~365.5), and right border starts at slot 0x80\r
41// (~474 cycles). hint occurs after 5 slots into the border (~488.5 cycles).\r
42\r
df18e715 43// The horizontal sync period (HBLANK) is 30/37 slots (h32/h40):\r
44// h32: 4 slots front porch (1.49us), 13 HSYNC (4.84us), 13 back porch (4.84us)\r
45// h40: 5 slots front porch (1.49us), 16 HSYNC (4.77us), 16 back porch (4.77us)\r
7263343d 46// HBLANK starts at slot 0x93/0xb4 and ends in the middle of slot 0x05/0x06,\r
47// NB VDP slows down the h40 clock to h32 during HSYNC for 17 slots to get the\r
48// right sync timing. Ignored in the slot calculation, but hblen40 is correct.\r
df18e715 49enum { hboff32 = 0x93-hint32, hblen32 = 0xf8-(gapend32-gapstart32)-hint32};//30\r
7263343d 50enum { hboff40 = 0xb4-hint40, hblen40 = 0xf8-(gapend40-gapstart40)-hint40};//37\r
1886ac5f 51\r
52// number of slots in a scanline\r
53#define slots32 (0x100-(gapend32-gapstart32)) // 171\r
54#define slots40 (0x100-(gapend40-gapstart40)) // 210\r
55\r
56// In blanked display, all slots but the refresh slots are usable for transfers,\r
57// in active display only 16(h32) / 18(h40) slots can be used.\r
58\r
59// dma and refresh slots for active display, 16 for H32\r
60static u8 dmaslots32[] =\r
61 { 145,243, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 129,130 };\r
62static u8 refslots32[] =\r
63 { 250, 26, 58, 90, 122 };\r
64// dma and refresh slots for active display, 18 for H40\r
65static u8 dmaslots40[] =\r
66 { 232, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 130,138,146, 161,162 };\r
67static u8 refslots40[] =\r
68 { 250, 26, 58, 90, 122, 154 };\r
69\r
70// table sizes\r
df18e715 71enum { cycsz = slcpu/clkdiv };\r
1886ac5f 72enum { sl32blsz=slots32-sizeof(refslots32)+1, sl32acsz=sizeof(dmaslots32)+1 };\r
73enum { sl40blsz=slots40-sizeof(refslots40)+1, sl40acsz=sizeof(dmaslots40)+1 };\r
74\r
75// Tables must be considerably larger than one scanline, since 68k emulation\r
76// isn't stopping in the middle of an operation. If the last op is a 32 bit\r
df18e715 77// VDP access 2 slots may need to be taken from the next scanline, which can be\r
1886ac5f 78// more than 100 CPU cycles. For safety just cover 2 scanlines.\r
79\r
df18e715 80// table for hvcounter mapping. check: Sonic 3D Blast bonus, Cannon Fodder,\r
81// Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune\r
1886ac5f 82static u8 hcounts_32[2*cycsz], hcounts_40[2*cycsz];\r
83// tables mapping cycles to slots\r
84static u16 vdpcyc2sl_32_bl[2*cycsz],vdpcyc2sl_40_bl[2*cycsz];\r
85static u16 vdpcyc2sl_32_ac[2*cycsz],vdpcyc2sl_40_ac[2*cycsz];\r
86// tables mapping slots to cycles\r
87// NB the sl2cyc tables must cover all slots present in the cyc2sl tables.\r
88static u16 vdpsl2cyc_32_bl[2*sl32blsz],vdpsl2cyc_40_bl[2*sl40blsz];\r
89static u16 vdpsl2cyc_32_ac[2*sl32acsz],vdpsl2cyc_40_ac[2*sl40acsz];\r
90\r
91\r
92// calculate timing tables for one mode (H32 or H40)\r
93// NB tables aligned to HINT, since the main loop uses HINT as synchronization\r
94#define INITTABLES(s) { \\r
df18e715 95 float factor = (float)slcpu/slots##s; \\r
1886ac5f 96 int ax, bx, rx, ac, bc; \\r
97 int i, n; \\r
98 \\r
99 /* calculate internal VDP slot numbers */ \\r
100 for (i = 0; i < cycsz; i++) { \\r
101 n = hint##s + i*clkdiv/factor; \\r
102 if (n >= gapstart##s) n += gapend##s-gapstart##s; \\r
df18e715 103 hcounts_##s[i] = n % 256; \\r
1886ac5f 104 } \\r
1886ac5f 105 \\r
106 ax = bx = ac = bc = rx = 0; \\r
107 for (i = 0; i < cycsz; i++) { \\r
108 n = hcounts_##s[i]; \\r
109 if (i == 0 || n != hcounts_##s[i-1]) { \\r
110 /* fill slt <=> cycle tables, active scanline */ \\r
111 if (ax < ARRAY_SIZE(dmaslots##s) && dmaslots##s[ax] == n) { \\r
112 vdpsl2cyc_##s##_ac[++ax]=i; \\r
113 while (ac < i) vdpcyc2sl_##s##_ac[ac++] = ax-1; \\r
114 } \\r
115 /* fill slt <=> cycle tables, scanline off */ \\r
116 if (rx >= ARRAY_SIZE(refslots##s) || refslots##s[rx] != n) { \\r
117 vdpsl2cyc_##s##_bl[++bx]=i; \\r
118 while (bc < i) vdpcyc2sl_##s##_bl[bc++] = bx-1; \\r
119 } else \\r
120 ++rx; \\r
121 } \\r
122 } \\r
123 /* fill up cycle to slot mappings for last slot */ \\r
124 while (ac < cycsz) \\r
125 vdpcyc2sl_##s##_ac[ac] = ARRAY_SIZE(dmaslots##s), ac++; \\r
126 while (bc < cycsz) \\r
127 vdpcyc2sl_##s##_bl[bc] = slots##s-ARRAY_SIZE(refslots##s), bc++; \\r
df18e715 128 \\r
1886ac5f 129 /* extend tables for 2nd scanline */ \\r
df18e715 130 memcpy(hcounts_##s+cycsz, hcounts_##s, ARRAY_SIZE(hcounts_##s)-cycsz);\\r
1886ac5f 131 i = ARRAY_SIZE(dmaslots##s); \\r
132 while (ac < ARRAY_SIZE(vdpcyc2sl_##s##_ac)) \\r
133 vdpcyc2sl_##s##_ac[ac] = vdpcyc2sl_##s##_ac[ac-cycsz]+i, ac++; \\r
134 while (ax < ARRAY_SIZE(vdpsl2cyc_##s##_ac)-1) ax++, \\r
df18e715 135 vdpsl2cyc_##s##_ac[ax] = vdpsl2cyc_##s##_ac[ax-i]+cycsz; \\r
1886ac5f 136 i = slots##s - ARRAY_SIZE(refslots##s); \\r
137 while (bc < ARRAY_SIZE(vdpcyc2sl_##s##_bl)) \\r
138 vdpcyc2sl_##s##_bl[bc] = vdpcyc2sl_##s##_bl[bc-cycsz]+i, bc++; \\r
139 while (bx < ARRAY_SIZE(vdpsl2cyc_##s##_bl)-1) bx++, \\r
140 vdpsl2cyc_##s##_bl[bx] = vdpsl2cyc_##s##_bl[bx-i]+cycsz; \\r
141}\r
142 \r
143\r
144// initialize VDP timing tables\r
145void PicoVideoInit(void)\r
146{\r
147 INITTABLES(32);\r
148 INITTABLES(40);\r
149}\r
150\r
cc68a136 151\r
97232a47 152static int linedisabled; // display disabled on this line\r
153static int lineenabled; // display enabled on this line\r
154static int lineoffset; // offset at which dis/enable took place\r
bd73e6ee 155\r
97232a47 156u32 SATaddr, SATmask; // VRAM addr of sprite attribute table\r
cc68a136 157\r
4cc0fcaf 158int (*PicoDmaHook)(u32 source, int len, unsigned short **base, u32 *mask) = NULL;\r
cc68a136 159\r
17bd69ad 160\r
161/* VDP FIFO implementation\r
162 * \r
163 * fifo_slot: last slot executed in this scanline\r
17bd69ad 164 * fifo_total: #total FIFO entries pending\r
165 * fifo_data: last values transferred through fifo\r
987f0797 166 * fifo_queue: fifo transfer queue (#writes, flags)\r
17bd69ad 167 *\r
168 * FIFO states: empty total=0\r
169 * inuse total>0 && total<4\r
170 * full total==4\r
171 * wait total>4\r
172 * Conditions:\r
22a548f5 173 * fifo_slot is normally behind slot2cyc[cycles]. Advancing it beyond cycles\r
17bd69ad 174 * implies blocking the 68k up to that slot.\r
175 *\r
787a0af9 176 * A FIFO write goes to the end of the FIFO queue, but DMA running in background\r
177 * is always the last queue entry (transfers by CPU intervene and come 1st).\r
178 * There can be more pending writes than FIFO slots, but the CPU will be blocked\r
179 * until FIFO level (without background DMA) <= 4.\r
17bd69ad 180 * This is only about correct timing, data xfer must be handled by the caller.\r
181 * Blocking the CPU means burning cycles via SekCyclesBurn*(), which is to be\r
182 * executed by the caller.\r
183 *\r
184 * FIFOSync "executes" FIFO write slots up to the given cycle in the current\r
185 * scanline. A queue entry completely executed is removed from the queue.\r
186 * FIFOWrite pushes writes to the transfer queue. If it's a blocking write, 68k\r
187 * is blocked if more than 4 FIFO writes are pending.\r
188 * FIFORead executes a 68k read. 68k is blocked until the next transfer slot.\r
189 */\r
190\r
17bd69ad 191// NB code assumes fifo_* arrays have size 2^n\r
02138162 192static struct VdpFIFO { // XXX this must go into save file!\r
193 // last transferred FIFO data, ...x = index XXX currently only CPU\r
fda7339b 194 u16 fifo_data[4], fifo_dx;\r
daf29df9 195\r
02138162 196 // queued FIFO transfers, ...x = index, ...l = queue length\r
197 // each entry has 2 values: [n]>>3 = #writes, [n]&7 = flags (FQ_*)\r
fda7339b 198 u32 fifo_queue[8], fifo_qx, fifo_ql;\r
dc56ca2e 199 int fifo_total; // total# of pending FIFO entries (w/o BGDMA)\r
02138162 200\r
201 unsigned short fifo_slot; // last executed slot in current scanline\r
202 unsigned short fifo_maxslot;// #slots in scanline\r
17bd69ad 203\r
1886ac5f 204 const unsigned short *fifo_cyc2sl;\r
02138162 205 const unsigned short *fifo_sl2cyc;\r
0c9c8e47 206 const unsigned char *fifo_hcounts;\r
02138162 207} VdpFIFO;\r
17bd69ad 208\r
02138162 209enum { FQ_BYTE = 1, FQ_BGDMA = 2, FQ_FGDMA = 4 }; // queue flags, NB: BYTE = 1!\r
787a0af9 210\r
1886ac5f 211\r
212// NB should limit cyc2sl to table size in case 68k overdraws its aim. That can\r
213// happen if the last op is a blocking acess to VDP, or for exceptions (e.g.irq)\r
0c9c8e47 214#define Cyc2Sl(vf,lc) ((vf)->fifo_cyc2sl[(lc)/clkdiv])\r
215#define Sl2Cyc(vf,sl) ((vf)->fifo_sl2cyc[sl]*clkdiv)\r
dda72bea 216\r
987f0797 217// do the FIFO math\r
3b68e510 218static int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, int slots)\r
987f0797 219{\r
8eada9d6 220 u32 *qx = &vf->fifo_queue[vf->fifo_qx];\r
221 int l = slots, b = *qx & FQ_BYTE;\r
222 int cnt = *qx >> 3;\r
987f0797 223\r
787a0af9 224 // advance currently active FIFO entry\r
02138162 225 if (l > cnt)\r
226 l = cnt;\r
8eada9d6 227 if (!(*qx & FQ_BGDMA))\r
df18e715 228 vf->fifo_total -= ((cnt & b) + l) >> b;\r
8eada9d6 229 *qx -= l << 3;\r
987f0797 230\r
787a0af9 231 // if entry has been processed...\r
8eada9d6 232 if (cnt == l) {\r
c55a44a8 233 // remove entry from FIFO\r
8eada9d6 234 *qx = 0;\r
235 vf->fifo_qx = (vf->fifo_qx+1) & 7;\r
236 vf->fifo_ql --;\r
987f0797 237 }\r
02138162 238\r
987f0797 239 return l;\r
240}\r
241\r
8eada9d6 242static void SetFIFOState(struct VdpFIFO *vf, struct PicoVideo *pv)\r
987f0797 243{\r
fda7339b 244 u32 st = pv->status, cmd = pv->command;\r
787a0af9 245 // release CPU and terminate DMA if FIFO isn't blocking the 68k anymore\r
02138162 246 if (vf->fifo_total <= 4) {\r
247 st &= ~PVS_CPUWR;\r
248 if (!(st & (PVS_DMABG|PVS_DMAFILL))) {\r
249 st &= ~SR_DMA;\r
250 cmd &= ~0x80;\r
787a0af9 251 }\r
c55a44a8 252 }\r
8eada9d6 253 if (vf->fifo_ql == 0) {\r
4f6d3b28 254 st &= ~PVS_CPURD;\r
c55a44a8 255 // terminate DMA if applicable\r
8eada9d6 256 if (!(st & PVS_DMAFILL)) {\r
02138162 257 st &= ~(SR_DMA|PVS_DMABG);\r
258 cmd &= ~0x80;\r
c55a44a8 259 }\r
787a0af9 260 }\r
02138162 261 pv->status = st;\r
262 pv->command = cmd;\r
987f0797 263}\r
264\r
265// sync FIFO to cycles\r
266void PicoVideoFIFOSync(int cycles)\r
267{\r
02138162 268 struct VdpFIFO *vf = &VdpFIFO;\r
987f0797 269 struct PicoVideo *pv = &Pico.video;\r
17bd69ad 270 int slots, done;\r
271\r
272 // calculate #slots since last executed slot\r
dda72bea 273 slots = Cyc2Sl(vf, cycles) - vf->fifo_slot;\r
7263343d 274 if (slots <= 0 || !vf->fifo_ql) return;\r
17bd69ad 275\r
276 // advance FIFO queue by #done slots\r
277 done = slots;\r
8eada9d6 278 while (done > 0 && vf->fifo_ql) {\r
02138162 279 int l = AdvanceFIFOEntry(vf, pv, done);\r
280 vf->fifo_slot += l;\r
17bd69ad 281 done -= l;\r
17bd69ad 282 }\r
283\r
b6bdccb7 284 if (done != slots)\r
02138162 285 SetFIFOState(vf, pv);\r
17bd69ad 286}\r
287\r
288// drain FIFO, blocking 68k on the way. FIFO must be synced prior to drain.\r
02138162 289static int PicoVideoFIFODrain(int level, int cycles, int bgdma)\r
17bd69ad 290{\r
02138162 291 struct VdpFIFO *vf = &VdpFIFO;\r
17bd69ad 292 struct PicoVideo *pv = &Pico.video;\r
c55a44a8 293 unsigned ocyc = cycles;\r
fda7339b 294 int bd = vf->fifo_queue[vf->fifo_qx] & bgdma;\r
17bd69ad 295 int burn = 0;\r
296\r
8eada9d6 297 if (!(vf->fifo_ql && ((vf->fifo_total > level) | bd))) return 0;\r
298\r
787a0af9 299 // process FIFO entries until low level is reached\r
fda7339b 300 while (vf->fifo_slot < vf->fifo_maxslot &&\r
301 vf->fifo_ql && ((vf->fifo_total > level) | bd)) {\r
02138162 302 int b = vf->fifo_queue[vf->fifo_qx] & FQ_BYTE;\r
8eada9d6 303 int c = vf->fifo_queue[vf->fifo_qx] >> 3;\r
304 int cnt = bd ? c : ((vf->fifo_total-level)<<b) - (c&b);\r
305 int slot = (c < cnt ? c : cnt) + vf->fifo_slot;\r
02138162 306\r
307 if (slot > vf->fifo_maxslot) {\r
308 // target slot in later scanline, advance to eol\r
309 slot = vf->fifo_maxslot;\r
02138162 310 }\r
311 if (slot > vf->fifo_slot) {\r
fda7339b 312 // advance FIFO to target slot and CPU to cycles at that slot\r
313 vf->fifo_slot += AdvanceFIFOEntry(vf, pv, slot - vf->fifo_slot);\r
314 cycles = Sl2Cyc(vf, vf->fifo_slot);\r
315 bd = vf->fifo_queue[vf->fifo_qx] & bgdma;\r
17bd69ad 316 }\r
17bd69ad 317 }\r
fda7339b 318 if (vf->fifo_ql && ((vf->fifo_total > level) | bd))\r
7263343d 319 cycles = slcpu; // not completed in this scanline\r
02138162 320 if (cycles > ocyc)\r
321 burn = cycles - ocyc;\r
17bd69ad 322\r
02138162 323 SetFIFOState(vf, pv);\r
17bd69ad 324\r
325 return burn;\r
326}\r
327\r
328// read VDP data port\r
02138162 329static int PicoVideoFIFORead(void)\r
17bd69ad 330{\r
02138162 331 struct VdpFIFO *vf = &VdpFIFO;\r
17bd69ad 332 struct PicoVideo *pv = &Pico.video;\r
787a0af9 333 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
17bd69ad 334 int burn = 0;\r
335\r
8eada9d6 336 if (vf->fifo_ql) {\r
c55a44a8 337 // advance FIFO and CPU until FIFO is empty\r
02138162 338 burn = PicoVideoFIFODrain(0, lc, FQ_BGDMA);\r
c55a44a8 339 lc += burn;\r
340 }\r
17bd69ad 341\r
8eada9d6 342 if (vf->fifo_ql)\r
17bd69ad 343 pv->status |= PVS_CPURD; // target slot is in later scanline\r
344 else {\r
345 // use next VDP access slot for reading, block 68k until then\r
dda72bea 346 vf->fifo_slot = Cyc2Sl(vf, lc) + 1;\r
347 burn += Sl2Cyc(vf, vf->fifo_slot) - lc;\r
17bd69ad 348 }\r
349\r
350 return burn;\r
351}\r
352 \r
353// write VDP data port\r
987f0797 354int PicoVideoFIFOWrite(int count, int flags, unsigned sr_mask,unsigned sr_flags)\r
17bd69ad 355{\r
02138162 356 struct VdpFIFO *vf = &VdpFIFO;\r
17bd69ad 357 struct PicoVideo *pv = &Pico.video;\r
787a0af9 358 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
3b68e510 359 int burn = 0, x;\r
17bd69ad 360\r
3b68e510 361 // sync only needed if queue is too full or background dma might be deferred\r
f591b837 362 if ((vf->fifo_ql >= 6) | (pv->status & PVS_DMABG))\r
c55a44a8 363 PicoVideoFIFOSync(lc);\r
17bd69ad 364\r
f591b837 365 // determine last ent, ignoring bg dma (pushed back below if new ent created)\r
366 x = (vf->fifo_qx + vf->fifo_ql - 1 - !!(pv->status & PVS_DMABG)) & 7;\r
4f6d3b28 367\r
f591b837 368 pv->status = (pv->status & ~sr_mask) | sr_flags;\r
369 vf->fifo_total += count * !(flags & FQ_BGDMA);\r
4f6d3b28 370 if (!vf->fifo_ql)\r
371 vf->fifo_slot = Cyc2Sl(vf, lc+7); // FIFO latency ~3 vdp slots\r
372\r
f591b837 373 // determine queue position for entry\r
4f6d3b28 374 count <<= (flags & FQ_BYTE)+3;\r
375 if (vf->fifo_queue[x] && (vf->fifo_queue[x] & 7) == flags) {\r
376 // amalgamate entries if of same type and not empty (in case of bgdma)\r
377 vf->fifo_queue[x] += count;\r
378 } else {\r
379 // create new xfer queue entry\r
380 vf->fifo_ql ++;\r
381 x = (x+1) & 7;\r
382 vf->fifo_queue[(x+1)&7] = vf->fifo_queue[x]; // push back bg dma if exists\r
383 vf->fifo_queue[x] = count | flags;\r
17bd69ad 384 }\r
385\r
386 // if CPU is waiting for the bus, advance CPU and FIFO until bus is free\r
3b68e510 387 // do this only if it would exhaust the available slots since last sync\r
388 x = (Cyc2Sl(vf,lc) - vf->fifo_slot) / 2; // lower bound of FIFO ents \r
4f6d3b28 389 if ((pv->status & PVS_CPUWR) && vf->fifo_total > 4 + x)\r
787a0af9 390 burn = PicoVideoFIFODrain(4, lc, 0);\r
17bd69ad 391\r
392 return burn;\r
393}\r
394\r
395// at HINT, advance FIFO to new scanline\r
396int PicoVideoFIFOHint(void)\r
397{\r
02138162 398 struct VdpFIFO *vf = &VdpFIFO;\r
17bd69ad 399 struct PicoVideo *pv = &Pico.video;\r
8eada9d6 400 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
17bd69ad 401 int burn = 0;\r
402\r
403 // reset slot to start of scanline\r
02138162 404 vf->fifo_slot = 0;\r
0c9c8e47 405 // only need to refresh sprite position if we are synced\r
b38c0ea6 406 if (Pico.est.DrawScanline == Pico.m.scanline && !(pv->status & SR_VB))\r
ae9a76c9 407 PicoDrawRefreshSprites();\r
17bd69ad 408 \r
409 // if CPU is waiting for the bus, advance CPU and FIFO until bus is free\r
3b68e510 410 if (pv->status & PVS_CPUWR)\r
8eada9d6 411 burn = PicoVideoFIFODrain(4, lc, 0);\r
3b68e510 412 else if (pv->status & PVS_CPURD)\r
86198e03 413 burn = PicoVideoFIFORead();\r
17bd69ad 414\r
415 return burn;\r
416}\r
417\r
418// switch FIFO mode between active/inactive display\r
c55a44a8 419void PicoVideoFIFOMode(int active, int h40)\r
17bd69ad 420{\r
1886ac5f 421 static const unsigned short *vdpcyc2sl[2][2] =\r
422 { {vdpcyc2sl_32_bl, vdpcyc2sl_40_bl},{vdpcyc2sl_32_ac, vdpcyc2sl_40_ac} };\r
c55a44a8 423 static const unsigned short *vdpsl2cyc[2][2] =\r
1886ac5f 424 { {vdpsl2cyc_32_bl, vdpsl2cyc_40_bl},{vdpsl2cyc_32_ac, vdpsl2cyc_40_ac} };\r
0c9c8e47 425 static const unsigned char *vdphcounts[2] =\r
426 { hcounts_32, hcounts_40 };\r
c55a44a8 427\r
02138162 428 struct VdpFIFO *vf = &VdpFIFO;\r
17bd69ad 429 struct PicoVideo *pv = &Pico.video;\r
17bd69ad 430 int lc = SekCyclesDone() - Pico.t.m68c_line_start;\r
c55a44a8 431 active = active && !(pv->status & PVS_VB2);\r
17bd69ad 432\r
02138162 433 if (vf->fifo_maxslot)\r
c55a44a8 434 PicoVideoFIFOSync(lc);\r
c5ecd7a0 435 else\r
436 lc = 0;\r
17bd69ad 437\r
02138162 438 vf->fifo_cyc2sl = vdpcyc2sl[active][h40];\r
439 vf->fifo_sl2cyc = vdpsl2cyc[active][h40];\r
0c9c8e47 440 vf->fifo_hcounts = vdphcounts[h40];\r
c55a44a8 441 // recalculate FIFO slot for new mode\r
1886ac5f 442 vf->fifo_slot = Cyc2Sl(vf, lc);\r
7263343d 443 vf->fifo_maxslot = Cyc2Sl(vf, slcpu);\r
17bd69ad 444}\r
445\r
17bd69ad 446// VDP memory rd/wr\r
447\r
69996cb7 448static __inline void AutoIncrement(void)\r
cc68a136 449{\r
f1dbe764 450 struct PicoVideo *pvid = &Pico.video;\r
451 pvid->addr=(unsigned short)(pvid->addr+pvid->reg[0xf]);\r
452 if (pvid->addr < pvid->reg[0xf]) pvid->addr_u ^= 1;\r
cc68a136 453}\r
454\r
25be5c52 455static NOINLINE void VideoWriteVRAM128(u32 a, u16 d)\r
b71cbbf7 456{\r
457 // nasty\r
25be5c52 458 u32 b = ((a & 2) >> 1) | ((a & 0x400) >> 9) | (a & 0x3FC) | ((a & 0x1F800) >> 1);\r
459\r
460 ((u8 *)PicoMem.vram)[b] = d;\r
15eed405 461 if (!(u16)((b^SATaddr) & SATmask))\r
25be5c52 462 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
463\r
3618d636 464 if (((a^SATaddr) & SATmask) == 0)\r
25be5c52 465 UpdateSAT(a, d);\r
b71cbbf7 466}\r
467\r
cc68a136 468static void VideoWrite(u16 d)\r
469{\r
f1dbe764 470 struct PicoVideo *pvid = &Pico.video;\r
471 unsigned int a = pvid->addr;\r
cc68a136 472\r
f1dbe764 473 switch (pvid->type)\r
cc68a136 474 {\r
88fd63ad 475 case 1: if (a & 1)\r
476 d = (u16)((d << 8) | (d >> 8));\r
f1dbe764 477 a |= pvid->addr_u << 16;\r
25be5c52 478 VideoWriteVRAM(a, d);\r
947fb5f9 479 break;\r
f1dbe764 480 case 3: if (PicoMem.cram [(a >> 1) & 0x3f] != (d & 0xeee)) Pico.m.dirtyPal = 1;\r
25be5c52 481 PicoMem.cram [(a >> 1) & 0x3f] = d & 0xeee; break;\r
482 case 5: PicoMem.vsram[(a >> 1) & 0x3f] = d & 0x7ff; break;\r
483 case 0x81:\r
f1dbe764 484 a |= pvid->addr_u << 16;\r
25be5c52 485 VideoWriteVRAM128(a, d);\r
e1e7d1ed 486 break;\r
f1dbe764 487 //default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, pvid->type); break;\r
cc68a136 488 }\r
489\r
cc68a136 490 AutoIncrement();\r
491}\r
492\r
1613ec6c 493static unsigned int VideoRead(int is_from_z80)\r
cc68a136 494{\r
f1dbe764 495 struct PicoVideo *pvid = &Pico.video;\r
02138162 496 unsigned int a, d = VdpFIFO.fifo_data[(VdpFIFO.fifo_dx+1)&3];\r
cc68a136 497\r
f1dbe764 498 a=pvid->addr; a>>=1;\r
cc68a136 499\r
1613ec6c 500 if (!is_from_z80)\r
501 SekCyclesBurnRun(PicoVideoFIFORead());\r
f1dbe764 502 switch (pvid->type)\r
cc68a136 503 {\r
88fd63ad 504 case 0: d=PicoMem.vram [a & 0x7fff]; break;\r
25be5c52 505 case 8: d=PicoMem.cram [a & 0x003f] | (d & ~0x0eee); break;\r
17bd69ad 506 case 4: if ((a & 0x3f) >= 0x28) a = 0;\r
25be5c52 507 d=PicoMem.vsram [a & 0x003f] | (d & ~0x07ff); break;\r
f1dbe764 508 case 12:a=PicoMem.vram [a & 0x7fff]; if (pvid->addr&1) a >>= 8;\r
17bd69ad 509 d=(a & 0x00ff) | (d & ~0x00ff); break;\r
f1dbe764 510 default:elprintf(EL_ANOMALY, "VDP read with bad type %i", pvid->type); break;\r
cc68a136 511 }\r
4f672280 512\r
cc68a136 513 AutoIncrement();\r
514 return d;\r
515}\r
516\r
17bd69ad 517// VDP DMA\r
518\r
69996cb7 519static int GetDmaLength(void)\r
cc68a136 520{\r
521 struct PicoVideo *pvid=&Pico.video;\r
522 int len=0;\r
523 // 16-bit words to transfer:\r
524 len =pvid->reg[0x13];\r
525 len|=pvid->reg[0x14]<<8;\r
0c7d1ba3 526 len = ((len - 1) & 0xffff) + 1;\r
cc68a136 527 return len;\r
528}\r
529\r
15eed405 530static void DmaSlow(int len, u32 source)\r
cc68a136 531{\r
f1dbe764 532 struct PicoVideo *pvid=&Pico.video;\r
533 u32 inc = pvid->reg[0xf];\r
534 u32 a = pvid->addr | (pvid->addr_u << 16), e;\r
0c7d1ba3 535 u16 *r, *base = NULL;\r
536 u32 mask = 0x1ffff;\r
724db457 537 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
cc68a136 538\r
0c7d1ba3 539 elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%u] @ %06x",\r
f1dbe764 540 pvid->type, source, a, len, inc, (pvid->status&SR_VB)||!(pvid->reg[1]&0x40),\r
69996cb7 541 SekCyclesDone(), SekPc);\r
cc68a136 542\r
f1dbe764 543 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_FGDMA | (pvid->type == 1),\r
c55a44a8 544 PVS_DMABG, SR_DMA | PVS_CPUWR));\r
444c592a 545 // short transfers might have been completely conveyed to FIFO, adjust state\r
546 if ((pvid->status & SR_DMA) && VdpFIFO.fifo_total <= 4)\r
547 SetFIFOState(&VdpFIFO, pvid);\r
672ad671 548\r
0c7d1ba3 549 if ((source & 0xe00000) == 0xe00000) { // Ram\r
88fd63ad 550 base = (u16 *)PicoMem.ram;\r
0c7d1ba3 551 mask = 0xffff;\r
602133e1 552 }\r
93f9619e 553 else if (PicoIn.AHW & PAHW_MCD)\r
602133e1 554 {\r
0c7d1ba3 555 u8 r3 = Pico_mcd->s68k_regs[3];\r
556 elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", r3);\r
02ff0254 557 if (source < Pico.romsize /*0x20000*/) { // Bios area\r
558 base = (u16 *)(Pico.rom + (source & 0xfe0000));\r
559 } else if ((source & 0xfc0000) == pcd_base_address+0x200000) { // Word Ram\r
0c7d1ba3 560 if (!(r3 & 4)) { // 2M mode\r
561 base = (u16 *)(Pico_mcd->word_ram2M + (source & 0x20000));\r
ab0607f7 562 } else {\r
02ff0254 563 if ((source & 0xfe0000) < pcd_base_address+0x220000) { // 1M mode\r
0c7d1ba3 564 int bank = r3 & 1;\r
565 base = (u16 *)(Pico_mcd->word_ram1M[bank]);\r
69996cb7 566 } else {\r
0c7d1ba3 567 DmaSlowCell(source - 2, a, len, inc);\r
fa1e5e29 568 return;\r
69996cb7 569 }\r
ab0607f7 570 }\r
0c7d1ba3 571 source -= 2;\r
02ff0254 572 } else if ((source & 0xfe0000) == pcd_base_address+0x020000) { // Prg Ram\r
0c7d1ba3 573 base = (u16 *)Pico_mcd->prg_ram_b[r3 >> 6];\r
574 source -= 2; // XXX: test\r
ab0607f7 575 }\r
602133e1 576 }\r
577 else\r
578 {\r
50483b53 579 // if we have DmaHook, let it handle ROM because of possible DMA delay\r
0c7d1ba3 580 u32 source2;\r
581 if (PicoDmaHook && (source2 = PicoDmaHook(source, len, &base, &mask)))\r
582 source = source2;\r
583 else // Rom\r
584 base = m68k_dma_source(source);\r
ab0607f7 585 }\r
0c7d1ba3 586 if (!base) {\r
f1dbe764 587 elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", pvid->type, source, a);\r
0c7d1ba3 588 return;\r
672ad671 589 }\r
cc68a136 590\r
0c7d1ba3 591 // operate in words\r
592 source >>= 1;\r
593 mask >>= 1;\r
594\r
f1dbe764 595 switch (pvid->type)\r
cc68a136 596 {\r
597 case 1: // vram\r
0c9c8e47 598 e = a + len*2-1;\r
88fd63ad 599 r = PicoMem.vram;\r
0c9c8e47 600 if (inc == 2 && !(a & 1) && !((a ^ e) >> 16) &&\r
601 ((a >= SATaddr + 0x280) | (e < SATaddr)) &&\r
602 !((source ^ (source + len-1)) & ~mask))\r
cc68a136 603 {\r
cea65903 604 // most used DMA mode\r
0c7d1ba3 605 memcpy((char *)r + a, base + (source & mask), len * 2);\r
606 a += len * 2;\r
86198e03 607 break;\r
cea65903 608 }\r
86198e03 609 for(; len; len--)\r
cea65903 610 {\r
86198e03 611 u16 d = base[source++ & mask];\r
612 if(a & 1) d=(d<<8)|(d>>8);\r
613 VideoWriteVRAM(a, d);\r
614 // AutoIncrement\r
615 a = (a+inc) & ~0x20000;\r
cc68a136 616 }\r
cc68a136 617 break;\r
4f672280 618\r
cc68a136 619 case 3: // cram\r
cc68a136 620 Pico.m.dirtyPal = 1;\r
88fd63ad 621 r = PicoMem.cram;\r
f1dbe764 622 if (inc == 0 && !(pvid->reg[1] & 0x40) &&\r
623 (pvid->reg[7] & 0x3f) == ((a/2) & 0x3f)) { // bg color DMA\r
724db457 624 PicoVideoSync(1);\r
625 int sl = VdpFIFO.fifo_hcounts[lc/clkdiv];\r
626 if (sl > VdpFIFO.fifo_hcounts[0]-5) // hint delay is 5 slots\r
627 sl = (s8)sl;\r
628 // TODO this is needed to cover timing inaccuracies\r
a9d1e995 629 if (sl <= 12) sl = -3;\r
630 else if (sl <= 40) sl = 30;\r
724db457 631 PicoDrawBgcDMA(base, source, mask, len, sl);\r
f1dbe764 632 // do last DMA cycle since it's all going to the same cram location\r
633 source = source+len-1;\r
634 len = 1;\r
724db457 635 }\r
0c7d1ba3 636 for (; len; len--)\r
cc68a136 637 {\r
25be5c52 638 r[(a / 2) & 0x3f] = base[source++ & mask] & 0xeee;\r
cc68a136 639 // AutoIncrement\r
25be5c52 640 a = (a+inc) & ~0x20000;\r
cc68a136 641 }\r
cc68a136 642 break;\r
643\r
0c7d1ba3 644 case 5: // vsram\r
88fd63ad 645 r = PicoMem.vsram;\r
0c7d1ba3 646 for (; len; len--)\r
cc68a136 647 {\r
25be5c52 648 r[(a / 2) & 0x3f] = base[source++ & mask] & 0x7ff;\r
cc68a136 649 // AutoIncrement\r
25be5c52 650 a = (a+inc) & ~0x20000;\r
cc68a136 651 }\r
cc68a136 652 break;\r
69996cb7 653\r
b71cbbf7 654 case 0x81: // vram 128k\r
b71cbbf7 655 for(; len; len--)\r
656 {\r
25be5c52 657 u16 d = base[source++ & mask];\r
658 VideoWriteVRAM128(a, d);\r
b71cbbf7 659 // AutoIncrement\r
25be5c52 660 a = (a+inc) & ~0x20000;\r
b71cbbf7 661 }\r
b71cbbf7 662 break;\r
663\r
69996cb7 664 default:\r
f1dbe764 665 if (pvid->type != 0 || (EL_LOGMASK & EL_VDPDMA))\r
666 elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", pvid->type);\r
69996cb7 667 break;\r
cc68a136 668 }\r
669 // remember addr\r
f1dbe764 670 pvid->addr = a;\r
671 pvid->addr_u = a >> 16;\r
cc68a136 672}\r
673\r
674static void DmaCopy(int len)\r
675{\r
f1dbe764 676 struct PicoVideo *pvid=&Pico.video;\r
677 u32 a = pvid->addr | (pvid->addr_u << 16);\r
88fd63ad 678 u8 *vr = (u8 *)PicoMem.vram;\r
f1dbe764 679 u8 inc = pvid->reg[0xf];\r
cc68a136 680 int source;\r
ebd70cb5 681 elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());\r
4f672280 682\r
8eada9d6 683 // XXX implement VRAM 128k? Is this even working? xfer/count still in bytes?\r
684 SekCyclesBurnRun(PicoVideoFIFOWrite(2*len, FQ_BGDMA, // 2 slots each (rd+wr)\r
c55a44a8 685 PVS_CPUWR, SR_DMA | PVS_DMABG));\r
cc68a136 686\r
f1dbe764 687 source =pvid->reg[0x15];\r
688 source|=pvid->reg[0x16]<<8;\r
cc68a136 689\r
2aa27095 690 for (; len; len--)\r
cc68a136 691 {\r
25be5c52 692 vr[(u16)a] = vr[(u16)(source++)];\r
3618d636 693 if (((a^SATaddr) & SATmask) == 0)\r
25be5c52 694 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
cc68a136 695 // AutoIncrement\r
25be5c52 696 a = (a+inc) & ~0x20000;\r
cc68a136 697 }\r
698 // remember addr\r
f1dbe764 699 pvid->addr = a;\r
700 pvid->addr_u = a >> 16;\r
cc68a136 701}\r
702\r
0c7d1ba3 703static NOINLINE void DmaFill(int data)\r
cc68a136 704{\r
f1dbe764 705 struct PicoVideo *pvid=&Pico.video;\r
706 u32 a = pvid->addr | (pvid->addr_u << 16), e;\r
88fd63ad 707 u8 *vr = (u8 *)PicoMem.vram;\r
708 u8 high = (u8)(data >> 8);\r
f1dbe764 709 u8 inc = pvid->reg[0xf];\r
0c7d1ba3 710 int source;\r
711 int len, l;\r
4f672280 712\r
0c7d1ba3 713 len = GetDmaLength();\r
ebd70cb5 714 elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());\r
4f672280 715\r
8eada9d6 716 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_BGDMA, // 1 slot each (wr)\r
c55a44a8 717 PVS_CPUWR | PVS_DMAFILL, SR_DMA | PVS_DMABG));\r
cc68a136 718\r
f1dbe764 719 switch (pvid->type)\r
0c7d1ba3 720 {\r
721 case 1: // vram\r
0c9c8e47 722 e = a + len-1;\r
723 if (inc == 1 && !((a ^ e) >> 16) &&\r
724 ((a >= SATaddr + 0x280) | (e < SATaddr)))\r
86198e03 725 {\r
726 // most used DMA mode\r
727 memset(vr + (u16)a, high, len);\r
728 a += len;\r
729 break;\r
730 }\r
0c7d1ba3 731 for (l = len; l; l--) {\r
732 // Write upper byte to adjacent address\r
733 // (here we are byteswapped, so address is already 'adjacent')\r
25be5c52 734 vr[(u16)a] = high;\r
3618d636 735 if (((a^SATaddr) & SATmask) == 0)\r
25be5c52 736 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
cc68a136 737\r
0c7d1ba3 738 // Increment address register\r
25be5c52 739 a = (a+inc) & ~0x20000;\r
0c7d1ba3 740 }\r
741 break;\r
742 case 3: // cram\r
17bd69ad 743 Pico.m.dirtyPal = 1;\r
25be5c52 744 data &= 0xeee;\r
17bd69ad 745 for (l = len; l; l--) {\r
746 PicoMem.cram[(a/2) & 0x3f] = data;\r
747\r
748 // Increment address register\r
25be5c52 749 a = (a+inc) & ~0x20000;\r
17bd69ad 750 }\r
751 break;\r
0c7d1ba3 752 case 5: { // vsram\r
25be5c52 753 data &= 0x7ff;\r
17bd69ad 754 for (l = len; l; l--) {\r
755 PicoMem.vsram[(a/2) & 0x3f] = data;\r
756\r
757 // Increment address register\r
25be5c52 758 a = (a+inc) & ~0x20000;\r
17bd69ad 759 }\r
760 break;\r
0c7d1ba3 761 }\r
17bd69ad 762 case 0x81: // vram 128k\r
e1e7d1ed 763 for (l = len; l; l--) {\r
25be5c52 764 VideoWriteVRAM128(a, data);\r
e1e7d1ed 765\r
766 // Increment address register\r
25be5c52 767 a = (a+inc) & ~0x20000;\r
e1e7d1ed 768 }\r
e1e7d1ed 769 break;\r
0c7d1ba3 770 default:\r
771 a += len * inc;\r
772 break;\r
cc68a136 773 }\r
0c7d1ba3 774\r
cc68a136 775 // remember addr\r
f1dbe764 776 pvid->addr = a;\r
777 pvid->addr_u = a >> 16;\r
0c7d1ba3 778 // register update\r
f1dbe764 779 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
780 source = pvid->reg[0x15];\r
781 source |= pvid->reg[0x16] << 8;\r
0c7d1ba3 782 source += len;\r
f1dbe764 783 pvid->reg[0x15] = source;\r
784 pvid->reg[0x16] = source >> 8;\r
cc68a136 785}\r
786\r
17bd69ad 787// VDP command handling\r
788\r
0c7d1ba3 789static NOINLINE void CommandDma(void)\r
cc68a136 790{\r
b72662e2 791 struct PicoVideo *pvid = &Pico.video;\r
0c7d1ba3 792 u32 len, method;\r
793 u32 source;\r
cc68a136 794\r
17bd69ad 795 PicoVideoFIFOSync(SekCyclesDone()-Pico.t.m68c_line_start);\r
796 if (pvid->status & SR_DMA) {\r
310d973b 797 elprintf(EL_VDPDMA, "Dma overlap, left=%d @ %06x",\r
02138162 798 VdpFIFO.fifo_total, SekPc);\r
8eada9d6 799 VdpFIFO.fifo_total = VdpFIFO.fifo_ql = 0;\r
4f6d3b28 800 pvid->status &= ~PVS_DMAFILL;\r
17bd69ad 801 }\r
eef77d7a 802\r
0c7d1ba3 803 len = GetDmaLength();\r
f1dbe764 804 source = pvid->reg[0x15];\r
805 source |= pvid->reg[0x16] << 8;\r
806 source |= pvid->reg[0x17] << 16;\r
cc68a136 807\r
808 method=pvid->reg[0x17]>>6;\r
0c7d1ba3 809 if (method < 2)\r
810 DmaSlow(len, source << 1); // 68000 to VDP\r
811 else if (method == 3)\r
812 DmaCopy(len); // VRAM Copy\r
17bd69ad 813 else {\r
3c6da92b 814 pvid->status |= SR_DMA|PVS_DMAFILL;\r
0c7d1ba3 815 return;\r
17bd69ad 816 }\r
0c7d1ba3 817 source += len;\r
f1dbe764 818 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
819 pvid->reg[0x15] = source;\r
820 pvid->reg[0x16] = source >> 8;\r
cc68a136 821}\r
822\r
86198e03 823static NOINLINE void CommandChange(struct PicoVideo *pvid)\r
cc68a136 824{\r
b71cbbf7 825 unsigned int cmd, addr;\r
cc68a136 826\r
b71cbbf7 827 cmd = pvid->command;\r
cc68a136 828\r
829 // Get type of transfer 0xc0000030 (v/c/vsram read/write)\r
b71cbbf7 830 pvid->type = (u8)(((cmd >> 2) & 0xc) | (cmd >> 30));\r
831 if (pvid->type == 1) // vram\r
832 pvid->type |= pvid->reg[1] & 0x80; // 128k\r
cc68a136 833\r
834 // Get address 0x3fff0003\r
b71cbbf7 835 addr = (cmd >> 16) & 0x3fff;\r
836 addr |= (cmd << 14) & 0xc000;\r
837 pvid->addr = (u16)addr;\r
838 pvid->addr_u = (u8)((cmd >> 2) & 1);\r
cc68a136 839}\r
840\r
17bd69ad 841// VDP interface\r
842 \r
d515a352 843static inline int InHblank(int offs)\r
844{\r
0c9c8e47 845 // check if in left border (14 pixels) or HBLANK (86 pixels), 116 68k cycles\r
846 return SekCyclesDone() - Pico.t.m68c_line_start <= offs;\r
d515a352 847}\r
848\r
f9ed9446 849void PicoVideoSync(int skip)\r
b6d7ac70 850{\r
97232a47 851 struct VdpFIFO *vf = &VdpFIFO;\r
43e14010 852 int lines = Pico.video.reg[1]&0x08 ? 240 : 224;\r
97232a47 853 int last = Pico.m.scanline - (skip > 0);\r
f9ed9446 854\r
855 if (!(PicoIn.opt & POPT_ALT_RENDERER) && !PicoIn.skipFrame) {\r
856 if (last >= lines)\r
857 last = lines-1;\r
858 else // in active display, need to sync next frame as well\r
859 Pico.est.rendstatus |= PDRAW_SYNC_NEXT;\r
17bd69ad 860\r
2aa27095 861 //elprintf(EL_ANOMALY, "sync");\r
97232a47 862 if (unlikely(linedisabled >= 0 && linedisabled <= last)) {\r
863 if (Pico.est.DrawScanline <= linedisabled) {\r
864 int sl = vf->fifo_hcounts[lineoffset/clkdiv];\r
865 PicoDrawSync(linedisabled, sl ? sl : 1, 0);\r
866 }\r
867 linedisabled = -1;\r
17bd69ad 868 }\r
97232a47 869 if (unlikely(lineenabled >= 0 && lineenabled <= last)) {\r
870 if (Pico.est.DrawScanline <= lineenabled) {\r
871 int sl = vf->fifo_hcounts[lineoffset/clkdiv];\r
872 PicoDrawSync(lineenabled, 0, sl ? sl : 1);\r
873 }\r
874 lineenabled = -1;\r
875 }\r
876 if (Pico.est.DrawScanline <= last)\r
877 PicoDrawSync(last, 0, 0);\r
b6d7ac70 878 }\r
f9ed9446 879 if (skip >= 0)\r
880 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;\r
b6d7ac70 881}\r
882\r
4cc0fcaf 883PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)\r
cc68a136 884{\r
885 struct PicoVideo *pvid=&Pico.video;\r
886\r
22814963 887 //elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x [%u] @ %06x",\r
888 // a, d, SekCyclesDone(), SekPc);\r
cc68a136 889\r
22814963 890 a &= 0x1c;\r
e0bcb7a9 891 switch (a)\r
4f672280 892 {\r
e0bcb7a9 893 case 0x00: // Data port 0 or 2\r
69996cb7 894 if (pvid->pending) {\r
86198e03 895 CommandChange(pvid);\r
69996cb7 896 pvid->pending=0;\r
897 }\r
cc68a136 898\r
0c9c8e47 899 // try avoiding the sync if the data doesn't change.\r
900 // Writes to the SAT in VRAM are special since they update the SAT cache.\r
901 if ((pvid->reg[1]&0x40) &&\r
902 !(pvid->type == 1 && !(pvid->addr&1) && ((pvid->addr^SATaddr)&SATmask) && PicoMem.vram[pvid->addr>>1] == d) &&\r
d515a352 903 !(pvid->type == 3 && PicoMem.cram[(pvid->addr>>1) & 0x3f] == (d & 0xeee)) &&\r
904 !(pvid->type == 5 && PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))\r
f9ed9446 905 // the vertical scroll value for this line must be read from VSRAM early,\r
906 // since the A/B tile row to be read depends on it. E.g. Skitchin, OD2\r
907 // in contrast, CRAM writes would have an immediate effect on the current\r
444c592a 908 // pixel, so sync can be closer to start of actual image data\r
da102297 909 PicoVideoSync(InHblank(pvid->type == 3 ? 103 : 30)); // cram in Toy Story\r
d515a352 910\r
17bd69ad 911 if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))\r
cc68a136 912 {\r
02138162 913 VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;\r
17bd69ad 914 SekCyclesBurnRun(PicoVideoFIFOWrite(1, pvid->type == 1, 0, PVS_CPUWR));\r
915\r
916 elprintf(EL_ASVDP, "VDP data write: [%04x] %04x [%u] {%i} @ %06x",\r
f1dbe764 917 pvid->addr, d, SekCyclesDone(), pvid->type, SekPc);\r
cc68a136 918 }\r
0c7d1ba3 919 VideoWrite(d);\r
920\r
17bd69ad 921 // start DMA fill on write. NB VSRAM and CRAM fills use wrong FIFO data.\r
787a0af9 922 if (pvid->status & PVS_DMAFILL)\r
02138162 923 DmaFill(VdpFIFO.fifo_data[(VdpFIFO.fifo_dx + !!(pvid->type&~0x81))&3]);\r
0c7d1ba3 924\r
e0bcb7a9 925 break;\r
cc68a136 926\r
e0bcb7a9 927 case 0x04: // Control (command) port 4 or 6\r
787a0af9 928 if (pvid->status & SR_DMA)\r
929 SekCyclesBurnRun(PicoVideoFIFORead()); // kludge, flush out running DMA\r
b6d7ac70 930 if (pvid->pending)\r
cc68a136 931 {\r
932 // Low word of command:\r
17bd69ad 933 if (!(pvid->reg[1]&0x10))\r
934 d = (d&~0x80)|(pvid->command&0x80);\r
0c7d1ba3 935 pvid->command &= 0xffff0000;\r
936 pvid->command |= d;\r
937 pvid->pending = 0;\r
86198e03 938 CommandChange(pvid);\r
0c7d1ba3 939 // Check for dma:\r
940 if (d & 0x80) {\r
f9ed9446 941 PicoVideoSync(InHblank(93));\r
0c7d1ba3 942 CommandDma();\r
943 }\r
602133e1 944 }\r
945 else\r
946 {\r
947 if ((d&0xc000)==0x8000)\r
cc68a136 948 {\r
949 // Register write:\r
950 int num=(d>>8)&0x1f;\r
0ffefdb8 951 int dold=pvid->reg[num];\r
602133e1 952 pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)\r
0ffefdb8 953 if (num > 0x0a && !(pvid->reg[1]&4)) {\r
954 elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);\r
602133e1 955 return;\r
b6d7ac70 956 }\r
602133e1 957\r
0c9c8e47 958 d &= 0xff;\r
97232a47 959\r
960 if (num == 1 && ((pvid->reg[1]^d)&0x40)) {\r
97232a47 961 // handle line blanking before line rendering. Only the last switch\r
6fd16ed6 962 // before the 1st sync for other reasons is honoured. Switching after\r
963 // active area is on next line\r
964 int skip = InHblank(470); // Deadly Moves\r
965 PicoVideoSync(skip);\r
966 lineenabled = (d&0x40) ? Pico.m.scanline + !skip: -1;\r
967 linedisabled = (d&0x40) ? -1 : Pico.m.scanline + !skip;\r
968 lineoffset = (skip ? SekCyclesDone() - Pico.t.m68c_line_start : 0);\r
f1dbe764 969 } else if (((1<<num) & 0x738ff) && pvid->reg[num] != d)\r
f9ed9446 970 // VDP regs 0-7,11-13,16-18 influence rendering, ignore all others\r
971 PicoVideoSync(InHblank(93)); // Toy Story\r
972 pvid->reg[num] = d;\r
0c9c8e47 973\r
602133e1 974 switch (num)\r
975 {\r
976 case 0x00:\r
7647f87b 977 if ((~dold&d)&2) {\r
978 unsigned c = SekCyclesDone() - Pico.t.m68c_line_start;\r
979 pvid->hv_latch = VdpFIFO.fifo_hcounts[c/clkdiv] | (pvid->v_counter << 8);\r
980 }\r
ebd70cb5 981 elprintf(EL_INTSW, "hint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x10)>>4,\r
602133e1 982 (d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);\r
147f8df1 983 goto update_irq;\r
602133e1 984 case 0x01:\r
7647f87b 985 if ((d^dold)&0x40)\r
986 PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);\r
0e4bde9b 987 if (!(pvid->status & PVS_VB2))\r
988 pvid->status &= ~SR_VB;\r
989 pvid->status |= ((d >> 3) ^ SR_VB) & SR_VB; // forced blanking\r
7647f87b 990 elprintf(EL_INTSW, "vint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x20)>>5,\r
991 (d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);\r
147f8df1 992 goto update_irq;\r
602133e1 993 case 0x05:\r
25be5c52 994 case 0x06:\r
0c9c8e47 995 if (d^dold) Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
602133e1 996 break;\r
997 case 0x0c:\r
998 // renderers should update their palettes if sh/hi mode is changed\r
b1a047c9 999 if ((d^dold)&8) Pico.m.dirtyPal = 1;\r
7647f87b 1000 if ((d^dold)&1) {\r
1001 PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);\r
1002 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
1003 }\r
602133e1 1004 break;\r
25be5c52 1005 default:\r
1006 return;\r
602133e1 1007 }\r
51d6248b 1008 if (Pico.est.rendstatus & PDRAW_DIRTY_SPRITES) {\r
1009 SATaddr = ((pvid->reg[5]&0x7f) << 9) | ((pvid->reg[6]&0x20) << 11);\r
1010 SATmask = ~0x1ff;\r
1011 if (pvid->reg[12]&1)\r
1012 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit\r
1013 //elprintf(EL_STATUS, "spritep moved to %04x", SATaddr);\r
1014 }\r
147f8df1 1015 return;\r
602133e1 1016\r
147f8df1 1017update_irq:\r
03e4f2a3 1018#ifndef EMU_CORE_DEBUG\r
0290987c 1019 // update IRQ level; TODO hack, still fire irq if disabling now\r
1020 if (!SekShouldInterrupt() || SekIrqLevel < pvid->hint_irq)\r
03e4f2a3 1021 {\r
22814963 1022 int lines, pints, irq = 0;\r
602133e1 1023 lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);\r
22814963 1024 pints = pvid->pending_ints & lines;\r
b6d7ac70 1025 if (pints & 0x20) irq = 6;\r
ba2b97dc 1026 else if (pints & 0x10) irq = pvid->hint_irq;\r
0290987c 1027\r
b8a38093 1028 if (irq) {\r
0290987c 1029 // VDP irqs have highest prio, just overwrite old level\r
ba2b97dc 1030 SekInterrupt(irq); // update line\r
602133e1 1031\r
0290987c 1032 // TODO this is broken because cost of current insn isn't known here\r
b8a38093 1033 SekEndRun(21); // make it delayed\r
1034 } else if (SekIrqLevel >= pvid->hint_irq) {\r
1035 // no VDP irq, query lower irqs\r
0290987c 1036 SekInterrupt(PicoIn.AHW & PAHW_PICO ? PicoPicoIrqAck(0) : 0);\r
b8a38093 1037 }\r
cc68a136 1038 }\r
cc68a136 1039#endif\r
602133e1 1040 }\r
1041 else\r
1042 {\r
cc68a136 1043 // High word of command:\r
1044 pvid->command&=0x0000ffff;\r
1045 pvid->command|=d<<16;\r
1046 pvid->pending=1;\r
1047 }\r
1048 }\r
e0bcb7a9 1049 break;\r
1050\r
1051 // case 0x08: // 08 0a - HV counter - lock up\r
1052 // case 0x0c: // 0c 0e - HV counter - lock up\r
1053 // case 0x10: // 10 12 - PSG - handled by caller\r
1054 // case 0x14: // 14 16 - PSG - handled by caller\r
1055 // case 0x18: // 18 1a - no effect?\r
1056 case 0x1c: // 1c 1e - debug\r
1057 pvid->debug = d;\r
1058 pvid->debug_p = 0;\r
1059 if (d & (1 << 6)) {\r
1060 pvid->debug_p |= PVD_KILL_A | PVD_KILL_B;\r
1061 pvid->debug_p |= PVD_KILL_S_LO | PVD_KILL_S_HI;\r
1062 }\r
1063 switch ((d >> 7) & 3) {\r
1064 case 1:\r
1065 pvid->debug_p &= ~(PVD_KILL_S_LO | PVD_KILL_S_HI);\r
1066 pvid->debug_p |= PVD_FORCE_S;\r
1067 break;\r
1068 case 2:\r
1069 pvid->debug_p &= ~PVD_KILL_A;\r
1070 pvid->debug_p |= PVD_FORCE_A;\r
1071 break;\r
1072 case 3:\r
1073 pvid->debug_p &= ~PVD_KILL_B;\r
1074 pvid->debug_p |= PVD_FORCE_B;\r
1075 break;\r
1076 }\r
1077 break;\r
cc68a136 1078 }\r
1079}\r
1080\r
17bd69ad 1081static u32 VideoSr(const struct PicoVideo *pv)\r
cc68a136 1082{\r
7263343d 1083 unsigned int hp = pv->reg[12]&1 ? hboff40*488.5/slots40 : hboff32*488.5/slots32;\r
1084 unsigned int hl = pv->reg[12]&1 ? hblen40*488.5/slots40 : hblen32*488.5/slots32;\r
1085 unsigned int c = SekCyclesDone() - Pico.t.m68c_line_start;\r
72c2a04a 1086 u32 d;\r
1087\r
1088 PicoVideoFIFOSync(c);\r
1089 d = (u16)pv->status;\r
cc68a136 1090\r
787a0af9 1091 if (c - hp < hl)\r
0e4bde9b 1092 d |= SR_HB;\r
17bd69ad 1093\r
02138162 1094 if (VdpFIFO.fifo_total >= 4)\r
17bd69ad 1095 d |= SR_FULL;\r
02138162 1096 else if (!VdpFIFO.fifo_total)\r
17bd69ad 1097 d |= SR_EMPT;\r
0e4bde9b 1098 return d;\r
1099}\r
cc68a136 1100\r
15eed405 1101PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)\r
0e4bde9b 1102{\r
f1dbe764 1103 struct PicoVideo *pv = &Pico.video;\r
0e4bde9b 1104 a &= 0x1c;\r
cc68a136 1105\r
0e4bde9b 1106 if (a == 0x04) // control port\r
1107 {\r
15eed405 1108 u32 d = VideoSr(pv);\r
17bd69ad 1109 if (pv->pending) {\r
86198e03 1110 CommandChange(pv);\r
17bd69ad 1111 pv->pending = 0;\r
1112 }\r
22814963 1113 elprintf(EL_SR, "SR read: %04x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
b6d7ac70 1114 return d;\r
cc68a136 1115 }\r
1116\r
9f29605f 1117 if (a == 0x08)\r
cc68a136 1118 {\r
15eed405 1119 unsigned int c;\r
1120 u32 d;\r
947fb5f9 1121\r
df18e715 1122 c = SekCyclesDone() - Pico.t.m68c_line_start;\r
f1dbe764 1123 if (pv->reg[0]&2)\r
1124 d = pv->hv_latch;\r
1125 else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8);\r
cc68a136 1126\r
f1dbe764 1127 elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc);\r
17bd69ad 1128 return d;\r
9761a7d0 1129 }\r
cc68a136 1130\r
9761a7d0 1131 if (a==0x00) // data port\r
1132 {\r
1613ec6c 1133 return VideoRead(0);\r
9761a7d0 1134 }\r
cc68a136 1135\r
9f29605f 1136 return PicoRead16_floating(a | 0xc00000);\r
9761a7d0 1137}\r
1138\r
1613ec6c 1139unsigned char PicoVideoRead8DataH(int is_from_z80)\r
9761a7d0 1140{\r
1613ec6c 1141 return VideoRead(is_from_z80) >> 8;\r
75b84e4b 1142}\r
9761a7d0 1143\r
1613ec6c 1144unsigned char PicoVideoRead8DataL(int is_from_z80)\r
75b84e4b 1145{\r
1613ec6c 1146 return VideoRead(is_from_z80);\r
75b84e4b 1147}\r
cc68a136 1148\r
1613ec6c 1149unsigned char PicoVideoRead8CtlH(int is_from_z80)\r
75b84e4b 1150{\r
86198e03 1151 struct PicoVideo *pv = &Pico.video;\r
1152 u8 d = VideoSr(pv) >> 8;\r
1153 if (pv->pending) {\r
1154 CommandChange(pv);\r
1155 pv->pending = 0;\r
17bd69ad 1156 }\r
75b84e4b 1157 elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);\r
1158 return d;\r
1159}\r
1160\r
1613ec6c 1161unsigned char PicoVideoRead8CtlL(int is_from_z80)\r
75b84e4b 1162{\r
86198e03 1163 struct PicoVideo *pv = &Pico.video;\r
1164 u8 d = VideoSr(pv);\r
1165 if (pv->pending) {\r
1166 CommandChange(pv);\r
1167 pv->pending = 0;\r
17bd69ad 1168 }\r
75b84e4b 1169 elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);\r
1170 return d;\r
1171}\r
1172\r
1613ec6c 1173unsigned char PicoVideoRead8HV_H(int is_from_z80)\r
75b84e4b 1174{\r
7647f87b 1175 u32 d = Pico.video.v_counter;\r
1176 if (Pico.video.reg[0]&2)\r
1177 d = Pico.video.hv_latch >> 8;\r
1178 elprintf(EL_HVCNT, "vcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
1179 return d;\r
75b84e4b 1180}\r
1181\r
1182// FIXME: broken\r
1613ec6c 1183unsigned char PicoVideoRead8HV_L(int is_from_z80)\r
75b84e4b 1184{\r
df18e715 1185 u32 d = SekCyclesDone() - Pico.t.m68c_line_start;\r
3c6da92b 1186 if (Pico.video.reg[0]&2)\r
1187 d = Pico.video.hv_latch;\r
0c9c8e47 1188 else d = VdpFIFO.fifo_hcounts[d/clkdiv];\r
ebd70cb5 1189 elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
75b84e4b 1190 return d;\r
cc68a136 1191}\r
e7b3ad1b 1192\r
51d6248b 1193void PicoVideoReset(void)\r
1194{\r
51d6248b 1195 Pico.video.pending_ints=0;\r
c7661b80 1196 Pico.video.reg[1] &= ~0x40; // TODO verify display disabled after reset\r
1197 Pico.video.reg[10] = 0xff; // HINT is turned off after reset\r
1198 Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal\r
51d6248b 1199\r
51d6248b 1200 memset(&VdpFIFO, 0, sizeof(VdpFIFO));\r
1201 Pico.m.dirtyPal = 1;\r
1202\r
51d6248b 1203 PicoDrawBgcDMA(NULL, 0, 0, 0, 0);\r
96e59821 1204 PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);\r
51d6248b 1205}\r
1206\r
4496577e 1207void PicoVideoCacheSAT(int load)\r
e721f801 1208{\r
1209 struct PicoVideo *pv = &Pico.video;\r
1210 int l;\r
1211\r
1212 SATaddr = ((pv->reg[5]&0x7f) << 9) | ((pv->reg[6]&0x20) << 11);\r
1213 SATmask = ~0x1ff;\r
1214 if (pv->reg[12]&1)\r
1215 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit\r
1216\r
1217 // rebuild SAT cache XXX wrong since cache and memory can differ\r
ac891449 1218 for (l = 0; load && l < 2*80; l ++) {\r
1219 u16 addr = SATaddr + l*4;\r
56e0b865 1220 ((u16 *)VdpSATCache)[l*2 ] = PicoMem.vram[(addr>>1) ];\r
1221 ((u16 *)VdpSATCache)[l*2 + 1] = PicoMem.vram[(addr>>1) + 1];\r
e721f801 1222 }\r
1223\r
0c9c8e47 1224 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
e721f801 1225}\r
1226\r
daf29df9 1227void PicoVideoSave(void)\r
1228{\r
02138162 1229 struct VdpFIFO *vf = &VdpFIFO;\r
daf29df9 1230 struct PicoVideo *pv = &Pico.video;\r
1231 int l, x;\r
1232\r
1233 // account for all outstanding xfers XXX kludge, entry attr's not saved\r
e45908d7 1234 pv->fifo_cnt = pv->fifo_bgcnt = 0;\r
8eada9d6 1235 for (l = vf->fifo_ql, x = vf->fifo_qx + l-1; l > 0; l--, x--) {\r
1236 int cnt = (vf->fifo_queue[x&7] >> 3);\r
e45908d7 1237 if (vf->fifo_queue[x&7] & FQ_BGDMA)\r
1238 pv->fifo_bgcnt += cnt;\r
1239 else\r
1240 pv->fifo_cnt += cnt;\r
1241 }\r
daf29df9 1242}\r
1243\r
1244void PicoVideoLoad(void)\r
1245{\r
02138162 1246 struct VdpFIFO *vf = &VdpFIFO;\r
daf29df9 1247 struct PicoVideo *pv = &Pico.video;\r
fda7339b 1248 int b = pv->type == 1;\r
daf29df9 1249\r
1250 // convert former dma_xfers (why was this in PicoMisc anyway?)\r
1251 if (Pico.m.dma_xfers) {\r
22a548f5 1252 pv->fifo_cnt = Pico.m.dma_xfers << b;\r
daf29df9 1253 Pico.m.dma_xfers = 0;\r
1254 }\r
e45908d7 1255\r
1256 // fake entries in the FIFO if there are outstanding transfers\r
8eada9d6 1257 vf->fifo_ql = vf->fifo_qx = vf->fifo_total = 0;\r
fda7339b 1258 if (pv->fifo_cnt) {\r
8eada9d6 1259 int wc = pv->fifo_cnt;\r
8eada9d6 1260 vf->fifo_total = (wc+b) >> b;\r
e45908d7 1261 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | b | FQ_FGDMA;\r
1262 vf->fifo_ql ++;\r
c5ecd7a0 1263 if (vf->fifo_total > 4 && !(pv->status & (PVS_CPUWR|PVS_CPURD)))\r
1264 pv->status |= PVS_CPUWR;\r
e45908d7 1265 }\r
1266 if (pv->fifo_bgcnt) {\r
1267 int wc = pv->fifo_bgcnt;\r
8eada9d6 1268 if (!vf->fifo_ql)\r
4f6d3b28 1269 pv->status |= PVS_DMABG;\r
e45908d7 1270 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | FQ_BGDMA;\r
1271 vf->fifo_ql ++;\r
fda7339b 1272 }\r
4496577e 1273 PicoVideoCacheSAT(1);\r
c5ecd7a0 1274 vf->fifo_maxslot = 0;\r
daf29df9 1275}\r
e7b3ad1b 1276// vim:shiftwidth=2:ts=2:expandtab\r