3 * (c) Copyright Dave, 2004
\r
4 * (C) notaz, 2006-2009
\r
5 * (C) irixxxx, 2020-2024
\r
7 * This work is licensed under the terms of MAME license.
\r
8 * See COPYING file in the top-level directory.
\r
11 #include "pico_int.h"
\r
12 #define NEED_DMA_SOURCE
\r
16 enum { clkdiv = 2 }; // CPU clock granularity: one of 1,2,4,8
\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
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
28 enum { slcpu = 488 };
\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
35 enum { hint32 = 0x85, gapstart32 = 0x94, gapend32 = 0xe9};
\r
36 enum { hint40 = 0xa5, gapstart40 = 0xb7, gapend40 = 0xe5};
\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
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
49 enum { hboff32 = 0x93-hint32, hblen32 = 0xf8-(gapend32-gapstart32)-hint32};//30
\r
50 enum { hboff40 = 0xb4-hint40, hblen40 = 0xf8-(gapend40-gapstart40)-hint40};//37
\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
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
59 // dma and refresh slots for active display, 16 for H32
\r
60 static u8 dmaslots32[] =
\r
61 { 145,243, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 129,130 };
\r
62 static u8 refslots32[] =
\r
63 { 250, 26, 58, 90, 122 };
\r
64 // dma and refresh slots for active display, 18 for H40
\r
65 static u8 dmaslots40[] =
\r
66 { 232, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 130,138,146, 161,162 };
\r
67 static u8 refslots40[] =
\r
68 { 250, 26, 58, 90, 122, 154 };
\r
71 enum { cycsz = slcpu/clkdiv };
\r
72 enum { sl32blsz=slots32-sizeof(refslots32)+1, sl32acsz=sizeof(dmaslots32)+1 };
\r
73 enum { sl40blsz=slots40-sizeof(refslots40)+1, sl40acsz=sizeof(dmaslots40)+1 };
\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
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
82 static u8 hcounts_32[2*cycsz], hcounts_40[2*cycsz];
\r
83 // tables mapping cycles to slots
\r
84 static u16 vdpcyc2sl_32_bl[2*cycsz],vdpcyc2sl_40_bl[2*cycsz];
\r
85 static 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
88 static u16 vdpsl2cyc_32_bl[2*sl32blsz],vdpsl2cyc_40_bl[2*sl40blsz];
\r
89 static u16 vdpsl2cyc_32_ac[2*sl32acsz],vdpsl2cyc_40_ac[2*sl40acsz];
\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
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
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
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
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
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
144 // initialize VDP timing tables
\r
145 void PicoVideoInit(void)
\r
152 static int linedisabled; // display disabled on this line
\r
153 static int lineenabled; // display enabled on this line
\r
154 static int lineoffset; // offset at which dis/enable took place
\r
156 u32 SATaddr, SATmask; // VRAM addr of sprite attribute table
\r
158 int (*PicoDmaHook)(u32 source, int len, unsigned short **base, u32 *mask) = NULL;
\r
161 /* VDP FIFO implementation
\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
168 * FIFO states: empty total=0
\r
169 * inuse total>0 && total<4
\r
173 * fifo_slot is normally behind slot2cyc[cycles]. Advancing it beyond cycles
\r
174 * implies blocking the 68k up to that slot.
\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
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
191 // NB code assumes fifo_* arrays have size 2^n
\r
192 static 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
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
201 unsigned short fifo_slot; // last executed slot in current scanline
\r
202 unsigned short fifo_maxslot;// #slots in scanline
\r
204 const unsigned short *fifo_cyc2sl;
\r
205 const unsigned short *fifo_sl2cyc;
\r
206 const unsigned char *fifo_hcounts;
\r
209 enum { FQ_BYTE = 1, FQ_BGDMA = 2, FQ_FGDMA = 4 }; // queue flags, NB: BYTE = 1!
\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
217 // do the FIFO math
\r
218 static int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, int slots)
\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
224 // advance currently active FIFO entry
\r
227 if (!(*qx & FQ_BGDMA))
\r
228 vf->fifo_total -= ((cnt & b) + l) >> b;
\r
231 // if entry has been processed...
\r
233 // remove entry from FIFO
\r
235 vf->fifo_qx = (vf->fifo_qx+1) & 7;
\r
242 static void SetFIFOState(struct VdpFIFO *vf, struct PicoVideo *pv)
\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
248 if (!(st & (PVS_DMABG|PVS_DMAFILL))) {
\r
253 if (vf->fifo_ql == 0) {
\r
255 // terminate DMA if applicable
\r
256 if (!(st & PVS_DMAFILL)) {
\r
257 st &= ~(SR_DMA|PVS_DMABG);
\r
265 // sync FIFO to cycles
\r
266 void PicoVideoFIFOSync(int cycles)
\r
268 struct VdpFIFO *vf = &VdpFIFO;
\r
269 struct PicoVideo *pv = &Pico.video;
\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
276 // advance FIFO queue by #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
285 SetFIFOState(vf, pv);
\r
288 // drain FIFO, blocking 68k on the way. FIFO must be synced prior to drain.
\r
289 static int PicoVideoFIFODrain(int level, int cycles, int bgdma)
\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
297 if (!(vf->fifo_ql && ((vf->fifo_total > level) | bd))) return 0;
\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
307 if (slot > vf->fifo_maxslot) {
\r
308 // target slot in later scanline, advance to eol
\r
309 slot = vf->fifo_maxslot;
\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
318 if (vf->fifo_ql && ((vf->fifo_total > level) | bd))
\r
319 cycles = slcpu; // not completed in this scanline
\r
321 burn = cycles - ocyc;
\r
323 SetFIFOState(vf, pv);
\r
328 // read VDP data port
\r
329 static int PicoVideoFIFORead(void)
\r
331 struct VdpFIFO *vf = &VdpFIFO;
\r
332 struct PicoVideo *pv = &Pico.video;
\r
333 int lc = SekCyclesDone()-Pico.t.m68c_line_start;
\r
337 // advance FIFO and CPU until FIFO is empty
\r
338 burn = PicoVideoFIFODrain(0, lc, FQ_BGDMA);
\r
343 pv->status |= PVS_CPURD; // target slot is in later scanline
\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
353 // write VDP data port
\r
354 int PicoVideoFIFOWrite(int count, int flags, unsigned sr_mask,unsigned sr_flags)
\r
356 struct VdpFIFO *vf = &VdpFIFO;
\r
357 struct PicoVideo *pv = &Pico.video;
\r
358 int lc = SekCyclesDone()-Pico.t.m68c_line_start;
\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
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
368 pv->status = (pv->status & ~sr_mask) | sr_flags;
\r
369 vf->fifo_total += count * !(flags & FQ_BGDMA);
\r
371 vf->fifo_slot = Cyc2Sl(vf, lc+7); // FIFO latency ~3 vdp slots
\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
379 // create new xfer queue entry
\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
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
395 // at HINT, advance FIFO to new scanline
\r
396 int PicoVideoFIFOHint(void)
\r
398 struct VdpFIFO *vf = &VdpFIFO;
\r
399 struct PicoVideo *pv = &Pico.video;
\r
400 int lc = SekCyclesDone()-Pico.t.m68c_line_start;
\r
403 // reset slot to start of scanline
\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
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
418 // switch FIFO mode between active/inactive display
\r
419 void PicoVideoFIFOMode(int active, int h40)
\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
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
433 if (vf->fifo_maxslot)
\r
434 PicoVideoFIFOSync(lc);
\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
446 // VDP memory rd/wr
\r
448 static __inline void AutoIncrement(void)
\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
455 static NOINLINE void VideoWriteVRAM128(u32 a, u16 d)
\r
458 u32 b = ((a & 2) >> 1) | ((a & 0x400) >> 9) | (a & 0x3FC) | ((a & 0x1F800) >> 1);
\r
460 ((u8 *)PicoMem.vram)[b] = d;
\r
461 if (!(u16)((b^SATaddr) & SATmask))
\r
462 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
\r
464 if (((a^SATaddr) & SATmask) == 0)
\r
468 static void VideoWrite(u16 d)
\r
470 struct PicoVideo *pvid = &Pico.video;
\r
471 unsigned int a = pvid->addr;
\r
473 switch (pvid->type)
\r
476 d = (u16)((d << 8) | (d >> 8));
\r
477 a |= pvid->addr_u << 16;
\r
478 VideoWriteVRAM(a, d);
\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
484 a |= pvid->addr_u << 16;
\r
485 VideoWriteVRAM128(a, d);
\r
487 //default:elprintf(EL_ANOMALY, "VDP write %04x with bad type %i", d, pvid->type); break;
\r
493 static unsigned int VideoRead(int is_from_z80)
\r
495 struct PicoVideo *pvid = &Pico.video;
\r
496 unsigned int a, d = VdpFIFO.fifo_data[(VdpFIFO.fifo_dx+1)&3];
\r
498 a=pvid->addr; a>>=1;
\r
501 SekCyclesBurnRun(PicoVideoFIFORead());
\r
502 switch (pvid->type)
\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
519 static int GetDmaLength(void)
\r
521 struct PicoVideo *pvid=&Pico.video;
\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
530 static void DmaSlow(int len, u32 source)
\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
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
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
549 if ((source & 0xe00000) == 0xe00000) { // Ram
\r
550 base = (u16 *)PicoMem.ram;
\r
553 else if (PicoIn.AHW & PAHW_MCD)
\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
563 if ((source & 0xfe0000) < pcd_base_address+0x220000) { // 1M mode
\r
565 base = (u16 *)(Pico_mcd->word_ram1M[bank]);
\r
567 DmaSlowCell(source - 2, a, len, inc);
\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
579 // if we have DmaHook, let it handle ROM because of possible DMA delay
\r
581 if (PicoDmaHook && (source2 = PicoDmaHook(source, len, &base, &mask)))
\r
584 base = m68k_dma_source(source);
\r
587 elprintf(EL_VDPDMA|EL_ANOMALY, "DmaSlow[%i] %06x->%04x: invalid src", pvid->type, source, a);
\r
591 // operate in words
\r
595 switch (pvid->type)
\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
604 // most used DMA mode
\r
605 memcpy((char *)r + a, base + (source & mask), len * 2);
\r
611 u16 d = base[source++ & mask];
\r
612 if(a & 1) d=(d<<8)|(d>>8);
\r
613 VideoWriteVRAM(a, d);
\r
615 a = (a+inc) & ~0x20000;
\r
620 Pico.m.dirtyPal = 1;
\r
622 if (inc == 0 && !(pvid->reg[1] & 0x40) &&
\r
623 (pvid->reg[7] & 0x3f) == ((a/2) & 0x3f)) { // bg color DMA
\r
625 int sl = VdpFIFO.fifo_hcounts[lc/clkdiv];
\r
626 if (sl > VdpFIFO.fifo_hcounts[0]-5) // hint delay is 5 slots
\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
638 r[(a / 2) & 0x3f] = base[source++ & mask] & 0xeee;
\r
640 a = (a+inc) & ~0x20000;
\r
648 r[(a / 2) & 0x3f] = base[source++ & mask] & 0x7ff;
\r
650 a = (a+inc) & ~0x20000;
\r
654 case 0x81: // vram 128k
\r
657 u16 d = base[source++ & mask];
\r
658 VideoWriteVRAM128(a, d);
\r
660 a = (a+inc) & ~0x20000;
\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
671 pvid->addr_u = a >> 16;
\r
674 static void DmaCopy(int len)
\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
681 elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());
\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
687 source =pvid->reg[0x15];
\r
688 source|=pvid->reg[0x16]<<8;
\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
696 a = (a+inc) & ~0x20000;
\r
700 pvid->addr_u = a >> 16;
\r
703 static NOINLINE void DmaFill(int data)
\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
713 len = GetDmaLength();
\r
714 elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());
\r
716 SekCyclesBurnRun(PicoVideoFIFOWrite(len, FQ_BGDMA, // 1 slot each (wr)
\r
717 PVS_CPUWR | PVS_DMAFILL, SR_DMA | PVS_DMABG));
\r
719 switch (pvid->type)
\r
723 if (inc == 1 && !((a ^ e) >> 16) &&
\r
724 ((a >= SATaddr + 0x280) | (e < SATaddr)))
\r
726 // most used DMA mode
\r
727 memset(vr + (u16)a, high, len);
\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
735 if (((a^SATaddr) & SATmask) == 0)
\r
736 UpdateSAT(a, ((u16 *)vr)[(u16)a >> 1]);
\r
738 // Increment address register
\r
739 a = (a+inc) & ~0x20000;
\r
743 Pico.m.dirtyPal = 1;
\r
745 for (l = len; l; l--) {
\r
746 PicoMem.cram[(a/2) & 0x3f] = data;
\r
748 // Increment address register
\r
749 a = (a+inc) & ~0x20000;
\r
754 for (l = len; l; l--) {
\r
755 PicoMem.vsram[(a/2) & 0x3f] = data;
\r
757 // Increment address register
\r
758 a = (a+inc) & ~0x20000;
\r
762 case 0x81: // vram 128k
\r
763 for (l = len; l; l--) {
\r
764 VideoWriteVRAM128(a, data);
\r
766 // Increment address register
\r
767 a = (a+inc) & ~0x20000;
\r
777 pvid->addr_u = a >> 16;
\r
779 pvid->reg[0x13] = pvid->reg[0x14] = 0;
\r
780 source = pvid->reg[0x15];
\r
781 source |= pvid->reg[0x16] << 8;
\r
783 pvid->reg[0x15] = source;
\r
784 pvid->reg[0x16] = source >> 8;
\r
787 // VDP command handling
\r
789 static NOINLINE void CommandDma(void)
\r
791 struct PicoVideo *pvid = &Pico.video;
\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
803 len = GetDmaLength();
\r
804 source = pvid->reg[0x15];
\r
805 source |= pvid->reg[0x16] << 8;
\r
806 source |= pvid->reg[0x17] << 16;
\r
808 method=pvid->reg[0x17]>>6;
\r
810 DmaSlow(len, source << 1); // 68000 to VDP
\r
811 else if (method == 3)
\r
812 DmaCopy(len); // VRAM Copy
\r
814 pvid->status |= SR_DMA|PVS_DMAFILL;
\r
818 pvid->reg[0x13] = pvid->reg[0x14] = 0;
\r
819 pvid->reg[0x15] = source;
\r
820 pvid->reg[0x16] = source >> 8;
\r
823 static NOINLINE void CommandChange(struct PicoVideo *pvid)
\r
825 unsigned int cmd, addr;
\r
827 cmd = pvid->command;
\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
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
843 static inline int InHblank(int offs)
\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
849 void PicoVideoSync(int skip)
\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
855 if (!(PicoIn.opt & POPT_ALT_RENDERER) && !PicoIn.skipFrame) {
\r
858 else // in active display, need to sync next frame as well
\r
859 Pico.est.rendstatus |= PDRAW_SYNC_NEXT;
\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
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
876 if (Pico.est.DrawScanline <= last)
\r
877 PicoDrawSync(last, 0, 0);
\r
880 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;
\r
883 PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
\r
885 struct PicoVideo *pvid=&Pico.video;
\r
887 //elprintf(EL_STATUS, "PicoVideoWrite [%06x] %04x [%u] @ %06x",
\r
888 // a, d, SekCyclesDone(), SekPc);
\r
893 case 0x00: // Data port 0 or 2
\r
894 if (pvid->pending) {
\r
895 CommandChange(pvid);
\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
911 if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))
\r
913 VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;
\r
914 SekCyclesBurnRun(PicoVideoFIFOWrite(1, pvid->type == 1, 0, PVS_CPUWR));
\r
916 elprintf(EL_ASVDP, "VDP data write: [%04x] %04x [%u] {%i} @ %06x",
\r
917 pvid->addr, d, SekCyclesDone(), pvid->type, SekPc);
\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
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
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
938 CommandChange(pvid);
\r
941 PicoVideoSync(InHblank(93));
\r
947 if ((d&0xc000)==0x8000)
\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
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
978 unsigned c = SekCyclesDone() - Pico.t.m68c_line_start;
\r
979 pvid->hv_latch = VdpFIFO.fifo_hcounts[c/clkdiv] | (pvid->v_counter << 8);
\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
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
995 if (d^dold) Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
\r
998 // renderers should update their palettes if sh/hi mode is changed
\r
999 if ((d^dold)&8) Pico.m.dirtyPal = 1;
\r
1001 PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);
\r
1002 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
\r
1008 if (Pico.est.rendstatus & PDRAW_DIRTY_SPRITES) {
\r
1009 SATaddr = ((pvid->reg[5]&0x7f) << 9) | ((pvid->reg[6]&0x20) << 11);
\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
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
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
1029 // VDP irqs have highest prio, just overwrite old level
\r
1030 SekInterrupt(irq); // update line
\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
1043 // High word of command:
\r
1044 pvid->command&=0x0000ffff;
\r
1045 pvid->command|=d<<16;
\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
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
1063 switch ((d >> 7) & 3) {
\r
1065 pvid->debug_p &= ~(PVD_KILL_S_LO | PVD_KILL_S_HI);
\r
1066 pvid->debug_p |= PVD_FORCE_S;
\r
1069 pvid->debug_p &= ~PVD_KILL_A;
\r
1070 pvid->debug_p |= PVD_FORCE_A;
\r
1073 pvid->debug_p &= ~PVD_KILL_B;
\r
1074 pvid->debug_p |= PVD_FORCE_B;
\r
1081 static u32 VideoSr(const struct PicoVideo *pv)
\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
1088 PicoVideoFIFOSync(c);
\r
1089 d = (u16)pv->status;
\r
1094 if (VdpFIFO.fifo_total >= 4)
\r
1096 else if (!VdpFIFO.fifo_total)
\r
1101 PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)
\r
1103 struct PicoVideo *pv = &Pico.video;
\r
1106 if (a == 0x04) // control port
\r
1108 u32 d = VideoSr(pv);
\r
1109 if (pv->pending) {
\r
1110 CommandChange(pv);
\r
1113 elprintf(EL_SR, "SR read: %04x [%u] @ %06x", d, SekCyclesDone(), SekPc);
\r
1122 c = SekCyclesDone() - Pico.t.m68c_line_start;
\r
1125 else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8);
\r
1127 elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc);
\r
1131 if (a==0x00) // data port
\r
1133 return VideoRead(0);
\r
1136 return PicoRead16_floating(a | 0xc00000);
\r
1139 unsigned char PicoVideoRead8DataH(int is_from_z80)
\r
1141 return VideoRead(is_from_z80) >> 8;
\r
1144 unsigned char PicoVideoRead8DataL(int is_from_z80)
\r
1146 return VideoRead(is_from_z80);
\r
1149 unsigned char PicoVideoRead8CtlH(int is_from_z80)
\r
1151 struct PicoVideo *pv = &Pico.video;
\r
1152 u8 d = VideoSr(pv) >> 8;
\r
1153 if (pv->pending) {
\r
1154 CommandChange(pv);
\r
1157 elprintf(EL_SR, "SR read (h): %02x @ %06x", d, SekPc);
\r
1161 unsigned char PicoVideoRead8CtlL(int is_from_z80)
\r
1163 struct PicoVideo *pv = &Pico.video;
\r
1164 u8 d = VideoSr(pv);
\r
1165 if (pv->pending) {
\r
1166 CommandChange(pv);
\r
1169 elprintf(EL_SR, "SR read (l): %02x @ %06x", d, SekPc);
\r
1173 unsigned char PicoVideoRead8HV_H(int is_from_z80)
\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
1183 unsigned char PicoVideoRead8HV_L(int is_from_z80)
\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
1193 void PicoVideoReset(void)
\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
1200 memset(&VdpFIFO, 0, sizeof(VdpFIFO));
\r
1201 Pico.m.dirtyPal = 1;
\r
1203 PicoDrawBgcDMA(NULL, 0, 0, 0, 0);
\r
1204 PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);
\r
1207 void PicoVideoCacheSAT(int load)
\r
1209 struct PicoVideo *pv = &Pico.video;
\r
1212 SATaddr = ((pv->reg[5]&0x7f) << 9) | ((pv->reg[6]&0x20) << 11);
\r
1214 if (pv->reg[12]&1)
\r
1215 SATaddr &= ~0x200, SATmask &= ~0x200; // H40, zero lowest SAT bit
\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
1224 Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
\r
1227 void PicoVideoSave(void)
\r
1229 struct VdpFIFO *vf = &VdpFIFO;
\r
1230 struct PicoVideo *pv = &Pico.video;
\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
1240 pv->fifo_cnt += cnt;
\r
1244 void PicoVideoLoad(void)
\r
1246 struct VdpFIFO *vf = &VdpFIFO;
\r
1247 struct PicoVideo *pv = &Pico.video;
\r
1248 int b = pv->type == 1;
\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
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
1263 if (vf->fifo_total > 4 && !(pv->status & (PVS_CPUWR|PVS_CPURD)))
\r
1264 pv->status |= PVS_CPUWR;
\r
1266 if (pv->fifo_bgcnt) {
\r
1267 int wc = pv->fifo_bgcnt;
\r
1269 pv->status |= PVS_DMABG;
\r
1270 vf->fifo_queue[vf->fifo_qx + vf->fifo_ql] = (wc << 3) | FQ_BGDMA;
\r
1273 PicoVideoCacheSAT(1);
\r
1274 vf->fifo_maxslot = 0;
\r
1276 // vim:shiftwidth=2:ts=2:expandtab
\r