cff531af |
1 | /* |
2 | * PicoDrive |
3 | * (C) notaz, 2009,2010 |
7bf552b5 |
4 | * (C) irixxxx, 2019-2024 |
cff531af |
5 | * |
6 | * This work is licensed under the terms of MAME license. |
7 | * See COPYING file in the top-level directory. |
8 | */ |
974fdb5b |
9 | #include "../pico_int.h" |
10 | |
52e4a905 |
11 | // NB: 32X officially doesn't support H32 mode. However, it does work since the |
12 | // cartridge slot carries the EDCLK signal which is always H40 clock and is used |
83416730 |
13 | // as video clock by the 32X. The H32 MD image is overlaid with the 320 px 32X |
52e4a905 |
14 | // image which has the same on-screen width. How the /YS signal on the cartridge |
15 | // slot (signalling the display of background color) is processed in this case |
16 | // is however unclear and might lead to glitches due to race conditions by the |
17 | // different video clocks for H32 and H40. |
e44277d0 |
18 | // NB: there is an offset of 4 pixels between MD and 32X layers in H32 mode. |
19 | #define H32_OFFSET 4 |
52e4a905 |
20 | |
16b11d91 |
21 | // BGR555 to native conversion |
22 | #if defined(USE_BGR555) |
a95da16a |
23 | #define PXCONV(t) ((t)&(mr|mg|mb|mp)) |
16b11d91 |
24 | #define PXPRIO 0x8000 // prio in MSB |
cdc6aac4 |
25 | #elif defined(USE_BGR565) |
bded848c |
26 | #define PXCONV(t) (((t)&mr) | (((t)&(mg|mb)) << 1) | (((t)&mp) >> 10)) |
cdc6aac4 |
27 | #define PXPRIO 0x0020 // prio in LS green bit |
16b11d91 |
28 | #else // RGB565 |
bded848c |
29 | #define PXCONV(t) ((((t)&mr) << 11) | (((t)&mg) << 1) | (((t)&(mp|mb)) >> 10)) |
16b11d91 |
30 | #define PXPRIO 0x0020 // prio in LS green bit |
31 | #endif |
32 | |
5a681086 |
33 | int (*PicoScan32xBegin)(unsigned int num); |
34 | int (*PicoScan32xEnd)(unsigned int num); |
35 | int Pico32xDrawMode; |
36 | |
b1a047c9 |
37 | void *DrawLineDestBase32x; |
38 | int DrawLineDestIncrement32x; |
39 | |
5e49c3a8 |
40 | static void convert_pal555(int invert_prio) |
974fdb5b |
41 | { |
f821bb70 |
42 | u32 *ps = (void *)Pico32xMem->pal; |
43 | u32 *pd = (void *)Pico32xMem->pal_native; |
bded848c |
44 | u32 mr = 0x001f001f; // masks for red, green, blue, prio |
45 | u32 mg = 0x03e003e0; |
46 | u32 mb = 0x7c007c00; |
47 | u32 mp = 0x80008000; |
f821bb70 |
48 | u32 inv = 0; |
974fdb5b |
49 | int i; |
50 | |
5e49c3a8 |
51 | if (invert_prio) |
16b11d91 |
52 | inv = 0x80008000; |
5e49c3a8 |
53 | |
974fdb5b |
54 | for (i = 0x100/2; i > 0; i--, ps++, pd++) { |
f821bb70 |
55 | u32 t = *ps ^ inv; |
16b11d91 |
56 | *pd = PXCONV(t); |
974fdb5b |
57 | } |
58 | |
59 | Pico32x.dirty_pal = 0; |
60 | } |
61 | |
5a681086 |
62 | // direct color mode |
63 | #define do_line_dc(pd, p32x, pmd, inv, pmd_draw_code) \ |
64 | { \ |
bded848c |
65 | const u16 mr = 0x001f; \ |
66 | const u16 mg = 0x03e0; \ |
67 | const u16 mb = 0x7c00; \ |
68 | const u16 mp = 0x0000; \ |
f740428b |
69 | unsigned short t; \ |
70 | int i = 320; \ |
5a681086 |
71 | \ |
f740428b |
72 | while (i > 0) { \ |
73 | for (; i > 0 && (*pmd & 0x3f) == mdbg; pd++, pmd++, i--) { \ |
74 | t = *p32x++; \ |
16b11d91 |
75 | *pd = PXCONV(t); \ |
f740428b |
76 | } \ |
77 | for (; i > 0 && (*pmd & 0x3f) != mdbg; pd++, pmd++, i--) { \ |
16b11d91 |
78 | t = *p32x++ ^ inv; \ |
79 | if (t & 0x8000) \ |
80 | *pd = PXCONV(t); \ |
f740428b |
81 | else \ |
82 | pmd_draw_code; \ |
5a681086 |
83 | } \ |
5a681086 |
84 | } \ |
85 | } |
86 | |
87 | // packed pixel mode |
88 | #define do_line_pp(pd, p32x, pmd, pmd_draw_code) \ |
89 | { \ |
90 | unsigned short t; \ |
f740428b |
91 | int i = 320; \ |
92 | while (i > 0) { \ |
93 | for (; i > 0 && (*pmd & 0x3f) == mdbg; pd++, pmd++, i--) { \ |
57c5a5e5 |
94 | t = pal[*(unsigned char *)(MEM_BE2((uintptr_t)(p32x++)))]; \ |
5a681086 |
95 | *pd = t; \ |
f740428b |
96 | } \ |
97 | for (; i > 0 && (*pmd & 0x3f) != mdbg; pd++, pmd++, i--) { \ |
57c5a5e5 |
98 | t = pal[*(unsigned char *)(MEM_BE2((uintptr_t)(p32x++)))]; \ |
16b11d91 |
99 | if (t & PXPRIO) \ |
f740428b |
100 | *pd = t; \ |
101 | else \ |
102 | pmd_draw_code; \ |
103 | } \ |
5a681086 |
104 | } \ |
f740428b |
105 | } |
5a681086 |
106 | |
107 | // run length mode |
108 | #define do_line_rl(pd, p32x, pmd, pmd_draw_code) \ |
109 | { \ |
110 | unsigned short len, t; \ |
111 | int i; \ |
112 | for (i = 320; i > 0; p32x++) { \ |
113 | t = pal[*p32x & 0xff]; \ |
114 | for (len = (*p32x >> 8) + 1; len > 0 && i > 0; len--, i--, pd++, pmd++) { \ |
16b11d91 |
115 | if ((*pmd & 0x3f) == mdbg || (t & PXPRIO)) \ |
5a681086 |
116 | *pd = t; \ |
117 | else \ |
118 | pmd_draw_code; \ |
119 | } \ |
120 | } \ |
121 | } |
122 | |
e44277d0 |
123 | #define MD_LAYER_CODE_H32 \ |
124 | *dst = dst[H32_OFFSET] |
125 | |
41946d70 |
126 | // this is almost never used (Wiz and menu bg gen only) |
ea38612f |
127 | void FinalizeLine32xRGB555(int sh, int line, struct PicoEState *est) |
974fdb5b |
128 | { |
e44277d0 |
129 | unsigned short *dst = est->DrawLineDest; |
974fdb5b |
130 | unsigned short *pal = Pico32xMem->pal_native; |
99bdfd31 |
131 | unsigned char *pmd = est->HighCol + 8; |
e44277d0 |
132 | unsigned short *palmd = est->HighPal; |
5a681086 |
133 | unsigned short *dram, *p32x; |
134 | unsigned char mdbg; |
e44277d0 |
135 | int h32 = !(Pico.video.reg[12] & 0x1); |
974fdb5b |
136 | |
ea38612f |
137 | FinalizeLine555(sh, line, est); |
c987bb5c |
138 | |
5a681086 |
139 | if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 0 || // 32x blanking |
e0bcb7a9 |
140 | (Pico.video.debug_p & PVD_KILL_32X)) |
5a681086 |
141 | { |
5e49c3a8 |
142 | return; |
5a681086 |
143 | } |
5e49c3a8 |
144 | |
266c6afa |
145 | dram = (void *)Pico32xMem->dram[Pico32x.vdp_regs[0x0a/2] & P32XV_FS]; |
5a681086 |
146 | p32x = dram + dram[line]; |
147 | mdbg = Pico.video.reg[7] & 0x3f; |
e44277d0 |
148 | if (h32) pmd += H32_OFFSET; |
266c6afa |
149 | |
150 | if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 2) { // Direct Color Mode |
5a681086 |
151 | int inv_bit = (Pico32x.vdp_regs[0] & P32XV_PRI) ? 0x8000 : 0; |
e44277d0 |
152 | if (h32) { |
153 | do_line_dc(dst, p32x, pmd, inv_bit, MD_LAYER_CODE_H32); |
154 | } else |
155 | do_line_dc(dst, p32x, pmd, inv_bit,); |
266c6afa |
156 | return; |
157 | } |
158 | |
974fdb5b |
159 | if (Pico32x.dirty_pal) |
5e49c3a8 |
160 | convert_pal555(Pico32x.vdp_regs[0] & P32XV_PRI); |
974fdb5b |
161 | |
266c6afa |
162 | if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 1) { // Packed Pixel Mode |
e51e5983 |
163 | unsigned char *p32xb = (void *)p32x; |
164 | if (Pico32x.vdp_regs[2 / 2] & P32XV_SFT) |
165 | p32xb++; |
e44277d0 |
166 | if (h32) { |
167 | do_line_pp(dst, p32xb, pmd, MD_LAYER_CODE_H32); |
168 | } else |
169 | do_line_pp(dst, p32xb, pmd,); |
974fdb5b |
170 | } |
266c6afa |
171 | else { // Run Length Mode |
e44277d0 |
172 | if (h32) { |
173 | do_line_rl(dst, p32x, pmd, MD_LAYER_CODE_H32); |
174 | } else |
175 | do_line_rl(dst, p32x, pmd,); |
5a681086 |
176 | } |
177 | } |
178 | |
179 | #define MD_LAYER_CODE \ |
180 | *dst = palmd[*pmd] |
181 | |
182 | #define PICOSCAN_PRE \ |
e51e5983 |
183 | PicoScan32xBegin(l + (lines_sft_offs & 0xff)); \ |
99bdfd31 |
184 | dst = Pico.est.DrawLineDest; \ |
5a681086 |
185 | |
186 | #define PICOSCAN_POST \ |
e51e5983 |
187 | PicoScan32xEnd(l + (lines_sft_offs & 0xff)); \ |
d5d17782 |
188 | Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement32x; \ |
5a681086 |
189 | |
190 | #define make_do_loop(name, pre_code, post_code, md_code) \ |
191 | /* Direct Color Mode */ \ |
192 | static void do_loop_dc##name(unsigned short *dst, \ |
2322260c |
193 | unsigned short *dram, unsigned lines_sft_offs, int mdbg) \ |
5a681086 |
194 | { \ |
195 | int inv_bit = (Pico32x.vdp_regs[0] & P32XV_PRI) ? 0x8000 : 0; \ |
98a27142 |
196 | unsigned char *pmd = Pico.est.Draw2FB + \ |
7a961c19 |
197 | 328 * (lines_sft_offs & 0xff) + 8; \ |
98a27142 |
198 | unsigned short *palmd = Pico.est.HighPal; \ |
5a681086 |
199 | unsigned short *p32x; \ |
2322260c |
200 | int lines = (lines_sft_offs >> 16) & 0xff; \ |
5a681086 |
201 | int l; \ |
e44277d0 |
202 | if (lines_sft_offs & (2<<8)) pmd += H32_OFFSET; \ |
5a681086 |
203 | (void)palmd; \ |
204 | for (l = 0; l < lines; l++, pmd += 8) { \ |
205 | pre_code; \ |
2322260c |
206 | p32x = dram + dram[l + (lines_sft_offs >> 24)]; \ |
5a681086 |
207 | do_line_dc(dst, p32x, pmd, inv_bit, md_code); \ |
208 | post_code; \ |
aa97a0a0 |
209 | dst += DrawLineDestIncrement32x/2 - 320; \ |
5a681086 |
210 | } \ |
211 | } \ |
212 | \ |
213 | /* Packed Pixel Mode */ \ |
214 | static void do_loop_pp##name(unsigned short *dst, \ |
2322260c |
215 | unsigned short *dram, unsigned lines_sft_offs, int mdbg) \ |
5a681086 |
216 | { \ |
217 | unsigned short *pal = Pico32xMem->pal_native; \ |
98a27142 |
218 | unsigned char *pmd = Pico.est.Draw2FB + \ |
7a961c19 |
219 | 328 * (lines_sft_offs & 0xff) + 8; \ |
98a27142 |
220 | unsigned short *palmd = Pico.est.HighPal; \ |
e51e5983 |
221 | unsigned char *p32x; \ |
2322260c |
222 | int lines = (lines_sft_offs >> 16) & 0xff; \ |
5a681086 |
223 | int l; \ |
e44277d0 |
224 | if (lines_sft_offs & (2<<8)) pmd += H32_OFFSET; \ |
5a681086 |
225 | (void)palmd; \ |
226 | for (l = 0; l < lines; l++, pmd += 8) { \ |
227 | pre_code; \ |
2322260c |
228 | p32x = (void *)(dram + dram[l + (lines_sft_offs >> 24)]); \ |
e51e5983 |
229 | p32x += (lines_sft_offs >> 8) & 1; \ |
5a681086 |
230 | do_line_pp(dst, p32x, pmd, md_code); \ |
231 | post_code; \ |
aa97a0a0 |
232 | dst += DrawLineDestIncrement32x/2 - 320; \ |
5a681086 |
233 | } \ |
234 | } \ |
235 | \ |
236 | /* Run Length Mode */ \ |
237 | static void do_loop_rl##name(unsigned short *dst, \ |
2322260c |
238 | unsigned short *dram, unsigned lines_sft_offs, int mdbg) \ |
5a681086 |
239 | { \ |
240 | unsigned short *pal = Pico32xMem->pal_native; \ |
98a27142 |
241 | unsigned char *pmd = Pico.est.Draw2FB + \ |
7a961c19 |
242 | 328 * (lines_sft_offs & 0xff) + 8; \ |
98a27142 |
243 | unsigned short *palmd = Pico.est.HighPal; \ |
5a681086 |
244 | unsigned short *p32x; \ |
2322260c |
245 | int lines = (lines_sft_offs >> 16) & 0xff; \ |
5a681086 |
246 | int l; \ |
e44277d0 |
247 | if (lines_sft_offs & (2<<8)) pmd += H32_OFFSET; \ |
5a681086 |
248 | (void)palmd; \ |
249 | for (l = 0; l < lines; l++, pmd += 8) { \ |
250 | pre_code; \ |
2322260c |
251 | p32x = dram + dram[l + (lines_sft_offs >> 24)]; \ |
5a681086 |
252 | do_line_rl(dst, p32x, pmd, md_code); \ |
253 | post_code; \ |
aa97a0a0 |
254 | dst += DrawLineDestIncrement32x/2 - 320; \ |
5a681086 |
255 | } \ |
256 | } |
257 | |
258 | #ifdef _ASM_32X_DRAW |
259 | #undef make_do_loop |
260 | #define make_do_loop(name, pre_code, post_code, md_code) \ |
261 | extern void do_loop_dc##name(unsigned short *dst, \ |
2322260c |
262 | unsigned short *dram, unsigned lines_offs, int mdbg);\ |
5a681086 |
263 | extern void do_loop_pp##name(unsigned short *dst, \ |
2322260c |
264 | unsigned short *dram, unsigned lines_offs, int mdbg);\ |
5a681086 |
265 | extern void do_loop_rl##name(unsigned short *dst, \ |
2322260c |
266 | unsigned short *dram, unsigned lines_offs, int mdbg); |
5a681086 |
267 | #endif |
268 | |
269 | make_do_loop(,,,) |
270 | make_do_loop(_md, , , MD_LAYER_CODE) |
e44277d0 |
271 | make_do_loop(_h32, , , MD_LAYER_CODE_H32) |
5a681086 |
272 | make_do_loop(_scan, PICOSCAN_PRE, PICOSCAN_POST, ) |
e44277d0 |
273 | make_do_loop(_scan_h32, PICOSCAN_PRE, PICOSCAN_POST, MD_LAYER_CODE_H32) |
5a681086 |
274 | make_do_loop(_scan_md, PICOSCAN_PRE, PICOSCAN_POST, MD_LAYER_CODE) |
275 | |
2322260c |
276 | typedef void (*do_loop_func)(unsigned short *dst, unsigned short *dram, unsigned lines, int mdbg); |
e44277d0 |
277 | enum { DO_LOOP, DO_LOOP_H32, DO_LOOP_MD, DO_LOOP_SCAN, DO_LOOP_H32_SCAN, DO_LOOP_MD_SCAN }; |
5a681086 |
278 | |
e44277d0 |
279 | static const do_loop_func do_loop_dc_f[] = { do_loop_dc, do_loop_dc_h32, do_loop_dc_md, do_loop_dc_scan, do_loop_dc_scan_h32, do_loop_dc_scan_md }; |
280 | static const do_loop_func do_loop_pp_f[] = { do_loop_pp, do_loop_pp_h32, do_loop_pp_md, do_loop_pp_scan, do_loop_pp_scan_h32, do_loop_pp_scan_md }; |
281 | static const do_loop_func do_loop_rl_f[] = { do_loop_rl, do_loop_rl_h32, do_loop_rl_md, do_loop_rl_scan, do_loop_rl_scan_h32, do_loop_rl_scan_md }; |
5a681086 |
282 | |
283 | void PicoDraw32xLayer(int offs, int lines, int md_bg) |
284 | { |
285 | int have_scan = PicoScan32xBegin != NULL && PicoScan32xEnd != NULL; |
286 | const do_loop_func *do_loop; |
287 | unsigned short *dram; |
e51e5983 |
288 | int lines_sft_offs; |
5a681086 |
289 | int which_func; |
290 | |
2322260c |
291 | offs += Pico32x.sync_line; |
292 | |
b1a047c9 |
293 | Pico.est.DrawLineDest = (char *)DrawLineDestBase32x + offs * DrawLineDestIncrement32x; |
d5d17782 |
294 | Pico.est.DrawLineDestIncr = DrawLineDestIncrement32x; |
5a681086 |
295 | dram = Pico32xMem->dram[Pico32x.vdp_regs[0x0a/2] & P32XV_FS]; |
296 | |
b1a047c9 |
297 | if (Pico32xDrawMode == PDM32X_BOTH) |
298 | PicoDrawUpdateHighPal(); |
5a681086 |
299 | |
300 | if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 2) |
301 | { |
302 | // Direct Color Mode |
303 | do_loop = do_loop_dc_f; |
304 | goto do_it; |
305 | } |
306 | |
307 | if (Pico32x.dirty_pal) |
308 | convert_pal555(Pico32x.vdp_regs[0] & P32XV_PRI); |
309 | |
310 | if ((Pico32x.vdp_regs[0] & P32XV_Mx) == 1) |
311 | { |
312 | // Packed Pixel Mode |
313 | do_loop = do_loop_pp_f; |
314 | } |
315 | else |
316 | { |
317 | // Run Length Mode |
318 | do_loop = do_loop_rl_f; |
319 | } |
320 | |
321 | do_it: |
e44277d0 |
322 | // In 8bit modes MD+32X layers are merged together in 32X rendering, while in |
323 | // 16bit mode the MD layer is directly created in the target buffer and the |
324 | // 32X layer is overlaid onto that. |
7a961c19 |
325 | if (Pico32xDrawMode == PDM32X_BOTH) |
5a681086 |
326 | which_func = have_scan ? DO_LOOP_MD_SCAN : DO_LOOP_MD; |
e44277d0 |
327 | else if (!(Pico.video.reg[12] & 1)) // H32, mind 4 px offset |
328 | which_func = have_scan ? DO_LOOP_H32_SCAN : DO_LOOP_H32; |
5a681086 |
329 | else |
330 | which_func = have_scan ? DO_LOOP_SCAN : DO_LOOP; |
2322260c |
331 | lines_sft_offs = (Pico32x.sync_line << 24) | (lines << 16) | offs; |
e51e5983 |
332 | if (Pico32x.vdp_regs[2 / 2] & P32XV_SFT) |
333 | lines_sft_offs |= 1 << 8; |
e44277d0 |
334 | if (!(Pico.video.reg[12] & 1)) // offset flag for H32 |
335 | lines_sft_offs |= 2 << 8; |
5a681086 |
336 | |
99bdfd31 |
337 | do_loop[which_func](Pico.est.DrawLineDest, dram, lines_sft_offs, md_bg); |
5a681086 |
338 | } |
339 | |
7a961c19 |
340 | // mostly unused, games tend to keep 32X layer on |
341 | void PicoDraw32xLayerMdOnly(int offs, int lines) |
342 | { |
343 | int have_scan = PicoScan32xBegin != NULL && PicoScan32xEnd != NULL; |
b1a047c9 |
344 | unsigned short *dst = (void *)((char *)DrawLineDestBase32x + offs * DrawLineDestIncrement32x); |
98a27142 |
345 | unsigned char *pmd = Pico.est.Draw2FB + 328 * offs + 8; |
346 | unsigned short *pal = Pico.est.HighPal; |
2322260c |
347 | int plen = 320; |
7a961c19 |
348 | int l, p; |
349 | |
b1a047c9 |
350 | PicoDrawUpdateHighPal(); |
7a961c19 |
351 | |
2322260c |
352 | offs += Pico32x.sync_line; |
353 | dst += Pico32x.sync_line * DrawLineDestIncrement32x; |
354 | |
7a961c19 |
355 | for (l = 0; l < lines; l++) { |
356 | if (have_scan) { |
357 | PicoScan32xBegin(l + offs); |
2322260c |
358 | dst = (unsigned short *)Pico.est.DrawLineDest; |
7a961c19 |
359 | } |
360 | for (p = 0; p < plen; p += 4) { |
361 | dst[p + 0] = pal[*pmd++]; |
362 | dst[p + 1] = pal[*pmd++]; |
363 | dst[p + 2] = pal[*pmd++]; |
364 | dst[p + 3] = pal[*pmd++]; |
365 | } |
d5d17782 |
366 | dst = Pico.est.DrawLineDest = (char *)dst + DrawLineDestIncrement32x; |
7a961c19 |
367 | pmd += 328 - plen; |
368 | if (have_scan) |
369 | PicoScan32xEnd(l + offs); |
370 | } |
371 | } |
372 | |
41946d70 |
373 | void PicoDrawSetOutFormat32x(pdso_t which, int use_32x_line_mode) |
5a681086 |
374 | { |
b1a047c9 |
375 | if (which == PDF_RGB555) { |
bded848c |
376 | // CLUT pixels needed as well, for layer priority |
b1a047c9 |
377 | PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); |
aa97a0a0 |
378 | PicoDrawSetOutBufMD(NULL, 0); |
b1a047c9 |
379 | } else { |
bded848c |
380 | // store CLUT pixels, same layout as alt renderer |
5a681086 |
381 | PicoDrawSetInternalBuf(NULL, 0); |
627648e4 |
382 | PicoDrawSetOutBufMD(Pico.est.Draw2FB, 328); |
266c6afa |
383 | } |
41946d70 |
384 | |
b1a047c9 |
385 | if (use_32x_line_mode) |
386 | // we'll draw via FinalizeLine32xRGB555 (rare) |
387 | Pico32xDrawMode = PDM32X_OFF; |
388 | else |
e44277d0 |
389 | // in RGB555 mode the 32x layer is overlaid on the MD layer, in the other |
b1a047c9 |
390 | // modes 32x and MD layer are merged together by the 32x renderer |
391 | Pico32xDrawMode = (which == PDF_RGB555) ? PDM32X_32X_ONLY : PDM32X_BOTH; |
392 | } |
393 | |
394 | void PicoDrawSetOutBuf32X(void *dest, int increment) |
395 | { |
396 | DrawLineDestBase32x = dest; |
397 | DrawLineDestIncrement32x = increment; |
398 | // in RGB555 mode this buffer is also used by the MD renderer |
399 | if (Pico32xDrawMode != PDM32X_BOTH) |
400 | PicoDrawSetOutBufMD(DrawLineDestBase32x, DrawLineDestIncrement32x); |
974fdb5b |
401 | } |
402 | |
41946d70 |
403 | // vim:shiftwidth=2:ts=2:expandtab |