4 * (C) irixxxx, 2020-2024
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
9 * This work is licensed under the terms of MAME license.
10 * See COPYING file in the top-level directory.
13 #include <platform/common/upscale.h>
15 static void (*FinalizeLineSMS)(int line);
16 static int skip_next_line;
17 static int screen_offset, line_offset;
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
26 unsigned int sprites_status;
28 int sprites_zoom; // latched sprite zoom flag
29 int xscroll; // horizontal scroll
31 /* sprite collision detection */
32 static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed)
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);
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
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));
50 mp[0] = m, mp[1] = m>>8, mp[2] = m>>16;
53 // invisible overscan area, not tested for collision
54 mb[0] = mb[33] = mb[34] = 0;
58 /* Mode 4 - SMS Graphics */
59 /*=======================*/
61 static void TileBGM4(u16 sx, int pal)
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;
68 u32 *pd = (u32 *)(Pico.est.HighCol + sx);
69 pd[0] = pd[1] = pal * 0x01010101;
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)
75 #define PLANAR_PIXELBG(x,p) \
76 t = (pack>>(7-p)) & 0x01010101; \
77 t = (t*0x10204080) >> 28; \
80 static void TileNormBGM4(u16 sx, unsigned int pack, int pal)
82 u8 *pd = Pico.est.HighCol + sx;
95 static void TileFlipBGM4(u16 sx, unsigned int pack, int pal)
97 u8 *pd = Pico.est.HighCol + sx;
110 // non-transparent sprite pixels apply if no higher prio pixel is already there
111 #define PLANAR_PIXELSP(x,p) \
112 t = (pack>>(7-p)) & 0x01010101; \
113 if (t && (pd[x] & 0x2f) <= 0x20) { \
114 t = (t*0x10204080) >> 28; \
118 static void TileNormSprM4(u16 sx, unsigned int pack, int pal)
120 u8 *pd = Pico.est.HighCol + sx;
133 static void TileDoubleSprM4(int sx, unsigned int pack, int pal)
135 u8 *pd = Pico.est.HighCol + sx;
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)
156 static void ParseSpritesM4(int scanline)
158 struct PicoVideo *pv = &Pico.video;
160 int xoff = line_offset;
161 int sprite_base, addr_mask;
162 int zoomed = sprites_zoom & 0x1; // zoomed sprites, e.g. Earthworm Jim
167 xoff -= 8; // sprite shift
168 if (Pico.m.hardware & PMS_HW_LCD)
169 xoff -= 48; // GG LCD, adjust to center 160 px
171 sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
172 if (sprites_zoom & 2) {
173 addr_mask = 0xfe; h = 16;
175 addr_mask = 0xff; h = 8;
178 sprite_base = (pv->reg[6] & 4) << (13-2-1);
180 m = pv->status & SR_C;
181 memset(sprites_map, 0, sizeof(sprites_map));
182 for (i = s = 0; i < 64; i++)
186 if (y == 0xd0 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)))
190 y &= ~zoomed; // zoomed sprites apparently only on even lines, see GG Tarzan
191 if (y + h <= scanline || scanline < y)
192 continue; // not on this line
194 if (scanline >= 0) sprites_status |= SR_SOVR;
195 if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
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));
203 if (pv->reg[1] & 0x40) {
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);
209 // no collision detection in 1st column if it's masked
210 if (pv->reg[0] & 0x20)
217 sprites_status |= SR_C;
221 static void DrawSpritesM4(void)
224 int zoomed = sprites_zoom & 0x1; // zoomed sprites, e.g. Earthworm Jim
227 // now draw all sprites backwards
228 for (--s; s >= 0; s--) {
229 pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s]));
230 if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10);
231 else TileNormSprM4(sprites_x[s], pack, 0x10);
235 // cells_dx, tilex_ty merged to reduce register pressure
236 static void DrawStripM4(const u16 *nametab, int cells_dx, int tilex_ty)
239 int addr = 0, pal = 0;
241 // Draw tiles across screen:
242 for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
247 code = nametab[tilex_ty & 0x1f];
249 if (code != oldcode) {
251 // Get tile address/2:
252 addr = (code & 0x1ff) << 4;
253 addr += tilex_ty >> 16;
255 addr ^= 0xe; // Y-flip
257 pal = (code>>7) & 0x30; // prio | palette select
260 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); // Get 4 bitplanes / 8 pixels
261 if (pack == 0) TileBGM4(cells_dx, pal);
262 else if (code & 0x0200) TileFlipBGM4(cells_dx, pack, pal);
263 else TileNormBGM4(cells_dx, pack, pal);
267 static void DrawDisplayM4(int scanline)
269 struct PicoVideo *pv = &Pico.video;
270 u16 *nametab, *nametab2;
271 int line, tilex, dx, ty, cells;
272 int cellskip = 0; // XXX
275 // Find the line in the name table
276 line = pv->reg[9] + scanline; // vscroll + scanline
279 nametab = PicoMem.vram;
280 if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) {
283 nametab += ((pv->reg[2] & 0x0c) << (10-1)) + (0x700 >> 1);
285 while (line >= 224) line -= 224;
286 nametab += (pv->reg[2] & 0x0e) << (10-1);
287 // old SMS only, masks line:7 with reg[2]:0 for address calculation
288 //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f;
290 nametab2 = nametab + ((scanline>>3) << (6-1));
291 nametab = nametab + ((line>>3) << (6-1));
293 dx = xscroll; // hscroll
294 if (scanline < 16 && (pv->reg[0] & 0x40))
295 dx = 0; // hscroll disabled for top 2 rows (e.g. Fantasy Zone II)
297 tilex = (32 - (dx >> 3) + cellskip) & 0x1f;
298 ty = (line & 7) << 1; // Y-Offset into tile
299 cells = maxcells - cellskip - 1;
306 if (!(pv->debug_p & PVD_KILL_B)) {
307 if (Pico.m.hardware & PMS_HW_LCD) {
308 // on GG render only the center 160 px, but mind hscroll
309 DrawStripM4(nametab , (dx-8) | ((cells-11)<< 16),(tilex+5) | (ty << 16));
310 } else if (pv->reg[0] & 0x80) {
311 // vscroll disabled for rightmost 8 columns (e.g. Gauntlet)
312 int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7;
313 DrawStripM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16));
314 DrawStripM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17));
316 DrawStripM4(nametab , dx | ( cells << 16), tilex | (ty << 16));
320 if (!(pv->debug_p & PVD_KILL_S_LO))
323 if ((pv->reg[0] & 0x20) && !(Pico.m.hardware & PMS_HW_LCD)) {
324 // first column masked with background, caculate offset to start of line
325 dx = line_offset / 4;
326 ty = ((pv->reg[7]&0x0f)|0x10) * 0x01010101;
327 ((u32 *)Pico.est.HighCol)[dx] = ((u32 *)Pico.est.HighCol)[dx+1] = ty;
337 #define TMS_PIXELBG(x,p) \
338 t = (pack>>(7-p)) & 0x01; \
339 t = (pal >> (t << 2)) & 0x0f; \
343 static void TileNormBgM1(u16 sx, unsigned int pack, int pal) /* Text */
345 u8 *pd = Pico.est.HighCol + sx;
356 static void TileNormBgM2(u16 sx, int pal) /* Multicolor */
358 u8 *pd = Pico.est.HighCol + sx;
359 unsigned int pack = 0xf0;
372 static void TileNormBgMg(u16 sx, unsigned int pack, int pal) /* Graphics */
374 u8 *pd = Pico.est.HighCol + sx;
389 #define TMS_PIXELSP(x,p) \
390 t = (pack>>(7-p)) & 0x01; \
394 static void TileNormSprTMS(u16 sx, unsigned int pack, int pal)
396 u8 *pd = Pico.est.HighCol + sx;
409 static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal)
411 u8 *pd = Pico.est.HighCol + sx;
432 static void ParseSpritesTMS(int scanline)
434 struct PicoVideo *pv = &Pico.video;
438 int sprite_base, addr_mask;
439 int zoomed = sprites_zoom & 0x1; // zoomed sprites
444 sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7f) << 7);
445 if (sprites_zoom & 2) {
446 addr_mask = 0xfc; h = 16;
448 addr_mask = 0xff; h = 8;
451 sprite_base = (pv->reg[6] & 0x7) << 11;
453 m = pv->status & SR_C;
454 memset(sprites_map, 0, sizeof(sprites_map));
455 /* find sprites on this scanline */
456 for (i = s = 0; i < 32; i++)
459 y = sat[MEM_LE2(4*i)];
465 if (y + h <= scanline || scanline < y)
466 continue; // not on this line
468 if (scanline >= 0) sprites_status |= SR_SOVR | i;
469 if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
472 x = sat[MEM_LE2(4*i+1)] + xoff;
473 if (sat[MEM_LE2(4*i+3)] & 0x80)
476 sprites_c[s] = sat[MEM_LE2(4*i+3)] & 0x0f;
478 sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) +
479 ((scanline - y) >> zoomed);
480 if (pv->reg[1] & 0x40) {
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);
486 x += (zoomed ? 16:8);
487 if (sprites_c[s] && (sprites_zoom & 0x2) && x > 0 && x < 8+256) {
488 pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
489 if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed);
495 sprites_status |= SR_C;
499 /* Draw sprites into a scanline, max 4 */
500 static void DrawSpritesTMS(void)
503 int zoomed = sprites_zoom & 0x1; // zoomed sprites
506 // now draw all sprites backwards
507 for (--s; s >= 0; s--) {
508 int x, c, w = (zoomed ? 16: 8);
511 // c may be 0 (transparent): sprite invisible
513 pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
514 if (zoomed) TileDoubleSprTMS(x, pack, c);
515 else TileNormSprTMS(x, pack, c);
517 if (c && (sprites_zoom & 0x2) && (x+=w) > 0 && x < 8+256) {
518 pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
519 if (zoomed) TileDoubleSprTMS(x, pack, c);
520 else TileNormSprTMS(x, pack, c);
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)
532 // Draw tiles across screen:
533 for (; cells_dx >= 0; cells_dx += 6, tilex_ty++, cells_dx -= 0x10000)
535 unsigned int pack, pal;
538 code = nametab[tilex_ty & 0x3f];
539 pal = Pico.video.reg[7];
540 pack = pattab[code << 3];
541 TileNormBgM1(cells_dx, pack, pal);
545 /* Draw a scanline */
546 static void DrawDisplayM1(int scanline)
548 struct PicoVideo *pv = &Pico.video;
549 u8 *nametab, *pattab;
550 int tilex, dx, cells;
551 int cellskip = 0; // XXX
553 unsigned mask = pv->reg[0] & 0x2 ? 0x2000 : 0x3800; // M3: 2 bits table select
555 // name, color, pattern table:
556 nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00);
557 pattab = PicoMem.vramb + ((pv->reg[4]<<11) & mask);
558 pattab += ((scanline>>6) << 11) & ~mask; // table select bits for M3
560 nametab += ((scanline>>3) * maxcells);
561 pattab += (scanline & 0x7);
563 tilex = cellskip & 0x1f;
564 cells = maxcells - cellskip - 1;
565 dx = 8 + (cellskip << 3) + line_offset;
568 if (!(pv->debug_p & PVD_KILL_B))
569 DrawStripM1(nametab, pattab, dx | (cells << 16), tilex | (scanline << 16));
573 /* Mode 2 - Multicolor */
574 /*=====================*/
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)
579 // Draw tiles across screen:
580 for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
585 code = nametab[tilex_ty & 0x1f];
586 pal = pattab[code << 3];
587 TileNormBgM2(cells_dx, pal);
591 /* Draw a scanline */
592 static void DrawDisplayM2(int scanline)
594 struct PicoVideo *pv = &Pico.video;
595 u8 *nametab, *pattab;
596 int tilex, dx, cells;
597 int cellskip = 0; // XXX
599 unsigned mask = pv->reg[0] & 0x2 ? 0x2000 : 0x3800; // M3: 2 bits table select
601 // name, color, pattern table:
602 nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00);
603 pattab = PicoMem.vramb + ((pv->reg[4]<<11) & mask);
604 pattab += ((scanline>>6) << 11) & ~mask; // table select bits for M3
606 nametab += (scanline>>3) << 5;
607 pattab += (scanline>>2) & 0x7;
609 tilex = cellskip & 0x1f;
610 cells = maxcells - cellskip - 1;
611 dx = (cellskip << 3) + line_offset;
614 if (!(pv->debug_p & PVD_KILL_B))
615 DrawStripM2(nametab, pattab, dx | (cells << 16), tilex | (scanline << 16));
618 if (!(pv->debug_p & PVD_KILL_S_LO))
623 /* Mode 3 - Graphics II */
624 /*======================*/
626 /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
627 static void DrawStripM3(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty)
629 // Draw tiles across screen:
630 for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
632 unsigned int pack, pal;
635 code = nametab[tilex_ty & 0x1f] << 3;
638 TileNormBgMg(cells_dx, pack, pal);
642 /* Draw a scanline */
643 static void DrawDisplayM3(int scanline)
645 struct PicoVideo *pv = &Pico.video;
646 u8 *nametab, *coltab, *pattab;
647 int tilex, dx, cells;
648 int cellskip = 0; // XXX
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);
656 nametab += ((scanline>>3) << 5);
657 coltab += ((scanline>>6) <<11) + (scanline & 0x7);
658 pattab += ((scanline>>6) <<11) + (scanline & 0x7);
660 tilex = cellskip & 0x1f;
661 cells = maxcells - cellskip - 1;
662 dx = (cellskip << 3) + line_offset;
665 if (!(pv->debug_p & PVD_KILL_B))
666 DrawStripM3(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16));
669 if (!(pv->debug_p & PVD_KILL_S_LO))
674 /* Mode 0 - Graphics I */
675 /*=====================*/
677 /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
678 static void DrawStripM0(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty)
680 // Draw tiles across screen:
681 for (; cells_dx >= 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
683 unsigned int pack, pal;
686 code = nametab[tilex_ty & 0x1f];
687 pal = coltab[code >> 3];
688 pack = pattab[code << 3];
689 TileNormBgMg(cells_dx, pack, pal);
693 /* Draw a scanline */
694 static void DrawDisplayM0(int scanline)
696 struct PicoVideo *pv = &Pico.video;
697 u8 *nametab, *coltab, *pattab;
698 int tilex, dx, cells;
699 int cellskip = 0; // XXX
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);
707 nametab += (scanline>>3) << 5;
708 pattab += (scanline & 0x7);
710 tilex = cellskip & 0x1f;
711 cells = maxcells - cellskip - 1;
712 dx = (cellskip << 3) + line_offset;
715 if (!(pv->debug_p & PVD_KILL_B))
716 DrawStripM0(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16));
719 if (!(pv->debug_p & PVD_KILL_S_LO))
727 static void FinalizeLineRGB555SMS(int line);
728 static void FinalizeLine8bitSMS(int line);
730 void PicoFrameStartSMS(void)
732 struct PicoEState *est = &Pico.est;
733 int lines = 192, columns = 256, loffs, coffs;
736 loffs = screen_offset = 24; // 192 lines is really 224 with top/bottom bars
737 est->rendstatus = PDRAW_32_COLS;
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);
746 // SMS2 only 224/240 line modes, e.g. Micro Machines
748 est->rendstatus |= PDRAW_30_ROWS;
749 loffs = screen_offset = 0;
753 loffs = screen_offset = 8;
758 Pico.m.hardware &= ~PMS_HW_TMS;
759 if (PicoIn.tmsPalette || (PicoIn.AHW & (PAHW_SG|PAHW_SC)))
760 Pico.m.hardware |= PMS_HW_TMS;
762 // Copy LCD enable flag for easier handling
763 Pico.m.hardware &= ~PMS_HW_LCD;
764 if ((PicoIn.opt & POPT_EN_GG_LCD) && (PicoIn.AHW & PAHW_GG)) {
765 Pico.m.hardware |= PMS_HW_LCD;
767 // GG LCD always has 160x144 regardless of settings
772 if ((mode & 4) && (Pico.video.reg[0] & 0x20)) {
773 // SMS mode 4 with 1st column blanked
774 est->rendstatus |= PDRAW_SMS_BLANK_1;
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
784 est->rendstatus |= PDRAW_SOFTSCALE;
787 } else if (!(PicoIn.opt & POPT_DIS_32C_BORDER)) {
788 est->rendstatus |= PDRAW_BORDER_32;
789 line_offset -= coffs;
790 coffs = (320-columns) / 2;
791 if (FinalizeLineSMS == NULL)
792 line_offset += coffs; // ... else centering done in FinalizeLine
795 if (est->rendstatus != rendstatus_old || lines != rendlines) {
796 // mode_change() might reset rendstatus_old by calling SetOutFormat
797 int rendstatus = est->rendstatus;
798 emu_video_mode_change(loffs, lines, coffs, columns);
799 rendstatus_old = rendstatus;
804 est->HighCol = HighColBase + screen_offset * HighColIncrement;
805 est->DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
807 if (FinalizeLineSMS == FinalizeLine8bitSMS) {
808 Pico.m.dirtyPal = (Pico.m.dirtyPal || est->SonicPalCount ? 2 : 0);
809 memcpy(est->SonicPal, PicoMem.cram, 0x40*2);
811 est->SonicPalCount = 0;
814 void PicoParseSATSMS(int line)
816 if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line);
817 else ParseSpritesTMS(line);
820 void PicoLineSMS(int line)
822 int skip = skip_next_line;
824 int first = 48 - screen_offset;
826 // GG LCD, render only visible part of screen
827 if ((Pico.m.hardware & PMS_HW_LCD) && (line < first || line >= first+144))
830 if (PicoScanBegin != NULL && skip == 0)
831 skip = PicoScanBegin(line + screen_offset);
834 skip_next_line = skip - 1;
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
841 if (Pico.video.reg[1] & 0x40) {
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
845 else if (Pico.video.reg[0] & 0x02) DrawDisplayM3(line);
846 else DrawDisplayM0(line);
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);
851 xscroll = Pico.video.reg[8];
853 if (FinalizeLineSMS != NULL)
854 FinalizeLineSMS(line);
856 if (PicoScanEnd != NULL)
857 skip_next_line = PicoScanEnd(line + screen_offset);
860 Pico.est.HighCol += HighColIncrement;
861 Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
864 /* Palette for TMS9918 mode, see https://www.smspower.org/Development/Palette */
865 // RGB values: #000000 #000000 #21c842 #5edc78 #5455ed #7d76fc #d4524d #42ebf5
866 // #fc5554 #ff7978 #d4c154 #e6ce80 #21b03b #c95bba #cccccc #ffffff
867 static u16 tmspal[] = {
869 0x0000, 0x0000, 0x00a0, 0x00f0, 0x0a00, 0x0f00, 0x0005, 0x0ff0,
870 0x000a, 0x000f, 0x00aa, 0x00ff, 0x0050, 0x0f0f, 0x0aaa, 0x0fff,
872 0x0000, 0x0000, 0x04c2, 0x07d6, 0x0e55, 0x0f77, 0x055c, 0x0ee4,
873 0x055f, 0x077f, 0x05bc, 0x08ce, 0x03a2, 0x0b5c, 0x0ccc, 0x0fff,
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,
879 void PicoDoHighPal555SMS(void)
881 u32 *spal = (void *)Pico.est.SonicPal;
882 u32 *dpal = (void *)Pico.est.HighPal;
883 unsigned int cnt = Pico.est.SonicPalCount+1;
887 if (FinalizeLineSMS == FinalizeLineRGB555SMS || Pico.m.dirtyPal == 2)
890 // use hardware palette if not in 8bit accurate mode
891 if (FinalizeLineSMS != FinalizeLine8bitSMS)
892 spal = (void *)PicoMem.cram;
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 */
896 for (j = cnt; j > 0; j--) {
897 if (!(Pico.video.reg[0] & 0x4)) // fixed palette in TMS modes
898 spal = (u32 *)tmspal + (Pico.m.hardware & PMS_HW_TMS ? 16/2:0);
899 for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
901 #if defined(USE_BGR555)
902 t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3);
903 t |= (t >> 4) & 0x04210421;
904 #elif defined(USE_BGR565)
905 t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4);
906 t |= (t >> 4) & 0x08610861;
908 t = ((t & 0x000f000f)<<12)| ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7);
909 t |= (t >> 4) & 0x08610861;
913 memcpy(dpal, dpal-0x20/2, 0x20*2); // for prio bit
914 spal += 0x20/2, dpal += 0x20/2;
916 Pico.est.HighPal[0xe0] = 0;
919 static void FinalizeLineRGB555SMS(int line)
922 PicoDoHighPal555SMS();
924 // standard FinalizeLine can finish it for us,
925 // with features like scaling and such
926 FinalizeLine555(0, line, &Pico.est);
929 static void FinalizeLine8bitSMS(int line)
931 FinalizeLine8bit(0, line, &Pico.est);
934 void PicoDrawSetOutputSMS(pdso_t which)
938 case PDF_8BIT: FinalizeLineSMS = FinalizeLine8bitSMS; break;
939 case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break;
940 default: FinalizeLineSMS = NULL; // no multiple palettes, no scaling
941 PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break;
947 // vim:shiftwidth=2:ts=2:expandtab