platform ps2, handle audio similar to psp
[picodrive.git] / pico / videoport.c
... / ...
CommitLineData
1/*\r
2 * PicoDrive\r
3 * (c) Copyright Dave, 2004\r
4 * (C) notaz, 2006-2009\r
5 * (C) irixxxx, 2020-2024\r
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
10\r
11#include "pico_int.h"\r
12#define NEED_DMA_SOURCE\r
13#include "memory.h"\r
14\r
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
20// http://plutiedev.com/mirror/kabuto-hardware-notes\r
21// Thank you very much for the great work, Nemesis, Kabuto!\r
22\r
23// Slot clock is sysclock/20 for h32 and sysclock/16 for h40.\r
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
26// 1 slot is 20/7 = 2.857 68k cycles in h32, and 16/7 = 2.286 in h40. That's\r
27// 171 slots in h32, and ~214 (really 193 plus 17 prolonged in HSYNC) in h40.\r
28enum { slcpu = 488 };\r
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
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
35enum { hint32 = 0x85, gapstart32 = 0x94, gapend32 = 0xe9};\r
36enum { hint40 = 0xa5, gapstart40 = 0xb7, gapend40 = 0xe5};\r
37\r
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
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
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
49enum { hboff32 = 0x93-hint32, hblen32 = 0xf8-(gapend32-gapstart32)-hint32};//30\r
50enum { hboff40 = 0xb4-hint40, hblen40 = 0xf8-(gapend40-gapstart40)-hint40};//37\r
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
71enum { cycsz = slcpu/clkdiv };\r
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
77// VDP access 2 slots may need to be taken from the next scanline, which can be\r
78// more than 100 CPU cycles. For safety just cover 2 scanlines.\r
79\r
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
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
95 float factor = (float)slcpu/slots##s; \\r
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
103 hcounts_##s[i] = n % 256; \\r
104 } \\r
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
128 \\r
129 /* extend tables for 2nd scanline */ \\r
130 memcpy(hcounts_##s+cycsz, hcounts_##s, ARRAY_SIZE(hcounts_##s)-cycsz);\\r
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
135 vdpsl2cyc_##s##_ac[ax] = vdpsl2cyc_##s##_ac[ax-i]+cycsz; \\r
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
151\r
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
155\r
156u32 SATaddr, SATmask; // VRAM addr of sprite attribute table\r
157\r
158int (*PicoDmaHook)(u32 source, int len, unsigned short **base, u32 *mask) = NULL;\r
159\r
160\r
161/* VDP FIFO implementation\r
162 * \r
163 * fifo_slot: last slot executed in this scanline\r
164 * fifo_total: #total FIFO entries pending\r
165 * fifo_data: last values transferred through fifo\r
166 * fifo_queue: fifo transfer queue (#writes, flags)\r
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
173 * fifo_slot is normally behind slot2cyc[cycles]. Advancing it beyond cycles\r
174 * implies blocking the 68k up to that slot.\r
175 *\r
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
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
191// NB code assumes fifo_* arrays have size 2^n\r
192static struct VdpFIFO { // XXX this must go into save file!\r
193 // last transferred FIFO data, ...x = index XXX currently only CPU\r
194 u16 fifo_data[4], fifo_dx;\r
195\r
196 // queued FIFO transfers, ...x = index, ...l = queue length\r
197 // each entry has 2 values: [n]>>3 = #writes, [n]&7 = flags (FQ_*)\r
198 u32 fifo_queue[8], fifo_qx, fifo_ql;\r
199 int fifo_total; // total# of pending FIFO entries (w/o BGDMA)\r
200\r
201 unsigned short fifo_slot; // last executed slot in current scanline\r
202 unsigned short fifo_maxslot;// #slots in scanline\r
203\r
204 const unsigned short *fifo_cyc2sl;\r
205 const unsigned short *fifo_sl2cyc;\r
206 const unsigned char *fifo_hcounts;\r
207} VdpFIFO;\r
208\r
209enum { FQ_BYTE = 1, FQ_BGDMA = 2, FQ_FGDMA = 4 }; // queue flags, NB: BYTE = 1!\r
210\r
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
214#define Cyc2Sl(vf,lc) ((vf)->fifo_cyc2sl[(lc)/clkdiv])\r
215#define Sl2Cyc(vf,sl) ((vf)->fifo_sl2cyc[sl]*clkdiv)\r
216\r
217// do the FIFO math\r
218static int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, int slots)\r
219{\r
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
223\r
224 // advance currently active FIFO entry\r
225 if (l > cnt)\r
226 l = cnt;\r
227 if (!(*qx & FQ_BGDMA))\r
228 vf->fifo_total -= ((cnt & b) + l) >> b;\r
229 *qx -= l << 3;\r
230\r
231 // if entry has been processed...\r
232 if (cnt == l) {\r
233 // remove entry from FIFO\r
234 *qx = 0;\r
235 vf->fifo_qx = (vf->fifo_qx+1) & 7;\r
236 vf->fifo_ql --;\r
237 }\r
238\r
239 return l;\r
240}\r
241\r
242static void SetFIFOState(struct VdpFIFO *vf, struct PicoVideo *pv)\r
243{\r
244 u32 st = pv->status, cmd = pv->command;\r
245 // release CPU and terminate DMA if FIFO isn't blocking the 68k anymore\r
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
251 }\r
252 }\r
253 if (vf->fifo_ql == 0) {\r
254 st &= ~PVS_CPURD;\r
255 // terminate DMA if applicable\r
256 if (!(st & PVS_DMAFILL)) {\r
257 st &= ~(SR_DMA|PVS_DMABG);\r
258 cmd &= ~0x80;\r
259 }\r
260 }\r
261 pv->status = st;\r
262 pv->command = cmd;\r
263}\r
264\r
265// sync FIFO to cycles\r
266void PicoVideoFIFOSync(int cycles)\r
267{\r
268 struct VdpFIFO *vf = &VdpFIFO;\r
269 struct PicoVideo *pv = &Pico.video;\r
270 int slots, done;\r
271\r
272 // calculate #slots since last executed slot\r
273 slots = Cyc2Sl(vf, cycles) - vf->fifo_slot;\r
274 if (slots <= 0 || !vf->fifo_ql) return;\r
275\r
276 // advance FIFO queue by #done slots\r
277 done = slots;\r
278 while (done > 0 && vf->fifo_ql) {\r
279 int l = AdvanceFIFOEntry(vf, pv, done);\r
280 vf->fifo_slot += l;\r
281 done -= l;\r
282 }\r
283\r
284 if (done != slots)\r
285 SetFIFOState(vf, pv);\r
286}\r
287\r
288// drain FIFO, blocking 68k on the way. FIFO must be synced prior to drain.\r
289static int PicoVideoFIFODrain(int level, int cycles, int bgdma)\r
290{\r
291 struct VdpFIFO *vf = &VdpFIFO;\r
292 struct PicoVideo *pv = &Pico.video;\r
293 unsigned ocyc = cycles;\r
294 int bd = vf->fifo_queue[vf->fifo_qx] & bgdma;\r
295 int burn = 0;\r
296\r
297 if (!(vf->fifo_ql && ((vf->fifo_total > level) | bd))) return 0;\r
298\r
299 // process FIFO entries until low level is reached\r
300 while (vf->fifo_slot < vf->fifo_maxslot &&\r
301 vf->fifo_ql && ((vf->fifo_total > level) | bd)) {\r
302 int b = vf->fifo_queue[vf->fifo_qx] & FQ_BYTE;\r
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
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
310 }\r
311 if (slot > vf->fifo_slot) {\r
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
316 }\r
317 }\r
318 if (vf->fifo_ql && ((vf->fifo_total > level) | bd))\r
319 cycles = slcpu; // not completed in this scanline\r
320 if (cycles > ocyc)\r
321 burn = cycles - ocyc;\r
322\r
323 SetFIFOState(vf, pv);\r
324\r
325 return burn;\r
326}\r
327\r
328// read VDP data port\r
329static int PicoVideoFIFORead(void)\r
330{\r
331 struct VdpFIFO *vf = &VdpFIFO;\r
332 struct PicoVideo *pv = &Pico.video;\r
333 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
334 int burn = 0;\r
335\r
336 if (vf->fifo_ql) {\r
337 // advance FIFO and CPU until FIFO is empty\r
338 burn = PicoVideoFIFODrain(0, lc, FQ_BGDMA);\r
339 lc += burn;\r
340 }\r
341\r
342 if (vf->fifo_ql)\r
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
346 vf->fifo_slot = Cyc2Sl(vf, lc) + 1;\r
347 burn += Sl2Cyc(vf, vf->fifo_slot) - lc;\r
348 }\r
349\r
350 return burn;\r
351}\r
352 \r
353// write VDP data port\r
354int PicoVideoFIFOWrite(int count, int flags, unsigned sr_mask,unsigned sr_flags)\r
355{\r
356 struct VdpFIFO *vf = &VdpFIFO;\r
357 struct PicoVideo *pv = &Pico.video;\r
358 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
359 int burn = 0, x;\r
360\r
361 // sync only needed if queue is too full or background dma might be deferred\r
362 if ((vf->fifo_ql >= 6) | (pv->status & PVS_DMABG))\r
363 PicoVideoFIFOSync(lc);\r
364\r
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
367\r
368 pv->status = (pv->status & ~sr_mask) | sr_flags;\r
369 vf->fifo_total += count * !(flags & FQ_BGDMA);\r
370 if (!vf->fifo_ql)\r
371 vf->fifo_slot = Cyc2Sl(vf, lc+7); // FIFO latency ~3 vdp slots\r
372\r
373 // determine queue position for entry\r
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
384 }\r
385\r
386 // if CPU is waiting for the bus, advance CPU and FIFO until bus is free\r
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
389 if ((pv->status & PVS_CPUWR) && vf->fifo_total > 4 + x)\r
390 burn = PicoVideoFIFODrain(4, lc, 0);\r
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
398 struct VdpFIFO *vf = &VdpFIFO;\r
399 struct PicoVideo *pv = &Pico.video;\r
400 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
401 int burn = 0;\r
402\r
403 // reset slot to start of scanline\r
404 vf->fifo_slot = 0;\r
405 // only need to refresh sprite position if we are synced\r
406 if (Pico.est.DrawScanline == Pico.m.scanline && !(pv->status & SR_VB))\r
407 PicoDrawRefreshSprites();\r
408 \r
409 // if CPU is waiting for the bus, advance CPU and FIFO until bus is free\r
410 if (pv->status & PVS_CPUWR)\r
411 burn = PicoVideoFIFODrain(4, lc, 0);\r
412 else if (pv->status & PVS_CPURD)\r
413 burn = PicoVideoFIFORead();\r
414\r
415 return burn;\r
416}\r
417\r
418// switch FIFO mode between active/inactive display\r
419void PicoVideoFIFOMode(int active, int h40)\r
420{\r
421 static const unsigned short *vdpcyc2sl[2][2] =\r
422 { {vdpcyc2sl_32_bl, vdpcyc2sl_40_bl},{vdpcyc2sl_32_ac, vdpcyc2sl_40_ac} };\r
423 static const unsigned short *vdpsl2cyc[2][2] =\r
424 { {vdpsl2cyc_32_bl, vdpsl2cyc_40_bl},{vdpsl2cyc_32_ac, vdpsl2cyc_40_ac} };\r
425 static const unsigned char *vdphcounts[2] =\r
426 { hcounts_32, hcounts_40 };\r
427\r
428 struct VdpFIFO *vf = &VdpFIFO;\r
429 struct PicoVideo *pv = &Pico.video;\r
430 int lc = SekCyclesDone() - Pico.t.m68c_line_start;\r
431 active = active && !(pv->status & PVS_VB2);\r
432\r
433 if (vf->fifo_maxslot)\r
434 PicoVideoFIFOSync(lc);\r
435 else\r
436 lc = 0;\r
437\r
438 vf->fifo_cyc2sl = vdpcyc2sl[active][h40];\r
439 vf->fifo_sl2cyc = vdpsl2cyc[active][h40];\r
440 vf->fifo_hcounts = vdphcounts[h40];\r
441 // recalculate FIFO slot for new mode\r
442 vf->fifo_slot = Cyc2Sl(vf, lc);\r
443 vf->fifo_maxslot = Cyc2Sl(vf, slcpu);\r
444}\r
445\r
446// VDP memory rd/wr\r
447\r
448static __inline void AutoIncrement(void)\r
449{\r
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
453}\r
454\r
455static NOINLINE void VideoWriteVRAM128(u32 a, u16 d)\r
456{\r
457 // nasty\r
458 u32 b = ((a & 2) >> 1) | ((a & 0x400) >> 9) | (a & 0x3FC) | ((a & 0x1F800) >> 1);\r
459\r
460 ((u8 *)PicoMem.vram)[b] = d;\r
461 if (!(u16)((b^SATaddr) & SATmask))\r
462 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
463\r
464 if (((a^SATaddr) & SATmask) == 0)\r
465 UpdateSAT(a, d);\r
466}\r
467\r
468static void VideoWrite(u16 d)\r
469{\r
470 struct PicoVideo *pvid = &Pico.video;\r
471 unsigned int a = pvid->addr;\r
472\r
473 switch (pvid->type)\r
474 {\r
475 case 1: if (a & 1)\r
476 d = (u16)((d << 8) | (d >> 8));\r
477 a |= pvid->addr_u << 16;\r
478 VideoWriteVRAM(a, d);\r
479 break;\r
480 case 3: if (PicoMem.cram [(a >> 1) & 0x3f] != (d & 0xeee)) Pico.m.dirtyPal = 1;\r
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
484 a |= pvid->addr_u << 16;\r
485 VideoWriteVRAM128(a, d);\r
486 break;\r
487 //default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, pvid->type); break;\r
488 }\r
489\r
490 AutoIncrement();\r
491}\r
492\r
493static unsigned int VideoRead(int is_from_z80)\r
494{\r
495 struct PicoVideo *pvid = &Pico.video;\r
496 unsigned int a, d = VdpFIFO.fifo_data[(VdpFIFO.fifo_dx+1)&3];\r
497\r
498 a=pvid->addr; a>>=1;\r
499\r
500 if (!is_from_z80)\r
501 SekCyclesBurnRun(PicoVideoFIFORead());\r
502 switch (pvid->type)\r
503 {\r
504 case 0: d=PicoMem.vram [a & 0x7fff]; break;\r
505 case 8: d=PicoMem.cram [a & 0x003f] | (d & ~0x0eee); break;\r
506 case 4: if ((a & 0x3f) >= 0x28) a = 0;\r
507 d=PicoMem.vsram [a & 0x003f] | (d & ~0x07ff); break;\r
508 case 12:a=PicoMem.vram [a & 0x7fff]; if (pvid->addr&1) a >>= 8;\r
509 d=(a & 0x00ff) | (d & ~0x00ff); break;\r
510 default:elprintf(EL_ANOMALY, "VDP read with bad type %i", pvid->type); break;\r
511 }\r
512\r
513 AutoIncrement();\r
514 return d;\r
515}\r
516\r
517// VDP DMA\r
518\r
519static int GetDmaLength(void)\r
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
526 len = ((len - 1) & 0xffff) + 1;\r
527 return len;\r
528}\r
529\r
530static void DmaSlow(int len, u32 source)\r
531{\r
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
535 u16 *r, *base = NULL;\r
536 u32 mask = 0x1ffff;\r
537 int lc = SekCyclesDone()-Pico.t.m68c_line_start;\r
538\r
539 elprintf(EL_VDPDMA, "DmaSlow[%i] %06x->%04x len %i inc=%i blank %i [%u] @ %06x",\r
540 pvid->type, source, a, len, inc, (pvid->status&SR_VB)||!(pvid->reg[1]&0x40),\r
541 SekCyclesDone(), SekPc);\r
542\r
543 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_FGDMA | (pvid->type == 1),\r
544 PVS_DMABG, SR_DMA | PVS_CPUWR));\r
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
548\r
549 if ((source & 0xe00000) == 0xe00000) { // Ram\r
550 base = (u16 *)PicoMem.ram;\r
551 mask = 0xffff;\r
552 }\r
553 else if (PicoIn.AHW & PAHW_MCD)\r
554 {\r
555 u8 r3 = Pico_mcd->s68k_regs[3];\r
556 elprintf(EL_VDPDMA, "DmaSlow CD, r3=%02x", r3);\r
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
560 if (!(r3 & 4)) { // 2M mode\r
561 base = (u16 *)(Pico_mcd->word_ram2M + (source & 0x20000));\r
562 } else {\r
563 if ((source & 0xfe0000) < pcd_base_address+0x220000) { // 1M mode\r
564 int bank = r3 & 1;\r
565 base = (u16 *)(Pico_mcd->word_ram1M[bank]);\r
566 } else {\r
567 DmaSlowCell(source - 2, a, len, inc);\r
568 return;\r
569 }\r
570 }\r
571 source -= 2;\r
572 } else if ((source & 0xfe0000) == pcd_base_address+0x020000) { // Prg Ram\r
573 base = (u16 *)Pico_mcd->prg_ram_b[r3 >> 6];\r
574 source -= 2; // XXX: test\r
575 }\r
576 }\r
577 else\r
578 {\r
579 // if we have DmaHook, let it handle ROM because of possible DMA delay\r
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
585 }\r
586 if (!base) {\r
587 elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", pvid->type, source, a);\r
588 return;\r
589 }\r
590\r
591 // operate in words\r
592 source >>= 1;\r
593 mask >>= 1;\r
594\r
595 switch (pvid->type)\r
596 {\r
597 case 1: // vram\r
598 e = a + len*2-1;\r
599 r = PicoMem.vram;\r
600 if (inc == 2 && !(a & 1) && !((a ^ e) >> 16) &&\r
601 ((a >= SATaddr + 0x280) | (e < SATaddr)) &&\r
602 !((source ^ (source + len-1)) & ~mask))\r
603 {\r
604 // most used DMA mode\r
605 memcpy((char *)r + a, base + (source & mask), len * 2);\r
606 a += len * 2;\r
607 break;\r
608 }\r
609 for(; len; len--)\r
610 {\r
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
616 }\r
617 break;\r
618\r
619 case 3: // cram\r
620 Pico.m.dirtyPal = 1;\r
621 r = PicoMem.cram;\r
622 if (inc == 0 && !(pvid->reg[1] & 0x40) &&\r
623 (pvid->reg[7] & 0x3f) == ((a/2) & 0x3f)) { // bg color DMA\r
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
629 if (sl <= 12) sl = -3;\r
630 else if (sl <= 40) sl = 30;\r
631 PicoDrawBgcDMA(base, source, mask, len, sl);\r
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
635 }\r
636 for (; len; len--)\r
637 {\r
638 r[(a / 2) & 0x3f] = base[source++ & mask] & 0xeee;\r
639 // AutoIncrement\r
640 a = (a+inc) & ~0x20000;\r
641 }\r
642 break;\r
643\r
644 case 5: // vsram\r
645 r = PicoMem.vsram;\r
646 for (; len; len--)\r
647 {\r
648 r[(a / 2) & 0x3f] = base[source++ & mask] & 0x7ff;\r
649 // AutoIncrement\r
650 a = (a+inc) & ~0x20000;\r
651 }\r
652 break;\r
653\r
654 case 0x81: // vram 128k\r
655 for(; len; len--)\r
656 {\r
657 u16 d = base[source++ & mask];\r
658 VideoWriteVRAM128(a, d);\r
659 // AutoIncrement\r
660 a = (a+inc) & ~0x20000;\r
661 }\r
662 break;\r
663\r
664 default:\r
665 if (pvid->type != 0 || (EL_LOGMASK & EL_VDPDMA))\r
666 elprintf(EL_VDPDMA|EL_ANOMALY, "DMA with bad type %i", pvid->type);\r
667 break;\r
668 }\r
669 // remember addr\r
670 pvid->addr = a;\r
671 pvid->addr_u = a >> 16;\r
672}\r
673\r
674static void DmaCopy(int len)\r
675{\r
676 struct PicoVideo *pvid=&Pico.video;\r
677 u32 a = pvid->addr | (pvid->addr_u << 16);\r
678 u8 *vr = (u8 *)PicoMem.vram;\r
679 u8 inc = pvid->reg[0xf];\r
680 int source;\r
681 elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());\r
682\r
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
685 PVS_CPUWR, SR_DMA | PVS_DMABG));\r
686\r
687 source =pvid->reg[0x15];\r
688 source|=pvid->reg[0x16]<<8;\r
689\r
690 for (; len; len--)\r
691 {\r
692 vr[(u16)a] = vr[(u16)(source++)];\r
693 if (((a^SATaddr) & SATmask) == 0)\r
694 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
695 // AutoIncrement\r
696 a = (a+inc) & ~0x20000;\r
697 }\r
698 // remember addr\r
699 pvid->addr = a;\r
700 pvid->addr_u = a >> 16;\r
701}\r
702\r
703static NOINLINE void DmaFill(int data)\r
704{\r
705 struct PicoVideo *pvid=&Pico.video;\r
706 u32 a = pvid->addr | (pvid->addr_u << 16), e;\r
707 u8 *vr = (u8 *)PicoMem.vram;\r
708 u8 high = (u8)(data >> 8);\r
709 u8 inc = pvid->reg[0xf];\r
710 int source;\r
711 int len, l;\r
712\r
713 len = GetDmaLength();\r
714 elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());\r
715\r
716 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_BGDMA, // 1 slot each (wr)\r
717 PVS_CPUWR | PVS_DMAFILL, SR_DMA | PVS_DMABG));\r
718\r
719 switch (pvid->type)\r
720 {\r
721 case 1: // vram\r
722 e = a + len-1;\r
723 if (inc == 1 && !((a ^ e) >> 16) &&\r
724 ((a >= SATaddr + 0x280) | (e < SATaddr)))\r
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
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
734 vr[(u16)a] = high;\r
735 if (((a^SATaddr) & SATmask) == 0)\r
736 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);\r
737\r
738 // Increment address register\r
739 a = (a+inc) & ~0x20000;\r
740 }\r
741 break;\r
742 case 3: // cram\r
743 Pico.m.dirtyPal = 1;\r
744 data &= 0xeee;\r
745 for (l = len; l; l--) {\r
746 PicoMem.cram[(a/2) & 0x3f] = data;\r
747\r
748 // Increment address register\r
749 a = (a+inc) & ~0x20000;\r
750 }\r
751 break;\r
752 case 5: { // vsram\r
753 data &= 0x7ff;\r
754 for (l = len; l; l--) {\r
755 PicoMem.vsram[(a/2) & 0x3f] = data;\r
756\r
757 // Increment address register\r
758 a = (a+inc) & ~0x20000;\r
759 }\r
760 break;\r
761 }\r
762 case 0x81: // vram 128k\r
763 for (l = len; l; l--) {\r
764 VideoWriteVRAM128(a, data);\r
765\r
766 // Increment address register\r
767 a = (a+inc) & ~0x20000;\r
768 }\r
769 break;\r
770 default:\r
771 a += len * inc;\r
772 break;\r
773 }\r
774\r
775 // remember addr\r
776 pvid->addr = a;\r
777 pvid->addr_u = a >> 16;\r
778 // register update\r
779 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
780 source = pvid->reg[0x15];\r
781 source |= pvid->reg[0x16] << 8;\r
782 source += len;\r
783 pvid->reg[0x15] = source;\r
784 pvid->reg[0x16] = source >> 8;\r
785}\r
786\r
787// VDP command handling\r
788\r
789static NOINLINE void CommandDma(void)\r
790{\r
791 struct PicoVideo *pvid = &Pico.video;\r
792 u32 len, method;\r
793 u32 source;\r
794\r
795 PicoVideoFIFOSync(SekCyclesDone()-Pico.t.m68c_line_start);\r
796 if (pvid->status & SR_DMA) {\r
797 elprintf(EL_VDPDMA, "Dma overlap, left=%d @ %06x",\r
798 VdpFIFO.fifo_total, SekPc);\r
799 VdpFIFO.fifo_total = VdpFIFO.fifo_ql = 0;\r
800 pvid->status &= ~PVS_DMAFILL;\r
801 }\r
802\r
803 len = GetDmaLength();\r
804 source = pvid->reg[0x15];\r
805 source |= pvid->reg[0x16] << 8;\r
806 source |= pvid->reg[0x17] << 16;\r
807\r
808 method=pvid->reg[0x17]>>6;\r
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
813 else {\r
814 pvid->status |= SR_DMA|PVS_DMAFILL;\r
815 return;\r
816 }\r
817 source += len;\r
818 pvid->reg[0x13] = pvid->reg[0x14] = 0;\r
819 pvid->reg[0x15] = source;\r
820 pvid->reg[0x16] = source >> 8;\r
821}\r
822\r
823static NOINLINE void CommandChange(struct PicoVideo *pvid)\r
824{\r
825 unsigned int cmd, addr;\r
826\r
827 cmd = pvid->command;\r
828\r
829 // Get type of transfer 0xc0000030 (v/c/vsram read/write)\r
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
833\r
834 // Get address 0x3fff0003\r
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
839}\r
840\r
841// VDP interface\r
842 \r
843static inline int InHblank(int offs)\r
844{\r
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
847}\r
848\r
849void PicoVideoSync(int skip)\r
850{\r
851 struct VdpFIFO *vf = &VdpFIFO;\r
852 int lines = Pico.video.reg[1]&0x08 ? 240 : 224;\r
853 int last = Pico.m.scanline - (skip > 0);\r
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
860\r
861 //elprintf(EL_ANOMALY, "sync");\r
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
868 }\r
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
878 }\r
879 if (skip >= 0)\r
880 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;\r
881}\r
882\r
883PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)\r
884{\r
885 struct PicoVideo *pvid=&Pico.video;\r
886\r
887 //elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x [%u] @ %06x",\r
888 // a, d, SekCyclesDone(), SekPc);\r
889\r
890 a &= 0x1c;\r
891 switch (a)\r
892 {\r
893 case 0x00: // Data port 0 or 2\r
894 if (pvid->pending) {\r
895 CommandChange(pvid);\r
896 pvid->pending=0;\r
897 }\r
898\r
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
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
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
908 // pixel, so sync can be closer to start of actual image data\r
909 PicoVideoSync(InHblank(pvid->type == 3 ? 103 : 30)); // cram in Toy Story\r
910\r
911 if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))\r
912 {\r
913 VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;\r
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
917 pvid->addr, d, SekCyclesDone(), pvid->type, SekPc);\r
918 }\r
919 VideoWrite(d);\r
920\r
921 // start DMA fill on write. NB VSRAM and CRAM fills use wrong FIFO data.\r
922 if (pvid->status & PVS_DMAFILL)\r
923 DmaFill(VdpFIFO.fifo_data[(VdpFIFO.fifo_dx + !!(pvid->type&~0x81))&3]);\r
924\r
925 break;\r
926\r
927 case 0x04: // Control (command) port 4 or 6\r
928 if (pvid->status & SR_DMA)\r
929 SekCyclesBurnRun(PicoVideoFIFORead()); // kludge, flush out running DMA\r
930 if (pvid->pending)\r
931 {\r
932 // Low word of command:\r
933 if (!(pvid->reg[1]&0x10))\r
934 d = (d&~0x80)|(pvid->command&0x80);\r
935 pvid->command &= 0xffff0000;\r
936 pvid->command |= d;\r
937 pvid->pending = 0;\r
938 CommandChange(pvid);\r
939 // Check for dma:\r
940 if (d & 0x80) {\r
941 PicoVideoSync(InHblank(93));\r
942 CommandDma();\r
943 }\r
944 }\r
945 else\r
946 {\r
947 if ((d&0xc000)==0x8000)\r
948 {\r
949 // Register write:\r
950 int num=(d>>8)&0x1f;\r
951 int dold=pvid->reg[num];\r
952 pvid->type=0; // register writes clear command (else no Sega logo in Golden Axe II)\r
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
955 return;\r
956 }\r
957\r
958 d &= 0xff;\r
959\r
960 if (num == 1 && ((pvid->reg[1]^d)&0x40)) {\r
961 // handle line blanking before line rendering. Only the last switch\r
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
969 } else if (((1<<num) & 0x738ff) && pvid->reg[num] != d)\r
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
973\r
974 switch (num)\r
975 {\r
976 case 0x00:\r
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
981 elprintf(EL_INTSW, "hint_onoff: %i->%i [%u] pend=%i @ %06x", (dold&0x10)>>4,\r
982 (d&0x10)>>4, SekCyclesDone(), (pvid->pending_ints&0x10)>>4, SekPc);\r
983 goto update_irq;\r
984 case 0x01:\r
985 if ((d^dold)&0x40)\r
986 PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);\r
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
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
992 goto update_irq;\r
993 case 0x05:\r
994 case 0x06:\r
995 if (d^dold) Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
996 break;\r
997 case 0x0c:\r
998 // renderers should update their palettes if sh/hi mode is changed\r
999 if ((d^dold)&8) Pico.m.dirtyPal = 1;\r
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
1004 break;\r
1005 default:\r
1006 return;\r
1007 }\r
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
1015 return;\r
1016\r
1017update_irq:\r
1018#ifndef EMU_CORE_DEBUG\r
1019 // update IRQ level; TODO hack, still fire irq if disabling now\r
1020 if (!SekShouldInterrupt() || SekIrqLevel < pvid->hint_irq)\r
1021 {\r
1022 int lines, pints, irq = 0;\r
1023 lines = (pvid->reg[1] & 0x20) | (pvid->reg[0] & 0x10);\r
1024 pints = pvid->pending_ints & lines;\r
1025 if (pints & 0x20) irq = 6;\r
1026 else if (pints & 0x10) irq = pvid->hint_irq;\r
1027\r
1028 if (irq) {\r
1029 // VDP irqs have highest prio, just overwrite old level\r
1030 SekInterrupt(irq); // update line\r
1031\r
1032 // TODO this is broken because cost of current insn isn't known here\r
1033 SekEndRun(21); // make it delayed\r
1034 } else if (SekIrqLevel >= pvid->hint_irq) {\r
1035 // no VDP irq, query lower irqs\r
1036 SekInterrupt(PicoIn.AHW & PAHW_PICO ? PicoPicoIrqAck(0) : 0);\r
1037 }\r
1038 }\r
1039#endif\r
1040 }\r
1041 else\r
1042 {\r
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
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
1078 }\r
1079}\r
1080\r
1081static u32 VideoSr(const struct PicoVideo *pv)\r
1082{\r
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
1086 u32 d;\r
1087\r
1088 PicoVideoFIFOSync(c);\r
1089 d = (u16)pv->status;\r
1090\r
1091 if (c - hp < hl)\r
1092 d |= SR_HB;\r
1093\r
1094 if (VdpFIFO.fifo_total >= 4)\r
1095 d |= SR_FULL;\r
1096 else if (!VdpFIFO.fifo_total)\r
1097 d |= SR_EMPT;\r
1098 return d;\r
1099}\r
1100\r
1101PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)\r
1102{\r
1103 struct PicoVideo *pv = &Pico.video;\r
1104 a &= 0x1c;\r
1105\r
1106 if (a == 0x04) // control port\r
1107 {\r
1108 u32 d = VideoSr(pv);\r
1109 if (pv->pending) {\r
1110 CommandChange(pv);\r
1111 pv->pending = 0;\r
1112 }\r
1113 elprintf(EL_SR, "SR read: %04x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
1114 return d;\r
1115 }\r
1116\r
1117 if (a == 0x08)\r
1118 {\r
1119 unsigned int c;\r
1120 u32 d;\r
1121\r
1122 c = SekCyclesDone() - Pico.t.m68c_line_start;\r
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
1126\r
1127 elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc);\r
1128 return d;\r
1129 }\r
1130\r
1131 if (a==0x00) // data port\r
1132 {\r
1133 return VideoRead(0);\r
1134 }\r
1135\r
1136 return PicoRead16_floating(a | 0xc00000);\r
1137}\r
1138\r
1139unsigned char PicoVideoRead8DataH(int is_from_z80)\r
1140{\r
1141 return VideoRead(is_from_z80) >> 8;\r
1142}\r
1143\r
1144unsigned char PicoVideoRead8DataL(int is_from_z80)\r
1145{\r
1146 return VideoRead(is_from_z80);\r
1147}\r
1148\r
1149unsigned char PicoVideoRead8CtlH(int is_from_z80)\r
1150{\r
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
1156 }\r
1157 elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);\r
1158 return d;\r
1159}\r
1160\r
1161unsigned char PicoVideoRead8CtlL(int is_from_z80)\r
1162{\r
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
1168 }\r
1169 elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);\r
1170 return d;\r
1171}\r
1172\r
1173unsigned char PicoVideoRead8HV_H(int is_from_z80)\r
1174{\r
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
1180}\r
1181\r
1182// FIXME: broken\r
1183unsigned char PicoVideoRead8HV_L(int is_from_z80)\r
1184{\r
1185 u32 d = SekCyclesDone() - Pico.t.m68c_line_start;\r
1186 if (Pico.video.reg[0]&2)\r
1187 d = Pico.video.hv_latch;\r
1188 else d = VdpFIFO.fifo_hcounts[d/clkdiv];\r
1189 elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
1190 return d;\r
1191}\r
1192\r
1193void PicoVideoReset(void)\r
1194{\r
1195 Pico.video.pending_ints=0;\r
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
1199\r
1200 memset(&VdpFIFO, 0, sizeof(VdpFIFO));\r
1201 Pico.m.dirtyPal = 1;\r
1202\r
1203 PicoDrawBgcDMA(NULL, 0, 0, 0, 0);\r
1204 PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);\r
1205}\r
1206\r
1207void PicoVideoCacheSAT(int load)\r
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
1218 for (l = 0; load && l < 2*80; l ++) {\r
1219 u16 addr = SATaddr + l*4;\r
1220 ((u16 *)VdpSATCache)[l*2 ] = PicoMem.vram[(addr>>1) ];\r
1221 ((u16 *)VdpSATCache)[l*2 + 1] = PicoMem.vram[(addr>>1) + 1];\r
1222 }\r
1223\r
1224 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
1225}\r
1226\r
1227void PicoVideoSave(void)\r
1228{\r
1229 struct VdpFIFO *vf = &VdpFIFO;\r
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
1234 pv->fifo_cnt = pv->fifo_bgcnt = 0;\r
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
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
1242}\r
1243\r
1244void PicoVideoLoad(void)\r
1245{\r
1246 struct VdpFIFO *vf = &VdpFIFO;\r
1247 struct PicoVideo *pv = &Pico.video;\r
1248 int b = pv->type == 1;\r
1249\r
1250 // convert former dma_xfers (why was this in PicoMisc anyway?)\r
1251 if (Pico.m.dma_xfers) {\r
1252 pv->fifo_cnt = Pico.m.dma_xfers << b;\r
1253 Pico.m.dma_xfers = 0;\r
1254 }\r
1255\r
1256 // fake entries in the FIFO if there are outstanding transfers\r
1257 vf->fifo_ql = vf->fifo_qx = vf->fifo_total = 0;\r
1258 if (pv->fifo_cnt) {\r
1259 int wc = pv->fifo_cnt;\r
1260 vf->fifo_total = (wc+b) >> b;\r
1261 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | b | FQ_FGDMA;\r
1262 vf->fifo_ql ++;\r
1263 if (vf->fifo_total > 4 && !(pv->status & (PVS_CPUWR|PVS_CPURD)))\r
1264 pv->status |= PVS_CPUWR;\r
1265 }\r
1266 if (pv->fifo_bgcnt) {\r
1267 int wc = pv->fifo_bgcnt;\r
1268 if (!vf->fifo_ql)\r
1269 pv->status |= PVS_DMABG;\r
1270 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | FQ_BGDMA;\r
1271 vf->fifo_ql ++;\r
1272 }\r
1273 PicoVideoCacheSAT(1);\r
1274 vf->fifo_maxslot = 0;\r
1275}\r
1276// vim:shiftwidth=2:ts=2:expandtab\r