cff531af |
1 | /* |
0df7401c |
2 | * SMS renderer |
cff531af |
3 | * (C) notaz, 2009-2010 |
7bf552b5 |
4 | * (C) irixxxx, 2020-2024 |
0df7401c |
5 | * |
22917adc |
6 | * currently supports VDP mode 4 (SMS and GG) and mode 3-0 (TMS) |
7 | * modes numbered after the bit numbers used in Sega and TI documentation |
cff531af |
8 | * |
9 | * This work is licensed under the terms of MAME license. |
10 | * See COPYING file in the top-level directory. |
11 | */ |
200772b7 |
12 | #include "pico_int.h" |
d1ae0810 |
13 | #include <platform/common/upscale.h> |
200772b7 |
14 | |
0df7401c |
15 | static void (*FinalizeLineSMS)(int line); |
200772b7 |
16 | static int skip_next_line; |
35247900 |
17 | static int screen_offset, line_offset; |
1603058a |
18 | static u8 mode; |
0df7401c |
19 | |
1ce2b092 |
20 | static unsigned int sprites_addr[32]; // bitmap address |
21 | static unsigned char sprites_c[32]; // TMS sprites color |
22 | static int sprites_x[32]; // x position |
23 | static int sprites; // count |
24 | static unsigned char sprites_map[2+256/8+2]; // collision detection map |
25 | |
26 | unsigned int sprites_status; |
27 | |
2db0d004 |
28 | int sprites_zoom; // latched sprite zoom flag |
2db0d004 |
29 | int xscroll; // horizontal scroll |
30 | |
cc1547e8 |
31 | /* sprite collision detection */ |
32 | static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed) |
33 | { |
34 | static u8 morton[16] = { 0x00,0x03,0x0c,0x0f,0x30,0x33,0x3c,0x3f, |
35 | 0xc0,0xc3,0xcc,0xcf,0xf0,0xf3,0xfc,0xff }; |
36 | u8 *mp = mb + (sx>>3); |
37 | unsigned col, m; |
4dbd5223 |
38 | |
39 | // check sprite map for collision and update map with current sprite |
40 | if (!zoomed) { // 8 sprite pixels |
41 | m = mp[0] | (mp[1]<<8); |
42 | col = m & (pack<<(sx&7)); // collision if current sprite overlaps sprite map |
43 | m |= pack<<(sx&7); |
44 | mp[0] = m, mp[1] = m>>8; |
45 | } else { // 16 sprite pixels in zoom mode |
46 | pack = morton[pack&0x0f] | (morton[(pack>>4)&0x0f] << 8); |
47 | m = mp[0] | (mp[1]<<8) | (mp[2]<<16); |
48 | col = m & (pack<<(sx&7)); |
49 | m |= pack<<(sx&7); |
50 | mp[0] = m, mp[1] = m>>8, mp[2] = m>>16; |
51 | } |
52 | |
cc1547e8 |
53 | // invisible overscan area, not tested for collision |
68e7a5c1 |
54 | mb[0] = mb[33] = mb[34] = 0; |
cc1547e8 |
55 | return col; |
56 | } |
57 | |
22917adc |
58 | /* Mode 4 - SMS Graphics */ |
59 | /*=======================*/ |
0df7401c |
60 | |
61 | static void TileBGM4(u16 sx, int pal) |
b74303b1 |
62 | { |
d05e2eb3 |
63 | if (sx & 3) { |
64 | u8 *pd = (u8 *)(Pico.est.HighCol + sx); |
65 | pd[0] = pd[1] = pd[2] = pd[3] = pal; |
66 | pd[4] = pd[5] = pd[6] = pd[7] = pal; |
67 | } else { |
68 | u32 *pd = (u32 *)(Pico.est.HighCol + sx); |
69 | pd[0] = pd[1] = pal * 0x01010101; |
70 | } |
b74303b1 |
71 | } |
72 | |
d0cb6cfa |
73 | // 8 pixels are arranged in 4 bitplane bytes in a 32 bit word. To pull the |
74 | // 4 bitplanes together multiply with each bit distance (multiples of 1<<7) |
2dacba52 |
75 | #define PLANAR_PIXELBG(x,p) \ |
0df7401c |
76 | t = (pack>>(7-p)) & 0x01010101; \ |
77 | t = (t*0x10204080) >> 28; \ |
2e66d031 |
78 | pd[x] = pal|t; |
79 | |
2dacba52 |
80 | static void TileNormBGM4(u16 sx, unsigned int pack, int pal) |
2e66d031 |
81 | { |
0df7401c |
82 | u8 *pd = Pico.est.HighCol + sx; |
83 | u32 t; |
2e66d031 |
84 | |
2dacba52 |
85 | PLANAR_PIXELBG(0, 0) |
86 | PLANAR_PIXELBG(1, 1) |
87 | PLANAR_PIXELBG(2, 2) |
88 | PLANAR_PIXELBG(3, 3) |
89 | PLANAR_PIXELBG(4, 4) |
90 | PLANAR_PIXELBG(5, 5) |
91 | PLANAR_PIXELBG(6, 6) |
92 | PLANAR_PIXELBG(7, 7) |
2e66d031 |
93 | } |
94 | |
2dacba52 |
95 | static void TileFlipBGM4(u16 sx, unsigned int pack, int pal) |
2e66d031 |
96 | { |
0df7401c |
97 | u8 *pd = Pico.est.HighCol + sx; |
98 | u32 t; |
2e66d031 |
99 | |
2dacba52 |
100 | PLANAR_PIXELBG(0, 7) |
101 | PLANAR_PIXELBG(1, 6) |
102 | PLANAR_PIXELBG(2, 5) |
103 | PLANAR_PIXELBG(3, 4) |
104 | PLANAR_PIXELBG(4, 3) |
105 | PLANAR_PIXELBG(5, 2) |
106 | PLANAR_PIXELBG(6, 1) |
107 | PLANAR_PIXELBG(7, 0) |
2e66d031 |
108 | } |
109 | |
2dacba52 |
110 | // non-transparent sprite pixels apply if no higher prio pixel is already there |
111 | #define PLANAR_PIXELSP(x,p) \ |
0df7401c |
112 | t = (pack>>(7-p)) & 0x01010101; \ |
2dacba52 |
113 | if (t && (pd[x] & 0x2f) <= 0x20) { \ |
0df7401c |
114 | t = (t*0x10204080) >> 28; \ |
200772b7 |
115 | pd[x] = pal|t; \ |
116 | } |
117 | |
2dacba52 |
118 | static void TileNormSprM4(u16 sx, unsigned int pack, int pal) |
200772b7 |
119 | { |
0df7401c |
120 | u8 *pd = Pico.est.HighCol + sx; |
121 | u32 t; |
eaa14751 |
122 | |
2dacba52 |
123 | PLANAR_PIXELSP(0, 0) |
124 | PLANAR_PIXELSP(1, 1) |
125 | PLANAR_PIXELSP(2, 2) |
126 | PLANAR_PIXELSP(3, 3) |
127 | PLANAR_PIXELSP(4, 4) |
128 | PLANAR_PIXELSP(5, 5) |
129 | PLANAR_PIXELSP(6, 6) |
130 | PLANAR_PIXELSP(7, 7) |
200772b7 |
131 | } |
132 | |
2dacba52 |
133 | static void TileDoubleSprM4(int sx, unsigned int pack, int pal) |
200772b7 |
134 | { |
0df7401c |
135 | u8 *pd = Pico.est.HighCol + sx; |
136 | u32 t; |
eaa14751 |
137 | |
2dacba52 |
138 | PLANAR_PIXELSP(0, 0) |
139 | PLANAR_PIXELSP(1, 0) |
140 | PLANAR_PIXELSP(2, 1) |
141 | PLANAR_PIXELSP(3, 1) |
142 | PLANAR_PIXELSP(4, 2) |
143 | PLANAR_PIXELSP(5, 2) |
144 | PLANAR_PIXELSP(6, 3) |
145 | PLANAR_PIXELSP(7, 3) |
146 | PLANAR_PIXELSP(8, 4) |
147 | PLANAR_PIXELSP(9, 4) |
148 | PLANAR_PIXELSP(10, 5) |
149 | PLANAR_PIXELSP(11, 5) |
150 | PLANAR_PIXELSP(12, 6) |
151 | PLANAR_PIXELSP(13, 6) |
152 | PLANAR_PIXELSP(14, 7) |
153 | PLANAR_PIXELSP(15, 7) |
0df7401c |
154 | } |
155 | |
1ce2b092 |
156 | static void ParseSpritesM4(int scanline) |
200772b7 |
157 | { |
87b0845f |
158 | struct PicoVideo *pv = &Pico.video; |
0df7401c |
159 | u8 *sat; |
96948bdf |
160 | int xoff = line_offset; |
87b0845f |
161 | int sprite_base, addr_mask; |
2db0d004 |
162 | int zoomed = sprites_zoom & 0x1; // zoomed sprites, e.g. Earthworm Jim |
1ce2b092 |
163 | unsigned int pack; |
cc1547e8 |
164 | int i, s, h, m; |
87b0845f |
165 | |
166 | if (pv->reg[0] & 8) |
96948bdf |
167 | xoff -= 8; // sprite shift |
0aa63fce |
168 | if (Pico.m.hardware & PMS_HW_LCD) |
466fa079 |
169 | xoff -= 48; // GG LCD, adjust to center 160 px |
87b0845f |
170 | |
4c10007b |
171 | sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7); |
2db0d004 |
172 | if (sprites_zoom & 2) { |
87b0845f |
173 | addr_mask = 0xfe; h = 16; |
174 | } else { |
175 | addr_mask = 0xff; h = 8; |
176 | } |
0df7401c |
177 | if (zoomed) h *= 2; |
4c10007b |
178 | sprite_base = (pv->reg[6] & 4) << (13-2-1); |
87b0845f |
179 | |
2531b6fd |
180 | m = pv->status & SR_C; |
1ce2b092 |
181 | memset(sprites_map, 0, sizeof(sprites_map)); |
1c25c32c |
182 | for (i = s = 0; i < 64; i++) |
87b0845f |
183 | { |
184 | int y; |
f90bf8cd |
185 | y = sat[MEM_LE2(i)]; |
186 | if (y == 0xd0 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))) |
87b0845f |
187 | break; |
f90bf8cd |
188 | if (y >= 0xe0) |
68e7a5c1 |
189 | y -= 256; |
f90bf8cd |
190 | y &= ~zoomed; // zoomed sprites apparently only on even lines, see GG Tarzan |
87b0845f |
191 | if (y + h <= scanline || scanline < y) |
192 | continue; // not on this line |
1c25c32c |
193 | if (s >= 8) { |
1ce2b092 |
194 | if (scanline >= 0) sprites_status |= SR_SOVR; |
195 | if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32) |
f90bf8cd |
196 | break; |
1c25c32c |
197 | } |
87b0845f |
198 | |
466fa079 |
199 | if (xoff + sat[MEM_LE2(0x80 + i*2)] >= 0) { |
200 | sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)]; |
201 | sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) + |
202 | ((scanline - y) >> zoomed << (2-1)); |
2db0d004 |
203 | if (pv->reg[1] & 0x40) { |
1ce2b092 |
204 | // collision detection. Do it here since off-screen lines aren't drawn |
205 | pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); |
206 | // make sprite pixel map by merging the 4 bitplanes |
207 | pack = ((pack | (pack>>16)) | ((pack | (pack>>16))>>8)) & 0xff; |
208 | if (!m) m = CollisionDetect(sprites_map, sprites_x[s], pack, zoomed); |
96948bdf |
209 | // no collision detection in 1st column if it's masked |
210 | if (pv->reg[0] & 0x20) |
211 | sprites_map[1] = 0; |
1ce2b092 |
212 | } |
466fa079 |
213 | s++; |
214 | } |
87b0845f |
215 | } |
1ce2b092 |
216 | if (m) |
217 | sprites_status |= SR_C; |
218 | sprites = s; |
219 | } |
220 | |
221 | static void DrawSpritesM4(void) |
222 | { |
1ce2b092 |
223 | unsigned int pack; |
2db0d004 |
224 | int zoomed = sprites_zoom & 0x1; // zoomed sprites, e.g. Earthworm Jim |
1ce2b092 |
225 | int s = sprites; |
87b0845f |
226 | |
227 | // now draw all sprites backwards |
eaa14751 |
228 | for (--s; s >= 0; s--) { |
57c5a5e5 |
229 | pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); |
2dacba52 |
230 | if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10); |
231 | else TileNormSprM4(sprites_x[s], pack, 0x10); |
eaa14751 |
232 | } |
87b0845f |
233 | } |
234 | |
0df7401c |
235 | // cells_dx, tilex_ty merged to reduce register pressure |
2dacba52 |
236 | static void DrawStripM4(const u16 *nametab, int cells_dx, int tilex_ty) |
2e66d031 |
237 | { |
b74303b1 |
238 | int oldcode = -1; |
2e66d031 |
239 | int addr = 0, pal = 0; |
240 | |
241 | // Draw tiles across screen: |
0aa63fce |
242 | for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) |
2e66d031 |
243 | { |
244 | unsigned int pack; |
15eed405 |
245 | unsigned code; |
2e66d031 |
246 | |
4dbd5223 |
247 | code = nametab[tilex_ty & 0x1f]; |
2e66d031 |
248 | |
249 | if (code != oldcode) { |
250 | oldcode = code; |
251 | // Get tile address/2: |
252 | addr = (code & 0x1ff) << 4; |
4dbd5223 |
253 | addr += tilex_ty >> 16; |
2e66d031 |
254 | if (code & 0x0400) |
255 | addr ^= 0xe; // Y-flip |
256 | |
2dacba52 |
257 | pal = (code>>7) & 0x30; // prio | palette select |
2e66d031 |
258 | } |
259 | |
4dbd5223 |
260 | pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); // Get 4 bitplanes / 8 pixels |
0df7401c |
261 | if (pack == 0) TileBGM4(cells_dx, pal); |
2dacba52 |
262 | else if (code & 0x0200) TileFlipBGM4(cells_dx, pack, pal); |
263 | else TileNormBGM4(cells_dx, pack, pal); |
200772b7 |
264 | } |
265 | } |
266 | |
87b0845f |
267 | static void DrawDisplayM4(int scanline) |
200772b7 |
268 | { |
87b0845f |
269 | struct PicoVideo *pv = &Pico.video; |
0df7401c |
270 | u16 *nametab, *nametab2; |
87b0845f |
271 | int line, tilex, dx, ty, cells; |
272 | int cellskip = 0; // XXX |
273 | int maxcells = 32; |
200772b7 |
274 | |
275 | // Find the line in the name table |
87b0845f |
276 | line = pv->reg[9] + scanline; // vscroll + scanline |
200772b7 |
277 | |
87b0845f |
278 | // Find name table: |
88fd63ad |
279 | nametab = PicoMem.vram; |
4b3e9d92 |
280 | if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) { |
0df7401c |
281 | // 224/240 line mode |
4b3e9d92 |
282 | line &= 0xff; |
3758124c |
283 | nametab += ((pv->reg[2] & 0x0c) << (10-1)) + (0x700 >> 1); |
4b3e9d92 |
284 | } else { |
285 | while (line >= 224) line -= 224; |
3758124c |
286 | nametab += (pv->reg[2] & 0x0e) << (10-1); |
4b3e9d92 |
287 | // old SMS only, masks line:7 with reg[2]:0 for address calculation |
288 | //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f; |
289 | } |
0df7401c |
290 | nametab2 = nametab + ((scanline>>3) << (6-1)); |
291 | nametab = nametab + ((line>>3) << (6-1)); |
87b0845f |
292 | |
2db0d004 |
293 | dx = xscroll; // hscroll |
87b0845f |
294 | if (scanline < 16 && (pv->reg[0] & 0x40)) |
0df7401c |
295 | dx = 0; // hscroll disabled for top 2 rows (e.g. Fantasy Zone II) |
87b0845f |
296 | |
96948bdf |
297 | tilex = (32 - (dx >> 3) + cellskip) & 0x1f; |
87b0845f |
298 | ty = (line & 7) << 1; // Y-Offset into tile |
0aa63fce |
299 | cells = maxcells - cellskip - 1; |
87b0845f |
300 | |
96948bdf |
301 | dx = (dx & 7); |
87b0845f |
302 | dx += cellskip << 3; |
35247900 |
303 | dx += line_offset; |
87b0845f |
304 | |
2dacba52 |
305 | // tiles |
0df7401c |
306 | if (!(pv->debug_p & PVD_KILL_B)) { |
0aa63fce |
307 | if (Pico.m.hardware & PMS_HW_LCD) { |
96948bdf |
308 | // on GG render only the center 160 px, but mind hscroll |
309 | DrawStripM4(nametab , (dx-8) | ((cells-11)<< 16),(tilex+5) | (ty << 16)); |
466fa079 |
310 | } else if (pv->reg[0] & 0x80) { |
0df7401c |
311 | // vscroll disabled for rightmost 8 columns (e.g. Gauntlet) |
466fa079 |
312 | int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7; |
96948bdf |
313 | DrawStripM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16)); |
314 | DrawStripM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17)); |
0df7401c |
315 | } else |
96948bdf |
316 | DrawStripM4(nametab , dx | ( cells << 16), tilex | (ty << 16)); |
0df7401c |
317 | } |
87b0845f |
318 | |
319 | // sprites |
e0bcb7a9 |
320 | if (!(pv->debug_p & PVD_KILL_S_LO)) |
1ce2b092 |
321 | DrawSpritesM4(); |
87b0845f |
322 | |
0aa63fce |
323 | if ((pv->reg[0] & 0x20) && !(Pico.m.hardware & PMS_HW_LCD)) { |
0df7401c |
324 | // first column masked with background, caculate offset to start of line |
96948bdf |
325 | dx = line_offset / 4; |
68e7a5c1 |
326 | ty = ((pv->reg[7]&0x0f)|0x10) * 0x01010101; |
96948bdf |
327 | ((u32 *)Pico.est.HighCol)[dx] = ((u32 *)Pico.est.HighCol)[dx+1] = ty; |
7980d477 |
328 | } |
200772b7 |
329 | } |
330 | |
0df7401c |
331 | |
4dbd5223 |
332 | /* TMS Modes */ |
333 | /*===========*/ |
0df7401c |
334 | |
22917adc |
335 | /* Background */ |
0df7401c |
336 | |
337 | #define TMS_PIXELBG(x,p) \ |
338 | t = (pack>>(7-p)) & 0x01; \ |
339 | t = (pal >> (t << 2)) & 0x0f; \ |
22917adc |
340 | if (t) \ |
341 | pd[x] = t; |
342 | |
343 | static void TileNormBgM1(u16 sx, unsigned int pack, int pal) /* Text */ |
344 | { |
345 | u8 *pd = Pico.est.HighCol + sx; |
346 | unsigned int t; |
347 | |
348 | TMS_PIXELBG(0, 0) |
349 | TMS_PIXELBG(1, 1) |
350 | TMS_PIXELBG(2, 2) |
351 | TMS_PIXELBG(3, 3) |
352 | TMS_PIXELBG(4, 4) |
353 | TMS_PIXELBG(5, 5) |
354 | } |
355 | |
356 | static void TileNormBgM2(u16 sx, int pal) /* Multicolor */ |
357 | { |
358 | u8 *pd = Pico.est.HighCol + sx; |
359 | unsigned int pack = 0xf0; |
360 | unsigned int t; |
361 | |
362 | TMS_PIXELBG(0, 0) |
363 | TMS_PIXELBG(1, 1) |
364 | TMS_PIXELBG(2, 2) |
365 | TMS_PIXELBG(3, 3) |
366 | TMS_PIXELBG(4, 4) |
367 | TMS_PIXELBG(5, 5) |
368 | TMS_PIXELBG(6, 6) |
369 | TMS_PIXELBG(7, 7) |
370 | } |
0df7401c |
371 | |
22917adc |
372 | static void TileNormBgMg(u16 sx, unsigned int pack, int pal) /* Graphics */ |
0df7401c |
373 | { |
374 | u8 *pd = Pico.est.HighCol + sx; |
375 | unsigned int t; |
376 | |
377 | TMS_PIXELBG(0, 0) |
378 | TMS_PIXELBG(1, 1) |
379 | TMS_PIXELBG(2, 2) |
380 | TMS_PIXELBG(3, 3) |
381 | TMS_PIXELBG(4, 4) |
382 | TMS_PIXELBG(5, 5) |
383 | TMS_PIXELBG(6, 6) |
384 | TMS_PIXELBG(7, 7) |
385 | } |
386 | |
387 | /* Sprites */ |
388 | |
389 | #define TMS_PIXELSP(x,p) \ |
390 | t = (pack>>(7-p)) & 0x01; \ |
391 | if (t) \ |
392 | pd[x] = pal; |
393 | |
4dbd5223 |
394 | static void TileNormSprTMS(u16 sx, unsigned int pack, int pal) |
0df7401c |
395 | { |
396 | u8 *pd = Pico.est.HighCol + sx; |
397 | unsigned int t; |
398 | |
399 | TMS_PIXELSP(0, 0) |
400 | TMS_PIXELSP(1, 1) |
401 | TMS_PIXELSP(2, 2) |
402 | TMS_PIXELSP(3, 3) |
403 | TMS_PIXELSP(4, 4) |
404 | TMS_PIXELSP(5, 5) |
405 | TMS_PIXELSP(6, 6) |
406 | TMS_PIXELSP(7, 7) |
407 | } |
408 | |
4dbd5223 |
409 | static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal) |
0df7401c |
410 | { |
411 | u8 *pd = Pico.est.HighCol + sx; |
412 | unsigned int t; |
413 | |
414 | TMS_PIXELSP(0, 0) |
415 | TMS_PIXELSP(1, 0) |
416 | TMS_PIXELSP(2, 1) |
417 | TMS_PIXELSP(3, 1) |
418 | TMS_PIXELSP(4, 2) |
419 | TMS_PIXELSP(5, 2) |
420 | TMS_PIXELSP(6, 3) |
421 | TMS_PIXELSP(7, 3) |
422 | TMS_PIXELSP(8, 4) |
423 | TMS_PIXELSP(9, 4) |
424 | TMS_PIXELSP(10, 5) |
425 | TMS_PIXELSP(11, 5) |
426 | TMS_PIXELSP(12, 6) |
427 | TMS_PIXELSP(13, 6) |
428 | TMS_PIXELSP(14, 7) |
429 | TMS_PIXELSP(15, 7) |
430 | } |
431 | |
1ce2b092 |
432 | static void ParseSpritesTMS(int scanline) |
0df7401c |
433 | { |
434 | struct PicoVideo *pv = &Pico.video; |
0df7401c |
435 | unsigned int pack; |
436 | u8 *sat; |
96948bdf |
437 | int xoff; |
0df7401c |
438 | int sprite_base, addr_mask; |
2db0d004 |
439 | int zoomed = sprites_zoom & 0x1; // zoomed sprites |
cc1547e8 |
440 | int i, s, h, m; |
0df7401c |
441 | |
96948bdf |
442 | xoff = line_offset; |
0df7401c |
443 | |
4c10007b |
444 | sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7f) << 7); |
2db0d004 |
445 | if (sprites_zoom & 2) { |
0df7401c |
446 | addr_mask = 0xfc; h = 16; |
447 | } else { |
448 | addr_mask = 0xff; h = 8; |
449 | } |
450 | if (zoomed) h *= 2; |
4c10007b |
451 | sprite_base = (pv->reg[6] & 0x7) << 11; |
0df7401c |
452 | |
2531b6fd |
453 | m = pv->status & SR_C; |
1ce2b092 |
454 | memset(sprites_map, 0, sizeof(sprites_map)); |
0df7401c |
455 | /* find sprites on this scanline */ |
456 | for (i = s = 0; i < 32; i++) |
457 | { |
1ce2b092 |
458 | int x, y; |
f90bf8cd |
459 | y = sat[MEM_LE2(4*i)]; |
460 | if (y == 0xd0) |
0df7401c |
461 | break; |
f90bf8cd |
462 | if (y >= 0xe0) |
0df7401c |
463 | y -= 256; |
f90bf8cd |
464 | y &= ~zoomed; |
0df7401c |
465 | if (y + h <= scanline || scanline < y) |
466 | continue; // not on this line |
467 | if (s >= 4) { |
1ce2b092 |
468 | if (scanline >= 0) sprites_status |= SR_SOVR | i; |
f90bf8cd |
469 | if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32) |
470 | break; |
0df7401c |
471 | } |
1ce2b092 |
472 | x = sat[MEM_LE2(4*i+1)] + xoff; |
473 | if (sat[MEM_LE2(4*i+3)] & 0x80) |
474 | x -= 32; |
0df7401c |
475 | |
1ce2b092 |
476 | sprites_c[s] = sat[MEM_LE2(4*i+3)] & 0x0f; |
477 | sprites_x[s] = x; |
0df7401c |
478 | sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) + |
479 | ((scanline - y) >> zoomed); |
2db0d004 |
480 | if (pv->reg[1] & 0x40) { |
1ce2b092 |
481 | // collision detection. Do it here since off-screen lines aren't drawn |
482 | if (sprites_c[s] && x > 0) { |
483 | pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; |
484 | if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed); |
485 | } |
486 | x += (zoomed ? 16:8); |
2db0d004 |
487 | if (sprites_c[s] && (sprites_zoom & 0x2) && x > 0 && x < 8+256) { |
1ce2b092 |
488 | pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; |
489 | if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed); |
490 | } |
491 | } |
0df7401c |
492 | s++; |
493 | } |
1ce2b092 |
494 | if (m) |
495 | sprites_status |= SR_C; |
496 | sprites = s; |
497 | } |
498 | |
499 | /* Draw sprites into a scanline, max 4 */ |
500 | static void DrawSpritesTMS(void) |
501 | { |
1ce2b092 |
502 | unsigned int pack; |
2db0d004 |
503 | int zoomed = sprites_zoom & 0x1; // zoomed sprites |
1ce2b092 |
504 | int s = sprites; |
0df7401c |
505 | |
0df7401c |
506 | // now draw all sprites backwards |
507 | for (--s; s >= 0; s--) { |
466fa079 |
508 | int x, c, w = (zoomed ? 16: 8); |
1ce2b092 |
509 | x = sprites_x[s]; |
510 | c = sprites_c[s]; |
fd396e58 |
511 | // c may be 0 (transparent): sprite invisible |
1ce2b092 |
512 | if (c && x > 0) { |
0df7401c |
513 | pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; |
1ce2b092 |
514 | if (zoomed) TileDoubleSprTMS(x, pack, c); |
515 | else TileNormSprTMS(x, pack, c); |
0df7401c |
516 | } |
2db0d004 |
517 | if (c && (sprites_zoom & 0x2) && (x+=w) > 0 && x < 8+256) { |
0df7401c |
518 | pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; |
1ce2b092 |
519 | if (zoomed) TileDoubleSprTMS(x, pack, c); |
520 | else TileNormSprTMS(x, pack, c); |
0df7401c |
521 | } |
522 | } |
523 | } |
524 | |
22917adc |
525 | |
526 | /* Mode 1 - Text */ |
527 | /*===============*/ |
528 | |
529 | /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ |
530 | static void DrawStripM1(const u8 *nametab, const u8 *pattab, int cells_dx, int tilex_ty) |
531 | { |
532 | // Draw tiles across screen: |
ecf87642 |
533 | for (; cells_dx >= 0; cells_dx += 6, tilex_ty++, cells_dx -= 0x10000) |
22917adc |
534 | { |
535 | unsigned int pack, pal; |
536 | unsigned code; |
537 | |
538 | code = nametab[tilex_ty & 0x3f]; |
539 | pal = Pico.video.reg[7]; |
540 | pack = pattab[code << 3]; |
541 | TileNormBgM1(cells_dx, pack, pal); |
542 | } |
543 | } |
544 | |
545 | /* Draw a scanline */ |
546 | static void DrawDisplayM1(int scanline) |
547 | { |
548 | struct PicoVideo *pv = &Pico.video; |
549 | u8 *nametab, *pattab; |
550 | int tilex, dx, cells; |
551 | int cellskip = 0; // XXX |
552 | int maxcells = 40; |
e48f3f27 |
553 | unsigned mask = pv->reg[0] & 0x2 ? 0x2000 : 0x3800; // M3: 2 bits table select |
22917adc |
554 | |
555 | // name, color, pattern table: |
556 | nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); |
e48f3f27 |
557 | pattab = PicoMem.vramb + ((pv->reg[4]<<11) & mask); |
558 | pattab += ((scanline>>6) << 11) & ~mask; // table select bits for M3 |
22917adc |
559 | |
560 | nametab += ((scanline>>3) * maxcells); |
561 | pattab += (scanline & 0x7); |
562 | |
563 | tilex = cellskip & 0x1f; |
ecf87642 |
564 | cells = maxcells - cellskip - 1; |
4182483e |
565 | dx = 8 + (cellskip << 3) + line_offset; |
22917adc |
566 | |
567 | // tiles |
568 | if (!(pv->debug_p & PVD_KILL_B)) |
569 | DrawStripM1(nametab, pattab, dx | (cells << 16), tilex | (scanline << 16)); |
570 | } |
571 | |
572 | |
573 | /* Mode 2 - Multicolor */ |
574 | /*=====================*/ |
575 | |
576 | /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ |
577 | static void DrawStripM2(const u8 *nametab, const u8 *pattab, int cells_dx, int tilex_ty) |
578 | { |
579 | // Draw tiles across screen: |
ecf87642 |
580 | for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) |
22917adc |
581 | { |
582 | unsigned int pal; |
583 | unsigned code; |
584 | |
585 | code = nametab[tilex_ty & 0x1f]; |
586 | pal = pattab[code << 3]; |
587 | TileNormBgM2(cells_dx, pal); |
588 | } |
589 | } |
590 | |
591 | /* Draw a scanline */ |
592 | static void DrawDisplayM2(int scanline) |
593 | { |
594 | struct PicoVideo *pv = &Pico.video; |
595 | u8 *nametab, *pattab; |
596 | int tilex, dx, cells; |
597 | int cellskip = 0; // XXX |
598 | int maxcells = 32; |
e48f3f27 |
599 | unsigned mask = pv->reg[0] & 0x2 ? 0x2000 : 0x3800; // M3: 2 bits table select |
22917adc |
600 | |
601 | // name, color, pattern table: |
602 | nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); |
e48f3f27 |
603 | pattab = PicoMem.vramb + ((pv->reg[4]<<11) & mask); |
604 | pattab += ((scanline>>6) << 11) & ~mask; // table select bits for M3 |
22917adc |
605 | |
e48f3f27 |
606 | nametab += (scanline>>3) << 5; |
22917adc |
607 | pattab += (scanline>>2) & 0x7; |
608 | |
609 | tilex = cellskip & 0x1f; |
ecf87642 |
610 | cells = maxcells - cellskip - 1; |
96948bdf |
611 | dx = (cellskip << 3) + line_offset; |
22917adc |
612 | |
613 | // tiles |
614 | if (!(pv->debug_p & PVD_KILL_B)) |
615 | DrawStripM2(nametab, pattab, dx | (cells << 16), tilex | (scanline << 16)); |
616 | |
617 | // sprites |
618 | if (!(pv->debug_p & PVD_KILL_S_LO)) |
619 | DrawSpritesTMS(); |
620 | } |
621 | |
622 | |
623 | /* Mode 3 - Graphics II */ |
624 | /*======================*/ |
4dbd5223 |
625 | |
0df7401c |
626 | /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ |
22917adc |
627 | static void DrawStripM3(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty) |
0df7401c |
628 | { |
629 | // Draw tiles across screen: |
ecf87642 |
630 | for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) |
0df7401c |
631 | { |
632 | unsigned int pack, pal; |
633 | unsigned code; |
634 | |
4dbd5223 |
635 | code = nametab[tilex_ty & 0x1f] << 3; |
0df7401c |
636 | pal = coltab[code]; |
637 | pack = pattab[code]; |
22917adc |
638 | TileNormBgMg(cells_dx, pack, pal); |
0df7401c |
639 | } |
640 | } |
641 | |
642 | /* Draw a scanline */ |
22917adc |
643 | static void DrawDisplayM3(int scanline) |
0df7401c |
644 | { |
645 | struct PicoVideo *pv = &Pico.video; |
646 | u8 *nametab, *coltab, *pattab; |
647 | int tilex, dx, cells; |
648 | int cellskip = 0; // XXX |
649 | int maxcells = 32; |
650 | |
651 | // name, color, pattern table: |
652 | nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); |
653 | coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x2000); |
654 | pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x2000); |
655 | |
656 | nametab += ((scanline>>3) << 5); |
657 | coltab += ((scanline>>6) <<11) + (scanline & 0x7); |
658 | pattab += ((scanline>>6) <<11) + (scanline & 0x7); |
659 | |
660 | tilex = cellskip & 0x1f; |
ecf87642 |
661 | cells = maxcells - cellskip - 1; |
96948bdf |
662 | dx = (cellskip << 3) + line_offset; |
0df7401c |
663 | |
664 | // tiles |
665 | if (!(pv->debug_p & PVD_KILL_B)) |
22917adc |
666 | DrawStripM3(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16)); |
0df7401c |
667 | |
668 | // sprites |
669 | if (!(pv->debug_p & PVD_KILL_S_LO)) |
1ce2b092 |
670 | DrawSpritesTMS(); |
4dbd5223 |
671 | } |
672 | |
22917adc |
673 | |
674 | /* Mode 0 - Graphics I */ |
675 | /*=====================*/ |
4dbd5223 |
676 | |
677 | /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ |
68e7a5c1 |
678 | static void DrawStripM0(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty) |
4dbd5223 |
679 | { |
680 | // Draw tiles across screen: |
ecf87642 |
681 | for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) |
4dbd5223 |
682 | { |
683 | unsigned int pack, pal; |
684 | unsigned code; |
685 | |
686 | code = nametab[tilex_ty & 0x1f]; |
687 | pal = coltab[code >> 3]; |
688 | pack = pattab[code << 3]; |
22917adc |
689 | TileNormBgMg(cells_dx, pack, pal); |
4dbd5223 |
690 | } |
691 | } |
692 | |
693 | /* Draw a scanline */ |
68e7a5c1 |
694 | static void DrawDisplayM0(int scanline) |
4dbd5223 |
695 | { |
696 | struct PicoVideo *pv = &Pico.video; |
697 | u8 *nametab, *coltab, *pattab; |
698 | int tilex, dx, cells; |
699 | int cellskip = 0; // XXX |
700 | int maxcells = 32; |
701 | |
702 | // name, color, pattern table: |
703 | nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); |
704 | coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x3fc0); |
705 | pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x3800); |
706 | |
707 | nametab += (scanline>>3) << 5; |
708 | pattab += (scanline & 0x7); |
709 | |
710 | tilex = cellskip & 0x1f; |
ecf87642 |
711 | cells = maxcells - cellskip - 1; |
96948bdf |
712 | dx = (cellskip << 3) + line_offset; |
4dbd5223 |
713 | |
714 | // tiles |
715 | if (!(pv->debug_p & PVD_KILL_B)) |
68e7a5c1 |
716 | DrawStripM0(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16)); |
4dbd5223 |
717 | |
718 | // sprites |
719 | if (!(pv->debug_p & PVD_KILL_S_LO)) |
1ce2b092 |
720 | DrawSpritesTMS(); |
0df7401c |
721 | } |
722 | |
723 | |
724 | /* Common/global */ |
725 | /*===============*/ |
726 | |
727 | static void FinalizeLineRGB555SMS(int line); |
646be42e |
728 | static void FinalizeLine8bitSMS(int line); |
0df7401c |
729 | |
730 | void PicoFrameStartSMS(void) |
200772b7 |
731 | { |
c180662e |
732 | struct PicoEState *est = &Pico.est; |
466fa079 |
733 | int lines = 192, columns = 256, loffs, coffs; |
96948bdf |
734 | |
200772b7 |
735 | skip_next_line = 0; |
466fa079 |
736 | loffs = screen_offset = 24; // 192 lines is really 224 with top/bottom bars |
c180662e |
737 | est->rendstatus = PDRAW_32_COLS; |
19954be1 |
738 | |
1603058a |
739 | // if mode changes make palette dirty since some modes switch to a fixed one |
740 | if (mode != ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18))) { |
741 | mode = (Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18); |
742 | Pico.m.dirtyPal = 1; |
743 | } |
744 | |
89693744 |
745 | switch (mode) { |
746 | // SMS2 only 224/240 line modes, e.g. Micro Machines |
747 | case 0x06|0x08: |
748 | est->rendstatus |= PDRAW_30_ROWS; |
749 | loffs = screen_offset = 0; |
750 | lines = 240; |
751 | break; |
752 | case 0x06|0x10: |
753 | loffs = screen_offset = 8; |
754 | lines = 224; |
755 | break; |
756 | } |
757 | |
214a6c62 |
758 | Pico.m.hardware &= ~PMS_HW_TMS; |
759 | if (PicoIn.tmsPalette || (PicoIn.AHW & (PAHW_SG|PAHW_SC))) |
760 | Pico.m.hardware |= PMS_HW_TMS; |
761 | |
466fa079 |
762 | // Copy LCD enable flag for easier handling |
df6c895c |
763 | Pico.m.hardware &= ~PMS_HW_LCD; |
e5a1d4c5 |
764 | if ((PicoIn.opt & POPT_EN_GG_LCD) && (PicoIn.AHW & PAHW_GG)) { |
df6c895c |
765 | Pico.m.hardware |= PMS_HW_LCD; |
466fa079 |
766 | |
466fa079 |
767 | // GG LCD always has 160x144 regardless of settings |
466fa079 |
768 | loffs = 48; |
769 | lines = 144; |
770 | columns = 160; |
e5a1d4c5 |
771 | } else { |
772 | if ((mode & 4) && (Pico.video.reg[0] & 0x20)) { |
773 | // SMS mode 4 with 1st column blanked |
c180662e |
774 | est->rendstatus |= PDRAW_SMS_BLANK_1; |
a5aae2c3 |
775 | columns = 248; |
e5a1d4c5 |
776 | } |
19954be1 |
777 | } |
96948bdf |
778 | |
779 | line_offset = 8; // FinalizeLine requires HighCol+8 |
780 | // ugh... nonetheless has offset in 8-bit fast mode if 1st col blanked! |
781 | coffs = (FinalizeLineSMS == NULL && columns == 248 ? 8 : 0); |
782 | if (FinalizeLineSMS != NULL && (PicoIn.opt & POPT_EN_SOFTSCALE)) { |
783 | // softscaling always generates 320px, but no scaling in 8bit fast |
c180662e |
784 | est->rendstatus |= PDRAW_SOFTSCALE; |
466fa079 |
785 | coffs = 0; |
3758124c |
786 | columns = 320; |
96948bdf |
787 | } else if (!(PicoIn.opt & POPT_DIS_32C_BORDER)) { |
c180662e |
788 | est->rendstatus |= PDRAW_BORDER_32; |
96948bdf |
789 | line_offset -= coffs; |
790 | coffs = (320-columns) / 2; |
791 | if (FinalizeLineSMS == NULL) |
792 | line_offset += coffs; // ... else centering done in FinalizeLine |
793 | } |
19954be1 |
794 | |
c180662e |
795 | if (est->rendstatus != rendstatus_old || lines != rendlines) { |
796 | // mode_change() might reset rendstatus_old by calling SetOutFormat |
797 | int rendstatus = est->rendstatus; |
466fa079 |
798 | emu_video_mode_change(loffs, lines, coffs, columns); |
c180662e |
799 | rendstatus_old = rendstatus; |
ae87bffa |
800 | rendlines = lines; |
1ce2b092 |
801 | sprites = 0; |
87b0845f |
802 | } |
5a681086 |
803 | |
c180662e |
804 | est->HighCol = HighColBase + screen_offset * HighColIncrement; |
805 | est->DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement; |
646be42e |
806 | |
807 | if (FinalizeLineSMS == FinalizeLine8bitSMS) { |
c180662e |
808 | Pico.m.dirtyPal = (Pico.m.dirtyPal || est->SonicPalCount ? 2 : 0); |
809 | memcpy(est->SonicPal, PicoMem.cram, 0x40*2); |
646be42e |
810 | } |
c180662e |
811 | est->SonicPalCount = 0; |
200772b7 |
812 | } |
813 | |
1ce2b092 |
814 | void PicoParseSATSMS(int line) |
815 | { |
816 | if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line); |
817 | else ParseSpritesTMS(line); |
818 | } |
819 | |
0df7401c |
820 | void PicoLineSMS(int line) |
200772b7 |
821 | { |
466fa079 |
822 | int skip = skip_next_line; |
fd396e58 |
823 | unsigned bgcolor; |
89693744 |
824 | int first = 48 - screen_offset; |
466fa079 |
825 | |
826 | // GG LCD, render only visible part of screen |
89693744 |
827 | if ((Pico.m.hardware & PMS_HW_LCD) && (line < first || line >= first+144)) |
466fa079 |
828 | goto norender; |
829 | |
830 | if (PicoScanBegin != NULL && skip == 0) |
831 | skip = PicoScanBegin(line + screen_offset); |
832 | |
833 | if (skip) { |
834 | skip_next_line = skip - 1; |
200772b7 |
835 | return; |
836 | } |
837 | |
200772b7 |
838 | // Draw screen: |
fd396e58 |
839 | bgcolor = (Pico.video.reg[7] & 0x0f) | ((Pico.video.reg[0] & 0x04) << 2); |
840 | BackFill(bgcolor, 0, &Pico.est); // bgcolor is from 2nd palette in mode 4 |
0df7401c |
841 | if (Pico.video.reg[1] & 0x40) { |
e48f3f27 |
842 | if (Pico.video.reg[0] & 0x04) DrawDisplayM4(line); // also M4+M3 |
843 | else if (Pico.video.reg[1] & 0x08) DrawDisplayM2(line); // also M2+M3 |
844 | else if (Pico.video.reg[1] & 0x10) DrawDisplayM1(line); // also M1+M3 |
22917adc |
845 | else if (Pico.video.reg[0] & 0x02) DrawDisplayM3(line); |
68e7a5c1 |
846 | else DrawDisplayM0(line); |
0df7401c |
847 | } |
200772b7 |
848 | |
2db0d004 |
849 | // latch current register values (may be overwritten by VDP reg writes later) |
850 | sprites_zoom = (Pico.video.reg[1] & 0x3) | (Pico.video.reg[0] & 0x8); |
2db0d004 |
851 | xscroll = Pico.video.reg[8]; |
852 | |
0df7401c |
853 | if (FinalizeLineSMS != NULL) |
854 | FinalizeLineSMS(line); |
200772b7 |
855 | |
856 | if (PicoScanEnd != NULL) |
87b0845f |
857 | skip_next_line = PicoScanEnd(line + screen_offset); |
5a681086 |
858 | |
466fa079 |
859 | norender: |
7980d477 |
860 | Pico.est.HighCol += HighColIncrement; |
99bdfd31 |
861 | Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement; |
200772b7 |
862 | } |
863 | |
22917adc |
864 | /* Palette for TMS9918 mode, see https://www.smspower.org/Development/Palette */ |
865 | // RGB values: #000000 #000000 #21c842 #5edc78 #5455ed #7d76fc #d4524d #42ebf5 |
3611781e |
866 | // #fc5554 #ff7978 #d4c154 #e6ce80 #21b03b #c95bba #cccccc #ffffff |
96948bdf |
867 | static u16 tmspal[] = { |
ace18401 |
868 | // SMS palette |
df6c895c |
869 | 0x0000, 0x0000, 0x00a0, 0x00f0, 0x0a00, 0x0f00, 0x0005, 0x0ff0, |
e48f3f27 |
870 | 0x000a, 0x000f, 0x00aa, 0x00ff, 0x0050, 0x0f0f, 0x0aaa, 0x0fff, |
96948bdf |
871 | // TMS palette |
e48f3f27 |
872 | 0x0000, 0x0000, 0x04c2, 0x07d6, 0x0e55, 0x0f77, 0x055c, 0x0ee4, |
873 | 0x055f, 0x077f, 0x05bc, 0x08ce, 0x03a2, 0x0b5c, 0x0ccc, 0x0fff, |
96948bdf |
874 | // SMS palette, closer to the TMS one |
875 | 0x0000, 0x0000, 0x05f0, 0x05f5, 0x0a50, 0x0f55, 0x055a, 0x0ff0, |
876 | 0x055f, 0x0aaf, 0x05aa, 0x05af, 0x00a0, 0x0f5f, 0x0aaa, 0x0fff, |
0df7401c |
877 | }; |
878 | |
879 | void PicoDoHighPal555SMS(void) |
200772b7 |
880 | { |
646be42e |
881 | u32 *spal = (void *)Pico.est.SonicPal; |
882 | u32 *dpal = (void *)Pico.est.HighPal; |
883 | unsigned int cnt = Pico.est.SonicPalCount+1; |
200772b7 |
884 | unsigned int t; |
646be42e |
885 | int i, j; |
886 | |
96948bdf |
887 | if (FinalizeLineSMS == FinalizeLineRGB555SMS || Pico.m.dirtyPal == 2) |
646be42e |
888 | Pico.m.dirtyPal = 0; |
889 | |
96948bdf |
890 | // use hardware palette if not in 8bit accurate mode |
891 | if (FinalizeLineSMS != FinalizeLine8bitSMS) |
646be42e |
892 | spal = (void *)PicoMem.cram; |
200772b7 |
893 | |
466fa079 |
894 | /* SMS 6 bit cram data was already converted to MD/GG format by vdp write, |
895 | * hence GG/SMS/TMS can all be handled the same here */ |
646be42e |
896 | for (j = cnt; j > 0; j--) { |
df6c895c |
897 | if (!(Pico.video.reg[0] & 0x4)) // fixed palette in TMS modes |
214a6c62 |
898 | spal = (u32 *)tmspal + (Pico.m.hardware & PMS_HW_TMS ? 16/2:0); |
646be42e |
899 | for (i = 0x20/2; i > 0; i--, spal++, dpal++) { |
ace18401 |
900 | t = *spal; |
0df7401c |
901 | #if defined(USE_BGR555) |
ace18401 |
902 | t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3); |
903 | t |= (t >> 4) & 0x04210421; |
0df7401c |
904 | #elif defined(USE_BGR565) |
ace18401 |
905 | t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4); |
906 | t |= (t >> 4) & 0x08610861; |
0df7401c |
907 | #else |
ace18401 |
908 | t = ((t & 0x000f000f)<<12)| ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7); |
909 | t |= (t >> 4) & 0x08610861; |
200772b7 |
910 | #endif |
ace18401 |
911 | *dpal = t; |
646be42e |
912 | } |
913 | memcpy(dpal, dpal-0x20/2, 0x20*2); // for prio bit |
914 | spal += 0x20/2, dpal += 0x20/2; |
200772b7 |
915 | } |
98a27142 |
916 | Pico.est.HighPal[0xe0] = 0; |
200772b7 |
917 | } |
918 | |
0df7401c |
919 | static void FinalizeLineRGB555SMS(int line) |
200772b7 |
920 | { |
200772b7 |
921 | if (Pico.m.dirtyPal) |
0df7401c |
922 | PicoDoHighPal555SMS(); |
200772b7 |
923 | |
19954be1 |
924 | // standard FinalizeLine can finish it for us, |
925 | // with features like scaling and such |
ea38612f |
926 | FinalizeLine555(0, line, &Pico.est); |
200772b7 |
927 | } |
928 | |
0df7401c |
929 | static void FinalizeLine8bitSMS(int line) |
87b0845f |
930 | { |
466fa079 |
931 | FinalizeLine8bit(0, line, &Pico.est); |
87b0845f |
932 | } |
933 | |
0df7401c |
934 | void PicoDrawSetOutputSMS(pdso_t which) |
200772b7 |
935 | { |
936 | switch (which) |
937 | { |
0df7401c |
938 | case PDF_8BIT: FinalizeLineSMS = FinalizeLine8bitSMS; break; |
939 | case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break; |
96948bdf |
940 | default: FinalizeLineSMS = NULL; // no multiple palettes, no scaling |
7980d477 |
941 | PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break; |
200772b7 |
942 | } |
d1ae0810 |
943 | rendstatus_old = -1; |
1603058a |
944 | mode = -1; |
200772b7 |
945 | } |
946 | |
1c25c32c |
947 | // vim:shiftwidth=2:ts=2:expandtab |