pandora: fix readme and pxml version
[picodrive.git] / pico / mode4.c
CommitLineData
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 15static void (*FinalizeLineSMS)(int line);
200772b7 16static int skip_next_line;
35247900 17static int screen_offset, line_offset;
1603058a 18static u8 mode;
0df7401c 19
1ce2b092 20static unsigned int sprites_addr[32]; // bitmap address
21static unsigned char sprites_c[32]; // TMS sprites color
22static int sprites_x[32]; // x position
23static int sprites; // count
24static unsigned char sprites_map[2+256/8+2]; // collision detection map
25
26unsigned int sprites_status;
27
2db0d004 28int sprites_zoom; // latched sprite zoom flag
2db0d004 29int xscroll; // horizontal scroll
30
cc1547e8 31/* sprite collision detection */
32static 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
61static 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 80static 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 95static 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 118static 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 133static 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 156static 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
221static 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 236static 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 267static 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
343static 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
356static 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 372static 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 394static 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 409static 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 432static 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 */
500static 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 */
530static 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 */
546static 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 */
577static 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 */
592static 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 627static 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 643static 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 678static 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 694static 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
727static void FinalizeLineRGB555SMS(int line);
646be42e 728static void FinalizeLine8bitSMS(int line);
0df7401c 729
730void 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 814void PicoParseSATSMS(int line)
815{
816 if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line);
817 else ParseSpritesTMS(line);
818}
819
0df7401c 820void 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 859norender:
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 867static 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
879void 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 919static 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 929static void FinalizeLine8bitSMS(int line)
87b0845f 930{
466fa079 931 FinalizeLine8bit(0, line, &Pico.est);
87b0845f 932}
933
0df7401c 934void 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