pandora: fix readme and pxml version
[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
d4b5888a 192static struct VdpFIFO {\r
02138162 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
549dd407 557 if ((source & 0xfc0000) == pcd_base_address) { // Bios area\r
558 base = (u16 *)(Pico_mcd->bios + (source & 0xfe0000));\r
02ff0254 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
549dd407 575 } else // Rom\r
576 base = m68k_dma_source(source);\r
602133e1 577 }\r
578 else\r
579 {\r
50483b53 580 // if we have DmaHook, let it handle ROM because of possible DMA delay\r
0c7d1ba3 581 u32 source2;\r
582 if (PicoDmaHook && (source2 = PicoDmaHook(source, len, &base, &mask)))\r
583 source = source2;\r
584 else // Rom\r
585 base = m68k_dma_source(source);\r
ab0607f7 586 }\r
0c7d1ba3 587 if (!base) {\r
f1dbe764 588 elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", pvid->type, source, a);\r
0c7d1ba3 589 return;\r
672ad671 590 }\r
cc68a136 591\r
0c7d1ba3 592 // operate in words\r
593 source >>= 1;\r
594 mask >>= 1;\r
595\r
f1dbe764 596 switch (pvid->type)\r
cc68a136 597 {\r
598 case 1: // vram\r
0c9c8e47 599 e = a + len*2-1;\r
88fd63ad 600 r = PicoMem.vram;\r
0c9c8e47 601 if (inc == 2 && !(a & 1) && !((a ^ e) >> 16) &&\r
602 ((a >= SATaddr + 0x280) | (e < SATaddr)) &&\r
603 !((source ^ (source + len-1)) & ~mask))\r
cc68a136 604 {\r
cea65903 605 // most used DMA mode\r
0c7d1ba3 606 memcpy((char *)r + a, base + (source & mask), len * 2);\r
607 a += len * 2;\r
86198e03 608 break;\r
cea65903 609 }\r
86198e03 610 for(; len; len--)\r
cea65903 611 {\r
86198e03 612 u16 d = base[source++ & mask];\r
613 if(a & 1) d=(d<<8)|(d>>8);\r
614 VideoWriteVRAM(a, d);\r
615 // AutoIncrement\r
616 a = (a+inc) & ~0x20000;\r
cc68a136 617 }\r
cc68a136 618 break;\r
4f672280 619\r
cc68a136 620 case 3: // cram\r
cc68a136 621 Pico.m.dirtyPal = 1;\r
88fd63ad 622 r = PicoMem.cram;\r
f1dbe764 623 if (inc == 0 && !(pvid->reg[1] & 0x40) &&\r
624 (pvid->reg[7] & 0x3f) == ((a/2) & 0x3f)) { // bg color DMA\r
724db457 625 PicoVideoSync(1);\r
626 int sl = VdpFIFO.fifo_hcounts[lc/clkdiv];\r
627 if (sl > VdpFIFO.fifo_hcounts[0]-5) // hint delay is 5 slots\r
628 sl = (s8)sl;\r
629 // TODO this is needed to cover timing inaccuracies\r
a9d1e995 630 if (sl <= 12) sl = -3;\r
631 else if (sl <= 40) sl = 30;\r
724db457 632 PicoDrawBgcDMA(base, source, mask, len, sl);\r
f1dbe764 633 // do last DMA cycle since it's all going to the same cram location\r
634 source = source+len-1;\r
635 len = 1;\r
724db457 636 }\r
0c7d1ba3 637 for (; len; len--)\r
cc68a136 638 {\r
25be5c52 639 r[(a / 2) & 0x3f] = base[source++ & mask] & 0xeee;\r
cc68a136 640 // AutoIncrement\r
25be5c52 641 a = (a+inc) & ~0x20000;\r
cc68a136 642 }\r
cc68a136 643 break;\r
644\r
0c7d1ba3 645 case 5: // vsram\r
88fd63ad 646 r = PicoMem.vsram;\r
0c7d1ba3 647 for (; len; len--)\r
cc68a136 648 {\r
25be5c52 649 r[(a / 2) & 0x3f] = base[source++ & mask] & 0x7ff;\r
cc68a136 650 // AutoIncrement\r
25be5c52 651 a = (a+inc) & ~0x20000;\r
cc68a136 652 }\r
cc68a136 653 break;\r
69996cb7 654\r
b71cbbf7 655 case 0x81: // vram 128k\r
b71cbbf7 656 for(; len; len--)\r
657 {\r
25be5c52 658 u16 d = base[source++ & mask];\r
659 VideoWriteVRAM128(a, d);\r
b71cbbf7 660 // AutoIncrement\r
25be5c52 661 a = (a+inc) & ~0x20000;\r
b71cbbf7 662 }\r
b71cbbf7 663 break;\r
664\r
69996cb7 665 default:\r
f1dbe764 666 if (pvid->type != 0 || (EL_LOGMASK & EL_VDPDMA))\r
667 elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", pvid->type);\r
69996cb7 668 break;\r
cc68a136 669 }\r
670 // remember addr\r
f1dbe764 671 pvid->addr = a;\r
672 pvid->addr_u = a >> 16;\r
cc68a136 673}\r
674\r
675static void DmaCopy(int len)\r
676{\r
f1dbe764 677 struct PicoVideo *pvid=&Pico.video;\r
678 u32 a = pvid->addr | (pvid->addr_u << 16);\r
88fd63ad 679 u8 *vr = (u8 *)PicoMem.vram;\r
f1dbe764 680 u8 inc = pvid->reg[0xf];\r
cc68a136 681 int source;\r
ebd70cb5 682 elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());\r
4f672280 683\r
8eada9d6 684 // XXX implement VRAM 128k? Is this even working? xfer/count still in bytes?\r
685 SekCyclesBurnRun(PicoVideoFIFOWrite(2*len, FQ_BGDMA, // 2 slots each (rd+wr)\r
c55a44a8 686 PVS_CPUWR, SR_DMA | PVS_DMABG));\r
cc68a136 687\r
f1dbe764 688 source =pvid->reg[0x15];\r
689 source|=pvid->reg[0x16]<<8;\r
cc68a136 690\r
2aa27095 691 for (; len; len--)\r
cc68a136 692 {\r
25be5c52 693 vr[(u16)a] = vr[(u16)(source++)];\r
3618d636 694 if (((a^SATaddr) & SATmask) == 0)\r
25be5c52 695 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
cc68a136 696 // AutoIncrement\r
25be5c52 697 a = (a+inc) & ~0x20000;\r
cc68a136 698 }\r
699 // remember addr\r
f1dbe764 700 pvid->addr = a;\r
701 pvid->addr_u = a >> 16;\r
cc68a136 702}\r
703\r
0c7d1ba3 704static NOINLINE void DmaFill(int data)\r
cc68a136 705{\r
f1dbe764 706 struct PicoVideo *pvid=&Pico.video;\r
707 u32 a = pvid->addr | (pvid->addr_u << 16), e;\r
88fd63ad 708 u8 *vr = (u8 *)PicoMem.vram;\r
709 u8 high = (u8)(data >> 8);\r
f1dbe764 710 u8 inc = pvid->reg[0xf];\r
0c7d1ba3 711 int source;\r
712 int len, l;\r
4f672280 713\r
0c7d1ba3 714 len = GetDmaLength();\r
ebd70cb5 715 elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());\r
4f672280 716\r
8eada9d6 717 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_BGDMA, // 1 slot each (wr)\r
c55a44a8 718 PVS_CPUWR | PVS_DMAFILL, SR_DMA | PVS_DMABG));\r
cc68a136 719\r
f1dbe764 720 switch (pvid->type)\r
0c7d1ba3 721 {\r
722 case 1: // vram\r
0c9c8e47 723 e = a + len-1;\r
724 if (inc == 1 && !((a ^ e) >> 16) &&\r
725 ((a >= SATaddr + 0x280) | (e < SATaddr)))\r
86198e03 726 {\r
727 // most used DMA mode\r
728 memset(vr + (u16)a, high, len);\r
729 a += len;\r
730 break;\r
731 }\r
0c7d1ba3 732 for (l = len; l; l--) {\r
733 // Write upper byte to adjacent address\r
734 // (here we are byteswapped, so address is already 'adjacent')\r
25be5c52 735 vr[(u16)a] = high;\r
3618d636 736 if (((a^SATaddr) & SATmask) == 0)\r
25be5c52 737 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
cc68a136 738\r
0c7d1ba3 739 // Increment address register\r
25be5c52 740 a = (a+inc) & ~0x20000;\r
0c7d1ba3 741 }\r
742 break;\r
743 case 3: // cram\r
17bd69ad 744 Pico.m.dirtyPal = 1;\r
25be5c52 745 data &= 0xeee;\r
17bd69ad 746 for (l = len; l; l--) {\r
747 PicoMem.cram[(a/2) & 0x3f] = data;\r
748\r
749 // Increment address register\r
25be5c52 750 a = (a+inc) & ~0x20000;\r
17bd69ad 751 }\r
752 break;\r
0c7d1ba3 753 case 5: { // vsram\r
25be5c52 754 data &= 0x7ff;\r
17bd69ad 755 for (l = len; l; l--) {\r
756 PicoMem.vsram[(a/2) & 0x3f] = data;\r
757\r
758 // Increment address register\r
25be5c52 759 a = (a+inc) & ~0x20000;\r
17bd69ad 760 }\r
761 break;\r
0c7d1ba3 762 }\r
17bd69ad 763 case 0x81: // vram 128k\r
e1e7d1ed 764 for (l = len; l; l--) {\r
25be5c52 765 VideoWriteVRAM128(a, data);\r
e1e7d1ed 766\r
767 // Increment address register\r
25be5c52 768 a = (a+inc) & ~0x20000;\r
e1e7d1ed 769 }\r
e1e7d1ed 770 break;\r
0c7d1ba3 771 default:\r
772 a += len * inc;\r
773 break;\r
cc68a136 774 }\r
0c7d1ba3 775\r
cc68a136 776 // remember addr\r
f1dbe764 777 pvid->addr = a;\r
778 pvid->addr_u = a >> 16;\r
0c7d1ba3 779 // register update\r
f1dbe764 780 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
781 source = pvid->reg[0x15];\r
782 source |= pvid->reg[0x16] << 8;\r
0c7d1ba3 783 source += len;\r
f1dbe764 784 pvid->reg[0x15] = source;\r
785 pvid->reg[0x16] = source >> 8;\r
cc68a136 786}\r
787\r
17bd69ad 788// VDP command handling\r
789\r
0c7d1ba3 790static NOINLINE void CommandDma(void)\r
cc68a136 791{\r
b72662e2 792 struct PicoVideo *pvid = &Pico.video;\r
0c7d1ba3 793 u32 len, method;\r
794 u32 source;\r
cc68a136 795\r
17bd69ad 796 PicoVideoFIFOSync(SekCyclesDone()-Pico.t.m68c_line_start);\r
797 if (pvid->status & SR_DMA) {\r
310d973b 798 elprintf(EL_VDPDMA, "Dma overlap, left=%d @ %06x",\r
02138162 799 VdpFIFO.fifo_total, SekPc);\r
8eada9d6 800 VdpFIFO.fifo_total = VdpFIFO.fifo_ql = 0;\r
4f6d3b28 801 pvid->status &= ~PVS_DMAFILL;\r
17bd69ad 802 }\r
eef77d7a 803\r
0c7d1ba3 804 len = GetDmaLength();\r
f1dbe764 805 source = pvid->reg[0x15];\r
806 source |= pvid->reg[0x16] << 8;\r
807 source |= pvid->reg[0x17] << 16;\r
cc68a136 808\r
809 method=pvid->reg[0x17]>>6;\r
0c7d1ba3 810 if (method < 2)\r
811 DmaSlow(len, source << 1); // 68000 to VDP\r
812 else if (method == 3)\r
813 DmaCopy(len); // VRAM Copy\r
17bd69ad 814 else {\r
3c6da92b 815 pvid->status |= SR_DMA|PVS_DMAFILL;\r
0c7d1ba3 816 return;\r
17bd69ad 817 }\r
0c7d1ba3 818 source += len;\r
f1dbe764 819 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
820 pvid->reg[0x15] = source;\r
821 pvid->reg[0x16] = source >> 8;\r
cc68a136 822}\r
823\r
86198e03 824static NOINLINE void CommandChange(struct PicoVideo *pvid)\r
cc68a136 825{\r
b71cbbf7 826 unsigned int cmd, addr;\r
cc68a136 827\r
b71cbbf7 828 cmd = pvid->command;\r
cc68a136 829\r
830 // Get type of transfer 0xc0000030 (v/c/vsram read/write)\r
b71cbbf7 831 pvid->type = (u8)(((cmd >> 2) & 0xc) | (cmd >> 30));\r
832 if (pvid->type == 1) // vram\r
833 pvid->type |= pvid->reg[1] & 0x80; // 128k\r
cc68a136 834\r
835 // Get address 0x3fff0003\r
b71cbbf7 836 addr = (cmd >> 16) & 0x3fff;\r
837 addr |= (cmd << 14) & 0xc000;\r
838 pvid->addr = (u16)addr;\r
839 pvid->addr_u = (u8)((cmd >> 2) & 1);\r
cc68a136 840}\r
841\r
17bd69ad 842// VDP interface\r
843 \r
d515a352 844static inline int InHblank(int offs)\r
845{\r
0c9c8e47 846 // check if in left border (14 pixels) or HBLANK (86 pixels), 116 68k cycles\r
847 return SekCyclesDone() - Pico.t.m68c_line_start <= offs;\r
d515a352 848}\r
849\r
f9ed9446 850void PicoVideoSync(int skip)\r
b6d7ac70 851{\r
97232a47 852 struct VdpFIFO *vf = &VdpFIFO;\r
43e14010 853 int lines = Pico.video.reg[1]&0x08 ? 240 : 224;\r
97232a47 854 int last = Pico.m.scanline - (skip > 0);\r
f9ed9446 855\r
856 if (!(PicoIn.opt & POPT_ALT_RENDERER) && !PicoIn.skipFrame) {\r
857 if (last >= lines)\r
858 last = lines-1;\r
859 else // in active display, need to sync next frame as well\r
860 Pico.est.rendstatus |= PDRAW_SYNC_NEXT;\r
17bd69ad 861\r
2aa27095 862 //elprintf(EL_ANOMALY, "sync");\r
97232a47 863 if (unlikely(linedisabled >= 0 && linedisabled <= last)) {\r
864 if (Pico.est.DrawScanline <= linedisabled) {\r
865 int sl = vf->fifo_hcounts[lineoffset/clkdiv];\r
866 PicoDrawSync(linedisabled, sl ? sl : 1, 0);\r
867 }\r
868 linedisabled = -1;\r
17bd69ad 869 }\r
97232a47 870 if (unlikely(lineenabled >= 0 && lineenabled <= last)) {\r
871 if (Pico.est.DrawScanline <= lineenabled) {\r
872 int sl = vf->fifo_hcounts[lineoffset/clkdiv];\r
873 PicoDrawSync(lineenabled, 0, sl ? sl : 1);\r
874 }\r
875 lineenabled = -1;\r
876 }\r
877 if (Pico.est.DrawScanline <= last)\r
878 PicoDrawSync(last, 0, 0);\r
b6d7ac70 879 }\r
f9ed9446 880 if (skip >= 0)\r
881 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;\r
b6d7ac70 882}\r
883\r
4cc0fcaf 884PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)\r
cc68a136 885{\r
886 struct PicoVideo *pvid=&Pico.video;\r
887\r
22814963 888 //elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x [%u] @ %06x",\r
889 // a, d, SekCyclesDone(), SekPc);\r
cc68a136 890\r
22814963 891 a &= 0x1c;\r
e0bcb7a9 892 switch (a)\r
4f672280 893 {\r
e0bcb7a9 894 case 0x00: // Data port 0 or 2\r
69996cb7 895 if (pvid->pending) {\r
86198e03 896 CommandChange(pvid);\r
69996cb7 897 pvid->pending=0;\r
898 }\r
cc68a136 899\r
0c9c8e47 900 // try avoiding the sync if the data doesn't change.\r
901 // Writes to the SAT in VRAM are special since they update the SAT cache.\r
902 if ((pvid->reg[1]&0x40) &&\r
903 !(pvid->type == 1 && !(pvid->addr&1) && ((pvid->addr^SATaddr)&SATmask) && PicoMem.vram[pvid->addr>>1] == d) &&\r
d515a352 904 !(pvid->type == 3 && PicoMem.cram[(pvid->addr>>1) & 0x3f] == (d & 0xeee)) &&\r
905 !(pvid->type == 5 && PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))\r
f9ed9446 906 // the vertical scroll value for this line must be read from VSRAM early,\r
907 // since the A/B tile row to be read depends on it. E.g. Skitchin, OD2\r
908 // in contrast, CRAM writes would have an immediate effect on the current\r
444c592a 909 // pixel, so sync can be closer to start of actual image data\r
da102297 910 PicoVideoSync(InHblank(pvid->type == 3 ? 103 : 30)); // cram in Toy Story\r
d515a352 911\r
17bd69ad 912 if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))\r
cc68a136 913 {\r
02138162 914 VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;\r
17bd69ad 915 SekCyclesBurnRun(PicoVideoFIFOWrite(1, pvid->type == 1, 0, PVS_CPUWR));\r
916\r
917 elprintf(EL_ASVDP, "VDP data write: [%04x] %04x [%u] {%i} @ %06x",\r
f1dbe764 918 pvid->addr, d, SekCyclesDone(), pvid->type, SekPc);\r
cc68a136 919 }\r
0c7d1ba3 920 VideoWrite(d);\r
921\r
17bd69ad 922 // start DMA fill on write. NB VSRAM and CRAM fills use wrong FIFO data.\r
787a0af9 923 if (pvid->status & PVS_DMAFILL)\r
02138162 924 DmaFill(VdpFIFO.fifo_data[(VdpFIFO.fifo_dx + !!(pvid->type&~0x81))&3]);\r
0c7d1ba3 925\r
e0bcb7a9 926 break;\r
cc68a136 927\r
e0bcb7a9 928 case 0x04: // Control (command) port 4 or 6\r
787a0af9 929 if (pvid->status & SR_DMA)\r
930 SekCyclesBurnRun(PicoVideoFIFORead()); // kludge, flush out running DMA\r
b6d7ac70 931 if (pvid->pending)\r
cc68a136 932 {\r
933 // Low word of command:\r
17bd69ad 934 if (!(pvid->reg[1]&0x10))\r
935 d = (d&~0x80)|(pvid->command&0x80);\r
0c7d1ba3 936 pvid->command &= 0xffff0000;\r
937 pvid->command |= d;\r
938 pvid->pending = 0;\r
86198e03 939 CommandChange(pvid);\r
0c7d1ba3 940 // Check for dma:\r
941 if (d & 0x80) {\r
f9ed9446 942 PicoVideoSync(InHblank(93));\r
0c7d1ba3 943 CommandDma();\r
944 }\r
602133e1 945 }\r
946 else\r
947 {\r
948 if ((d&0xc000)==0x8000)\r
cc68a136 949 {\r
950 // Register write:\r
951 int num=(d>>8)&0x1f;\r
0ffefdb8 952 int dold=pvid->reg[num];\r
602133e1 953 pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)\r
0ffefdb8 954 if (num > 0x0a && !(pvid->reg[1]&4)) {\r
955 elprintf(EL_ANOMALY, "%02x written to reg %02x in SMS mode @ %06x", d, num, SekPc);\r
602133e1 956 return;\r
b6d7ac70 957 }\r
602133e1 958\r
0c9c8e47 959 d &= 0xff;\r
97232a47 960\r
961 if (num == 1 && ((pvid->reg[1]^d)&0x40)) {\r
97232a47 962 // handle line blanking before line rendering. Only the last switch\r
6fd16ed6 963 // before the 1st sync for other reasons is honoured. Switching after\r
964 // active area is on next line\r
965 int skip = InHblank(470); // Deadly Moves\r
966 PicoVideoSync(skip);\r
967 lineenabled = (d&0x40) ? Pico.m.scanline + !skip: -1;\r
968 linedisabled = (d&0x40) ? -1 : Pico.m.scanline + !skip;\r
969 lineoffset = (skip ? SekCyclesDone() - Pico.t.m68c_line_start : 0);\r
f1dbe764 970 } else if (((1<<num) & 0x738ff) && pvid->reg[num] != d)\r
f9ed9446 971 // VDP regs 0-7,11-13,16-18 influence rendering, ignore all others\r
972 PicoVideoSync(InHblank(93)); // Toy Story\r
973 pvid->reg[num] = d;\r
0c9c8e47 974\r
602133e1 975 switch (num)\r
976 {\r
977 case 0x00:\r
7647f87b 978 if ((~dold&d)&2) {\r
979 unsigned c = SekCyclesDone() - Pico.t.m68c_line_start;\r
980 pvid->hv_latch = VdpFIFO.fifo_hcounts[c/clkdiv] | (pvid->v_counter << 8);\r
981 }\r
ebd70cb5 982 elprintf(EL_INTSW, "hint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x10)>>4,\r
602133e1 983 (d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);\r
147f8df1 984 goto update_irq;\r
602133e1 985 case 0x01:\r
7647f87b 986 if ((d^dold)&0x40)\r
987 PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);\r
0e4bde9b 988 if (!(pvid->status & PVS_VB2))\r
989 pvid->status &= ~SR_VB;\r
990 pvid->status |= ((d >> 3) ^ SR_VB) & SR_VB; // forced blanking\r
7647f87b 991 elprintf(EL_INTSW, "vint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x20)>>5,\r
992 (d&0x20)>>5, SekCyclesDone(), (pvid->pending_ints&0x20)>>5, SekPc);\r
147f8df1 993 goto update_irq;\r
602133e1 994 case 0x05:\r
25be5c52 995 case 0x06:\r
0c9c8e47 996 if (d^dold) Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
602133e1 997 break;\r
998 case 0x0c:\r
999 // renderers should update their palettes if sh/hi mode is changed\r
b1a047c9 1000 if ((d^dold)&8) Pico.m.dirtyPal = 1;\r
7647f87b 1001 if ((d^dold)&1) {\r
1002 PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);\r
1003 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
1004 }\r
602133e1 1005 break;\r
25be5c52 1006 default:\r
1007 return;\r
602133e1 1008 }\r
51d6248b 1009 if (Pico.est.rendstatus & PDRAW_DIRTY_SPRITES) {\r
1010 SATaddr = ((pvid->reg[5]&0x7f) << 9) | ((pvid->reg[6]&0x20) << 11);\r
1011 SATmask = ~0x1ff;\r
1012 if (pvid->reg[12]&1)\r
1013 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit\r
1014 //elprintf(EL_STATUS, "spritep moved to %04x", SATaddr);\r
1015 }\r
147f8df1 1016 return;\r
602133e1 1017\r
147f8df1 1018update_irq:\r
03e4f2a3 1019#ifndef EMU_CORE_DEBUG\r
0290987c 1020 // update IRQ level; TODO hack, still fire irq if disabling now\r
1021 if (!SekShouldInterrupt() || SekIrqLevel < pvid->hint_irq)\r
03e4f2a3 1022 {\r
22814963 1023 int lines, pints, irq = 0;\r
602133e1 1024 lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);\r
22814963 1025 pints = pvid->pending_ints & lines;\r
b6d7ac70 1026 if (pints & 0x20) irq = 6;\r
ba2b97dc 1027 else if (pints & 0x10) irq = pvid->hint_irq;\r
0290987c 1028\r
b8a38093 1029 if (irq) {\r
0290987c 1030 // VDP irqs have highest prio, just overwrite old level\r
ba2b97dc 1031 SekInterrupt(irq); // update line\r
602133e1 1032\r
0290987c 1033 // TODO this is broken because cost of current insn isn't known here\r
b8a38093 1034 SekEndRun(21); // make it delayed\r
1035 } else if (SekIrqLevel >= pvid->hint_irq) {\r
1036 // no VDP irq, query lower irqs\r
0290987c 1037 SekInterrupt(PicoIn.AHW & PAHW_PICO ? PicoPicoIrqAck(0) : 0);\r
b8a38093 1038 }\r
cc68a136 1039 }\r
cc68a136 1040#endif\r
602133e1 1041 }\r
1042 else\r
1043 {\r
cc68a136 1044 // High word of command:\r
1045 pvid->command&=0x0000ffff;\r
1046 pvid->command|=d<<16;\r
1047 pvid->pending=1;\r
1048 }\r
1049 }\r
e0bcb7a9 1050 break;\r
1051\r
1052 // case 0x08: // 08 0a - HV counter - lock up\r
1053 // case 0x0c: // 0c 0e - HV counter - lock up\r
1054 // case 0x10: // 10 12 - PSG - handled by caller\r
1055 // case 0x14: // 14 16 - PSG - handled by caller\r
1056 // case 0x18: // 18 1a - no effect?\r
1057 case 0x1c: // 1c 1e - debug\r
1058 pvid->debug = d;\r
1059 pvid->debug_p = 0;\r
1060 if (d & (1 << 6)) {\r
1061 pvid->debug_p |= PVD_KILL_A | PVD_KILL_B;\r
1062 pvid->debug_p |= PVD_KILL_S_LO | PVD_KILL_S_HI;\r
1063 }\r
1064 switch ((d >> 7) & 3) {\r
1065 case 1:\r
1066 pvid->debug_p &= ~(PVD_KILL_S_LO | PVD_KILL_S_HI);\r
1067 pvid->debug_p |= PVD_FORCE_S;\r
1068 break;\r
1069 case 2:\r
1070 pvid->debug_p &= ~PVD_KILL_A;\r
1071 pvid->debug_p |= PVD_FORCE_A;\r
1072 break;\r
1073 case 3:\r
1074 pvid->debug_p &= ~PVD_KILL_B;\r
1075 pvid->debug_p |= PVD_FORCE_B;\r
1076 break;\r
1077 }\r
1078 break;\r
cc68a136 1079 }\r
1080}\r
1081\r
17bd69ad 1082static u32 VideoSr(const struct PicoVideo *pv)\r
cc68a136 1083{\r
7263343d 1084 unsigned int hp = pv->reg[12]&1 ? hboff40*488.5/slots40 : hboff32*488.5/slots32;\r
1085 unsigned int hl = pv->reg[12]&1 ? hblen40*488.5/slots40 : hblen32*488.5/slots32;\r
1086 unsigned int c = SekCyclesDone() - Pico.t.m68c_line_start;\r
72c2a04a 1087 u32 d;\r
1088\r
1089 PicoVideoFIFOSync(c);\r
1090 d = (u16)pv->status;\r
cc68a136 1091\r
787a0af9 1092 if (c - hp < hl)\r
0e4bde9b 1093 d |= SR_HB;\r
17bd69ad 1094\r
02138162 1095 if (VdpFIFO.fifo_total >= 4)\r
17bd69ad 1096 d |= SR_FULL;\r
02138162 1097 else if (!VdpFIFO.fifo_total)\r
17bd69ad 1098 d |= SR_EMPT;\r
0e4bde9b 1099 return d;\r
1100}\r
cc68a136 1101\r
15eed405 1102PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)\r
0e4bde9b 1103{\r
f1dbe764 1104 struct PicoVideo *pv = &Pico.video;\r
0e4bde9b 1105 a &= 0x1c;\r
cc68a136 1106\r
0e4bde9b 1107 if (a == 0x04) // control port\r
1108 {\r
15eed405 1109 u32 d = VideoSr(pv);\r
17bd69ad 1110 if (pv->pending) {\r
86198e03 1111 CommandChange(pv);\r
17bd69ad 1112 pv->pending = 0;\r
1113 }\r
22814963 1114 elprintf(EL_SR, "SR read: %04x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
b6d7ac70 1115 return d;\r
cc68a136 1116 }\r
1117\r
9f29605f 1118 if (a == 0x08)\r
cc68a136 1119 {\r
15eed405 1120 unsigned int c;\r
1121 u32 d;\r
947fb5f9 1122\r
df18e715 1123 c = SekCyclesDone() - Pico.t.m68c_line_start;\r
f1dbe764 1124 if (pv->reg[0]&2)\r
1125 d = pv->hv_latch;\r
1126 else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8);\r
cc68a136 1127\r
f1dbe764 1128 elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc);\r
17bd69ad 1129 return d;\r
9761a7d0 1130 }\r
cc68a136 1131\r
9761a7d0 1132 if (a==0x00) // data port\r
1133 {\r
1613ec6c 1134 return VideoRead(0);\r
9761a7d0 1135 }\r
cc68a136 1136\r
9f29605f 1137 return PicoRead16_floating(a | 0xc00000);\r
9761a7d0 1138}\r
1139\r
1613ec6c 1140unsigned char PicoVideoRead8DataH(int is_from_z80)\r
9761a7d0 1141{\r
1613ec6c 1142 return VideoRead(is_from_z80) >> 8;\r
75b84e4b 1143}\r
9761a7d0 1144\r
1613ec6c 1145unsigned char PicoVideoRead8DataL(int is_from_z80)\r
75b84e4b 1146{\r
1613ec6c 1147 return VideoRead(is_from_z80);\r
75b84e4b 1148}\r
cc68a136 1149\r
1613ec6c 1150unsigned char PicoVideoRead8CtlH(int is_from_z80)\r
75b84e4b 1151{\r
86198e03 1152 struct PicoVideo *pv = &Pico.video;\r
1153 u8 d = VideoSr(pv) >> 8;\r
1154 if (pv->pending) {\r
1155 CommandChange(pv);\r
1156 pv->pending = 0;\r
17bd69ad 1157 }\r
75b84e4b 1158 elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);\r
1159 return d;\r
1160}\r
1161\r
1613ec6c 1162unsigned char PicoVideoRead8CtlL(int is_from_z80)\r
75b84e4b 1163{\r
86198e03 1164 struct PicoVideo *pv = &Pico.video;\r
1165 u8 d = VideoSr(pv);\r
1166 if (pv->pending) {\r
1167 CommandChange(pv);\r
1168 pv->pending = 0;\r
17bd69ad 1169 }\r
75b84e4b 1170 elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);\r
1171 return d;\r
1172}\r
1173\r
1613ec6c 1174unsigned char PicoVideoRead8HV_H(int is_from_z80)\r
75b84e4b 1175{\r
7647f87b 1176 u32 d = Pico.video.v_counter;\r
1177 if (Pico.video.reg[0]&2)\r
1178 d = Pico.video.hv_latch >> 8;\r
1179 elprintf(EL_HVCNT, "vcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
1180 return d;\r
75b84e4b 1181}\r
1182\r
1183// FIXME: broken\r
1613ec6c 1184unsigned char PicoVideoRead8HV_L(int is_from_z80)\r
75b84e4b 1185{\r
df18e715 1186 u32 d = SekCyclesDone() - Pico.t.m68c_line_start;\r
3c6da92b 1187 if (Pico.video.reg[0]&2)\r
1188 d = Pico.video.hv_latch;\r
0c9c8e47 1189 else d = VdpFIFO.fifo_hcounts[d/clkdiv];\r
ebd70cb5 1190 elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
75b84e4b 1191 return d;\r
cc68a136 1192}\r
e7b3ad1b 1193\r
51d6248b 1194void PicoVideoReset(void)\r
1195{\r
51d6248b 1196 Pico.video.pending_ints=0;\r
c7661b80 1197 Pico.video.reg[1] &= ~0x40; // TODO verify display disabled after reset\r
1198 Pico.video.reg[10] = 0xff; // HINT is turned off after reset\r
1199 Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal\r
51d6248b 1200\r
51d6248b 1201 memset(&VdpFIFO, 0, sizeof(VdpFIFO));\r
1202 Pico.m.dirtyPal = 1;\r
1203\r
51d6248b 1204 PicoDrawBgcDMA(NULL, 0, 0, 0, 0);\r
96e59821 1205 PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);\r
51d6248b 1206}\r
1207\r
4496577e 1208void PicoVideoCacheSAT(int load)\r
e721f801 1209{\r
1210 struct PicoVideo *pv = &Pico.video;\r
1211 int l;\r
1212\r
1213 SATaddr = ((pv->reg[5]&0x7f) << 9) | ((pv->reg[6]&0x20) << 11);\r
1214 SATmask = ~0x1ff;\r
1215 if (pv->reg[12]&1)\r
1216 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit\r
1217\r
1218 // rebuild SAT cache XXX wrong since cache and memory can differ\r
ac891449 1219 for (l = 0; load && l < 2*80; l ++) {\r
1220 u16 addr = SATaddr + l*4;\r
56e0b865 1221 ((u16 *)VdpSATCache)[l*2 ] = PicoMem.vram[(addr>>1) ];\r
1222 ((u16 *)VdpSATCache)[l*2 + 1] = PicoMem.vram[(addr>>1) + 1];\r
e721f801 1223 }\r
1224\r
0c9c8e47 1225 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
e721f801 1226}\r
1227\r
d4b5888a 1228#include <stddef.h>\r
1229\r
1230int PicoVideoSave(void *buf)\r
daf29df9 1231{\r
d4b5888a 1232 u8 *bp = buf;\r
1233 int i;\r
1234\r
1235 // FIFO stuff\r
1236 memcpy(bp, &VdpFIFO, offsetof(struct VdpFIFO, fifo_slot));\r
1237 bp += offsetof(struct VdpFIFO, fifo_slot);\r
1238\r
1239 // SAT Cache\r
1240 for (i = 0; i < 80; i++, bp += sizeof(u32))\r
1241 memcpy(bp, VdpSATCache+2*i, sizeof(u32));\r
1242\r
1243 return bp - (u8 *)buf;\r
daf29df9 1244}\r
1245\r
d4b5888a 1246void PicoVideoLoad(void *buf, int len)\r
daf29df9 1247{\r
02138162 1248 struct VdpFIFO *vf = &VdpFIFO;\r
daf29df9 1249 struct PicoVideo *pv = &Pico.video;\r
fda7339b 1250 int b = pv->type == 1;\r
daf29df9 1251\r
d4b5888a 1252 SATaddr = ((pv->reg[5]&0x7f) << 9) | ((pv->reg[6]&0x20) << 11);\r
1253 SATmask = ~0x1ff;\r
1254 if (pv->reg[12]&1)\r
1255 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit\r
1256\r
1257 if (len) {\r
1258 int i;\r
1259 if (len >= offsetof(struct VdpFIFO, fifo_slot))\r
1260 memcpy(&VdpFIFO, buf, offsetof(struct VdpFIFO, fifo_slot));\r
1261 for (i = 0; i < 80; i++)\r
1262 memcpy(VdpSATCache+2*i, buf + offsetof(struct VdpFIFO, fifo_slot) + 4*i, sizeof(u32));\r
1263 return;\r
1264 }\r
1265\r
daf29df9 1266 // convert former dma_xfers (why was this in PicoMisc anyway?)\r
1267 if (Pico.m.dma_xfers) {\r
22a548f5 1268 pv->fifo_cnt = Pico.m.dma_xfers << b;\r
daf29df9 1269 Pico.m.dma_xfers = 0;\r
1270 }\r
e45908d7 1271\r
1272 // fake entries in the FIFO if there are outstanding transfers\r
8eada9d6 1273 vf->fifo_ql = vf->fifo_qx = vf->fifo_total = 0;\r
fda7339b 1274 if (pv->fifo_cnt) {\r
8eada9d6 1275 int wc = pv->fifo_cnt;\r
8eada9d6 1276 vf->fifo_total = (wc+b) >> b;\r
e45908d7 1277 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | b | FQ_FGDMA;\r
1278 vf->fifo_ql ++;\r
c5ecd7a0 1279 if (vf->fifo_total > 4 && !(pv->status & (PVS_CPUWR|PVS_CPURD)))\r
1280 pv->status |= PVS_CPUWR;\r
e45908d7 1281 }\r
1282 if (pv->fifo_bgcnt) {\r
1283 int wc = pv->fifo_bgcnt;\r
8eada9d6 1284 if (!vf->fifo_ql)\r
4f6d3b28 1285 pv->status |= PVS_DMABG;\r
e45908d7 1286 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | FQ_BGDMA;\r
1287 vf->fifo_ql ++;\r
fda7339b 1288 }\r
4496577e 1289 PicoVideoCacheSAT(1);\r
c5ecd7a0 1290 vf->fifo_maxslot = 0;\r
daf29df9 1291}\r
e7b3ad1b 1292// vim:shiftwidth=2:ts=2:expandtab\r