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