Browse Source

sms renderer, fix unaligned bg-filled tiles
core+gp2x+psp, fix handling mid-frame palette changes
platform, fix vertical scaling, aspect ratio handling (gp2x, psp)
libretro, various fixes (input handling, aspect ratio)
platform, include current dir in bios search path

root 2 years ago
parent
commit
84d01c5bba

+ 2 - 2
pico/draw.c

@@ -1894,10 +1894,10 @@ PICO_INTERNAL void PicoFrameStart(void)
 
   if (FinalizeLine == FinalizeLine8bit) {
     // make a backup of the current palette in case Sonic mode is detected later
-    Pico.est.SonicPalCount = 0;
-    Pico.m.dirtyPal = (Pico.m.dirtyPal ? 2 : 0); // mark as dirty but copied
+    Pico.m.dirtyPal = (Pico.m.dirtyPal || Pico.est.SonicPalCount ? 2 : 0);
     blockcpy(Pico.est.SonicPal, PicoMem.cram, 0x40*2);
   }
+  Pico.est.SonicPalCount = 0;
 }
 
 static void DrawBlankedLine(int line, int offs, int sh, int bgc)

+ 21 - 16
pico/mode4.c

@@ -60,8 +60,14 @@ static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed)
 
 static void TileBGM4(u16 sx, int pal)
 {
-  u32 *pd = (u32 *)(Pico.est.HighCol + sx);
-  pd[0] = pd[1] = pal * 0x01010101;
+  if (sx & 3) {
+    u8 *pd = (u8 *)(Pico.est.HighCol + sx);
+    pd[0] = pd[1] = pd[2] = pd[3] = pal;
+    pd[4] = pd[5] = pd[6] = pd[7] = pal;
+  } else {
+    u32 *pd = (u32 *)(Pico.est.HighCol + sx);
+    pd[0] = pd[1] = pal * 0x01010101;
+  }
 }
 
 // 8 pixels are arranged in 4 bitplane bytes in a 32 bit word. To pull the
@@ -650,10 +656,10 @@ void PicoFrameStartSMS(void)
   Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
 
   if (FinalizeLineSMS == FinalizeLine8bitSMS) {
-    Pico.est.SonicPalCount = 0;
-    Pico.m.dirtyPal = (Pico.m.dirtyPal ? 2 : 0);
-    memcpy(Pico.est.SonicPal, PicoMem.cram, 0x20*2);
+    Pico.m.dirtyPal = (Pico.m.dirtyPal || Pico.est.SonicPalCount ? 2 : 0);
+    memcpy(Pico.est.SonicPal, PicoMem.cram, 0x40*2);
   }
+  Pico.est.SonicPalCount = 0;
 }
 
 void PicoParseSATSMS(int line)
@@ -701,13 +707,12 @@ norender:
 
 /* Fixed palette for TMS9918 modes */
 static u16 tmspal[32] = {
-#if 1  // SMS palette
+  // SMS palette
   0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
   0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
-#else  // TMS palette
+  // TMS palette
   0x0000, 0x0000, 0x04c2, 0x07d5, 0x0e55, 0x0f77, 0x045d, 0x0fe4,
   0x055f, 0x077f, 0x05cd, 0x08ce, 0x03b2, 0x0b5c, 0x0ccc, 0x0fff,
-#endif
 };
 
 void PicoDoHighPal555SMS(void)
@@ -731,18 +736,18 @@ void PicoDoHighPal555SMS(void)
     if (!(Pico.video.reg[0] & 0x4))
       spal = (u32 *)tmspal; // fixed palette in TMS modes
     for (i = 0x20/2; i > 0; i--, spal++, dpal++) { 
-     t = *spal;
+      t = *spal;
 #if defined(USE_BGR555)
-     t = ((t & 0x000f000f)<< 1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3);
-     t |= (t >> 4) & 0x04210421;
+      t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3);
+      t |= (t >> 4) & 0x04210421;
 #elif defined(USE_BGR565)
-     t = ((t & 0x000f000f)<< 1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4);
-     t |= (t >> 4) & 0x08610861;
+      t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4);
+      t |= (t >> 4) & 0x08610861;
 #else
-     t = ((t & 0x000f000f)<<12) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7);
-     t |= (t >> 4) & 0x08610861;
+      t = ((t & 0x000f000f)<<12)| ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7);
+      t |= (t >> 4) & 0x08610861;
 #endif
-     *dpal = t;
+      *dpal = t;
     }
     memcpy(dpal, dpal-0x20/2, 0x20*2); // for prio bit
     spal += 0x20/2, dpal += 0x20/2;

+ 3 - 2
pico/sms.c

@@ -56,18 +56,19 @@ static void vdp_data_write(unsigned char d)
   struct PicoVideo *pv = &Pico.video;
 
   if (pv->type == 3) {
+    // cram. 32 on SMS, but 64 on MD. Fill 2nd half of cram for prio bit mirror
     if (Pico.m.hardware & 0x1) { // GG, same layout as MD
       unsigned a = pv->addr & 0x3f;
       if (a & 0x1) { // write complete color on high byte write
         u16 c = ((d&0x0f) << 8) | Pico.ms.vdp_buffer;
         if (PicoMem.cram[a >> 1] != c) Pico.m.dirtyPal = 1;
-        PicoMem.cram[a >> 1] = c;
+        PicoMem.cram[a >> 1] = PicoMem.cram[(a >> 1)+0x20] = c;
       }
     } else { // SMS, convert to MD layout (00BbGgRr to 0000BbBbGgGgRrRr)
       unsigned a = pv->addr & 0x1f;
       u16 c = ((d&0x30)<<6) + ((d&0x0c)<<4) + ((d&0x03)<<2);
       if (PicoMem.cram[a] != (c | (c>>2))) Pico.m.dirtyPal = 1;
-      PicoMem.cram[a] = c | (c>>2);
+      PicoMem.cram[a] = PicoMem.cram[a+0x20] = c | (c>>2);
     }
   } else {
     PicoMem.vramb[MEM_LE2(pv->addr)] = d;

+ 1 - 1
platform/common/arm_utils.h

@@ -1,5 +1,5 @@
 
-void bgr444_to_rgb32(void *to, void *from);
+void bgr444_to_rgb32(void *to, void *from, unsigned entries);
 void bgr444_to_rgb32_sh(void *to, void *from);
 
 void vidcpy_m2(void *dest, void *src, int m32col, int with_32c_border);

+ 2 - 2
platform/common/arm_utils.s

@@ -62,12 +62,12 @@
 .endm
 
 
-.global bgr444_to_rgb32 @ void *to, void *from
+.global bgr444_to_rgb32 @ void *to, void *from, unsigned entries
 
 bgr444_to_rgb32:
     stmfd   sp!, {r4-r7,lr}
 
-    mov     r12, #0x40>>3 @ repeats
+    mov     r12, r2, lsr #3 @ repeats
     mov     lr, #0x00f00000
     orr     lr, lr, #0x00f0
 

+ 10 - 0
platform/common/emu.c

@@ -219,6 +219,16 @@ static const char *find_bios(int *region, const char *cd_fname)
 		strcat(static_buff, ".zip");
 		f = fopen(static_buff, "rb");
 		if (f) break;
+
+		strcpy(static_buff, files[i]);
+		strcat(static_buff, ".bin");
+		f = fopen(static_buff, "rb");
+		if (f) break;
+
+		static_buff[strlen(static_buff) - 4] = 0;
+		strcat(static_buff, ".zip");
+		f = fopen(static_buff, "rb");
+		if (f) break;
 	}
 
 	/** Check Bios files in <rom path>/bios */

+ 39 - 12
platform/gp2x/emu.c

@@ -256,8 +256,11 @@ static int ld_left, ld_lines; // numbers in Q1 format
 
 static int EmuScanBegin16_ld(unsigned int num)
 {
-	if ((signed int)(ld_counter - num) > 100)
-		ld_counter = 0;
+	if ((signed int)(ld_counter - num) > 100) {
+		// vsync, offset so that the upscaled image is centered
+		ld_counter = 120 - (120-num) * (ld_lines+2)/ld_lines;
+		ld_left = ld_lines;
+	}
 
 	if (emu_scan_begin)
 		return emu_scan_begin(ld_counter);
@@ -301,26 +304,26 @@ static int make_local_pal_md(int fast_mode)
 	int pallen = 0x100;
 
 	if (fast_mode) {
-		bgr444_to_rgb32(localPal, PicoMem.cram);
+		bgr444_to_rgb32(localPal, PicoMem.cram, 64);
 		pallen = 0x40;
 		Pico.m.dirtyPal = 0;
 	}
 	else if (Pico.est.rendstatus & PDRAW_SONIC_MODE) { // mid-frame palette changes
 		switch (Pico.est.SonicPalCount) {
-		case 3: bgr444_to_rgb32(localPal+0xc0, Pico.est.SonicPal+0xc0);
-		case 2: bgr444_to_rgb32(localPal+0x80, Pico.est.SonicPal+0x80);
-		case 1: bgr444_to_rgb32(localPal+0x40, Pico.est.SonicPal+0x40);
-		default:bgr444_to_rgb32(localPal, Pico.est.SonicPal);
+		case 3: bgr444_to_rgb32(localPal+0xc0, Pico.est.SonicPal+0xc0, 64);
+		case 2: bgr444_to_rgb32(localPal+0x80, Pico.est.SonicPal+0x80, 64);
+		case 1: bgr444_to_rgb32(localPal+0x40, Pico.est.SonicPal+0x40, 64);
+		default:bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
 		}
 		pallen = (Pico.est.SonicPalCount+1)*0x40;
 	}
 	else if (Pico.video.reg[0xC] & 8) { // shadow/hilight mode
-		bgr444_to_rgb32(localPal, Pico.est.SonicPal);
+		bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
 		bgr444_to_rgb32_sh(localPal, Pico.est.SonicPal);
 		memcpy(localPal+0xc0, localPal, 0x40*4); // for spr prio mess
 	}
 	else {
-		bgr444_to_rgb32(localPal, Pico.est.SonicPal);
+		bgr444_to_rgb32(localPal, Pico.est.SonicPal, 64);
 		memcpy(localPal+0x40, localPal, 0x40*4); // for spr prio mess
 		memcpy(localPal+0x80, localPal, 0x80*4); // for spr prio mess
 	}
@@ -336,9 +339,27 @@ static int make_local_pal_md(int fast_mode)
 
 static int make_local_pal_sms(int fast_mode)
 {
-	bgr444_to_rgb32(localPal, PicoMem.cram);
-	Pico.m.dirtyPal = 0;
-	return 0x40;
+	static u16 tmspal[32] = {
+		// SMS palette for TMS modes
+		0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
+		0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
+	};
+	int i;
+	
+	if (!(Pico.video.reg[0] & 0x4)) {
+		for (i = Pico.est.SonicPalCount; i >= 0; i--) {
+			bgr444_to_rgb32(localPal+i*0x40, tmspal, 32);
+			memcpy(localPal+i*0x40+0x20, localPal+i*0x40, 0x20*4);
+		}
+	} else {
+		for (i = Pico.est.SonicPalCount; i >= 0; i--) {
+			bgr444_to_rgb32(localPal+i*0x40, Pico.est.SonicPal+i*0x40, 32);
+			memcpy(localPal+i*0x40+0x20, localPal+i*0x40, 0x20*4);
+		}
+	}
+	if (Pico.m.dirtyPal == 2)
+		Pico.m.dirtyPal = 0;
+	return (Pico.est.SonicPalCount+1)*0x40;
 }
 
 void pemu_finalize_frame(const char *fps, const char *notice)
@@ -568,6 +589,12 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 	int scalex = 320, scaley = 240;
 	int ln_offs = 0;
 
+	/* NTSC always has 224 visible lines, anything smaller has bars */
+	if (line_count < 224 && line_count > 144) {
+		start_line -= (224-line_count) /2;
+		line_count = 224;
+	}
+
 	/* line doubling for swscaling, also needed for bg frames */
 	if (currentConfig.vscaling == EOPT_SCALE_SW && line_count < 240) {
 		ld_lines = ld_left = 2*line_count / (240 - line_count);

+ 29 - 46
platform/libretro/libretro.c

@@ -94,9 +94,7 @@ static const float VOUT_PAR = 0.0;
 static const float VOUT_4_3 = (4.0f / 3.0f);
 static const float VOUT_CRT = (1.29911f);
 
-static bool show_overscan = false;
-
-/* Required to allow on the fly changes to 'show overscan' */
+/* Required to allow on the fly changes to 'renderer' */
 static int vm_current_start_line = -1;
 static int vm_current_line_count = -1;
 static int vm_current_start_col = -1;
@@ -657,17 +655,9 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
    if (vout_16bit)
       PicoDrawSetOutBuf(vout_buf, vout_width * 2);
 
-   if (show_overscan)
-   {
-      vout_height = line_count + (start_line * 2);
-      vout_offset = 0;
-   }
-   else
-   {
-      vout_height = line_count;
-      /* Note: We multiply by 2 here to account for pitch */
-      vout_offset = vout_width * start_line * 2;
-   }
+   vout_height = line_count;
+   /* Note: We multiply by 2 here to account for pitch */
+   vout_offset = vout_width * start_line * 2;
 
    /* Redundant sanity check... */
    vout_height = (vout_height > VOUT_MAX_HEIGHT) ?
@@ -764,7 +754,7 @@ void retro_get_system_info(struct retro_system_info *info)
 
 void retro_get_system_av_info(struct retro_system_av_info *info)
 {
-   int common_height;
+   float tv_height = (vout_height > 144 ? Pico.m.pal ? 240 : 224 : 144);
    float common_width;
 
    memset(info, 0, sizeof(*info));
@@ -776,11 +766,10 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
    info->geometry.max_height   = vout_height;
 
    common_width = vout_width;
-   common_height = vout_height >= 192 && vout_height <= 224 ? 224 : vout_height;
    if (vout_aspect != 0)
-      common_width = vout_aspect * common_height;
+      common_width = vout_aspect * tv_height;
 
-   info->geometry.aspect_ratio = common_width / common_height;
+   info->geometry.aspect_ratio = common_width / vout_height;
 }
 
 /* savestates */
@@ -1312,6 +1301,8 @@ bool retro_load_game(const struct retro_game_info *info)
    PsndRerate(0);
 
    PicoDrawSetOutFormat(vout_format, 0);
+   if (vout_format == PDF_8BIT)
+      PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);
 
    /* Setup retro memory maps */
    set_memory_maps();
@@ -1407,18 +1398,18 @@ void retro_reset(void)
 }
 
 static const unsigned short retro_pico_map[] = {
-   1 << GBTN_B,
-   1 << GBTN_A,
-   1 << GBTN_MODE,
-   1 << GBTN_START,
-   1 << GBTN_UP,
-   1 << GBTN_DOWN,
-   1 << GBTN_LEFT,
-   1 << GBTN_RIGHT,
-   1 << GBTN_C,
-   1 << GBTN_Y,
-   1 << GBTN_X,
-   1 << GBTN_Z,
+   [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << GBTN_B,
+   [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << GBTN_A,
+   [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << GBTN_MODE,
+   [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << GBTN_START,
+   [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << GBTN_UP,
+   [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << GBTN_DOWN,
+   [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << GBTN_LEFT,
+   [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << GBTN_RIGHT,
+   [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << GBTN_C,
+   [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << GBTN_Y,
+   [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << GBTN_X,
+   [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << GBTN_Z,
 };
 #define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
 
@@ -1451,7 +1442,6 @@ static void update_variables(bool first_run)
    double new_sound_rate;
    unsigned short old_snd_filter;
    int32_t old_snd_filter_range;
-   bool old_show_overscan;
 
    var.value = NULL;
    var.key = "picodrive_input1";
@@ -1579,15 +1569,6 @@ static void update_variables(bool first_run)
          PicoIn.opt &= ~POPT_DIS_SPRITE_LIM;
    }
 
-   old_show_overscan = show_overscan;
-   var.value = NULL;
-   var.key = "picodrive_overscan";
-   show_overscan = false;
-   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
-      if (strcmp(var.value, "enabled") == 0)
-         show_overscan = true;
-   }
-
    var.value = NULL;
    var.key = "picodrive_overclk68k";
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
@@ -1664,6 +1645,8 @@ static void update_variables(bool first_run)
       if (vout_format == PDF_NONE)
          PicoIn.opt |= POPT_ALT_RENDERER;
       PicoDrawSetOutFormat(vout_format, 0);
+      if (vout_format == PDF_8BIT)
+         PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);
    }
 
    var.value = NULL;
@@ -1681,7 +1664,7 @@ static void update_variables(bool first_run)
    }
 
    /* setup video if required */
-   if (show_overscan != old_show_overscan || vout_format != old_vout_format)
+   if (vout_format != old_vout_format)
    {
       if ((vm_current_start_line != -1) &&
           (vm_current_line_count != -1) &&
@@ -1819,15 +1802,15 @@ void retro_run(void)
        * with the fast renderer this is improving performance, at the expense of accuracy.
        */
       /* This section is mostly copied from pemu_finalize_frame in platform/linux/emu.c */
-      unsigned short *pd = (unsigned short *)vout_buf;
+      unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
       /* Skip the leftmost 8 columns (it is used as an overlap area for rendering) */
-      unsigned char *ps = Pico.est.Draw2FB + 8;
+      unsigned char *ps = Pico.est.Draw2FB + vm_current_start_line * 328 + 8;
       unsigned short *pal = Pico.est.HighPal;
       int x;
       if (Pico.m.dirtyPal)
          PicoDrawUpdateHighPal();
-      /* Copy up to the max height to include the overscan area, and skip the leftmost 8 columns again */
-      for (i = 0; i < VOUT_MAX_HEIGHT; i++, ps += 8) {
+      /* Copy, and skip the leftmost 8 columns again */
+      for (i = 0; i < vout_height; i++, ps += 8) {
          for (x = 0; x < vout_width; x+=4) {
             *pd++ = pal[*ps++];
             *pd++ = pal[*ps++];
@@ -1842,7 +1825,7 @@ void retro_run(void)
       unsigned short *pd = (unsigned short *)vout_buf;
       unsigned short *ps = (unsigned short *)vout_ghosting_buf;
       int y;
-      for (y = 0; y < VOUT_MAX_HEIGHT; y++) {
+      for (y = 0; y < vout_height; y++) {
          if (vout_ghosting == 1)
             v_blend(pd, ps, vout_width, p_075_round);
          else

+ 0 - 11
platform/libretro/libretro_core_options.h

@@ -174,17 +174,6 @@ struct retro_core_option_definition option_defs_us[] = {
       },
       "disabled"
    },
-   {
-      "picodrive_overscan",
-      "Show Overscan",
-      "Crop out the potentially random glitchy video output that would have been hidden by the bezel around the edge of a standard-definition television screen.",
-      {
-         { "disabled", NULL },
-         { "enabled",  NULL },
-         { NULL, NULL },
-      },
-      "disabled"
-   },
    {
       "picodrive_overclk68k",
       "68K Overclock",

+ 0 - 9
platform/libretro/libretro_core_options_intl.h

@@ -139,15 +139,6 @@ struct retro_core_option_definition option_defs_tr[] = {
       },
       "PAR"
    },
-   {
-      "picodrive_overscan",
-      "Tarama Taşmasını Göster",
-      "Standart tanımlı bir televizyon ekranının kenar çevresinde, çerçeve tarafından gizlenmiş olabilecek rastgele video çıkışını kesin.",
-      {
-         { NULL, NULL },
-      },
-      "disabled"
-   },
    {
       "picodrive_overclk68k",
       "68K Hızaşırtma",

+ 2 - 2
platform/linux/blit.c

@@ -10,13 +10,13 @@
 // to      00000000 rrr00000 ggg00000 bbb00000 ...
 // TODO: rm when gp2x/emu.c is no longer used
 
-void bgr444_to_rgb32(void *to, void *from)
+void bgr444_to_rgb32(void *to, void *from, unsigned entries)
 {
 	unsigned short *ps = from;
 	unsigned int   *pd = to;
 	int pixels;
 
-	for (pixels = 0x40; pixels; pixels--, ps++, pd++)
+	for (pixels = entries; pixels; pixels--, ps++, pd++)
 	{
 		*pd = ((*ps<<20)&0xe00000) | ((*ps<<8)&0xe000) | ((*ps>>4)&0xe0);
 		*pd |= *pd >> 3;

+ 37 - 18
platform/psp/emu.c

@@ -204,32 +204,42 @@ static void set_scaling_params(void)
 	*/
 }
 
+static void do_pal_update_sms(void)
+{
+	static u16 tmspal[32] = {
+		// SMS palette
+		0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0,
+		0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff,
+	};
+	int i;
+	
+	if (!(Pico.video.reg[0] & 0x4)) {
+		for (i = Pico.est.SonicPalCount; i >= 0; i--)
+			do_pal_convert(localPal+i*0x40, tmspal, currentConfig.gamma, currentConfig.gamma2);
+	} else {
+		for (i = Pico.est.SonicPalCount; i >= 0; i--)
+			do_pal_convert(localPal+i*0x40, Pico.est.SonicPal+i*0x40, currentConfig.gamma, currentConfig.gamma2);
+	}
+	if (Pico.m.dirtyPal == 2)
+		Pico.m.dirtyPal = 0;
+	need_pal_upload = 1;
+}
+
 static void do_pal_update(void)
 {
 	u32 *dpal=(void *)localPal;
 	int i;
 
-	//for (i = 0x3f/2; i >= 0; i--)
-	//	dpal[i] = ((spal[i]&0x000f000f)<< 1)|((spal[i]&0x00f000f0)<<3)|((spal[i]&0x0f000f00)<<4);
-	if (PicoIn.AHW & PAHW_SMS) {
-		u32 *spal = (void *)PicoMem.cram; 
-		for (i = 0; i < 0x20 / 2; i++) {
-			u32 t = spal[i];
-			t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)<<10);
-			t |= (t >> 2) | ((t >> 4) & 0x08610861);
-			dpal[i] = t;
-		}
-		Pico.m.dirtyPal = 0;
-	} else if (PicoIn.opt & POPT_ALT_RENDERER) {
+	if (PicoIn.opt & POPT_ALT_RENDERER) {
 		do_pal_convert(localPal, PicoMem.cram, currentConfig.gamma, currentConfig.gamma2);
 		Pico.m.dirtyPal = 0;
 	}
 	else if (Pico.est.rendstatus & PDRAW_SONIC_MODE)
 	{
 		switch (Pico.est.SonicPalCount) {
-		case 3: do_pal_convert(localPal+0xc0/2, Pico.est.SonicPal+0xc0, currentConfig.gamma, currentConfig.gamma2);
-		case 2: do_pal_convert(localPal+0x80/2, Pico.est.SonicPal+0x80, currentConfig.gamma, currentConfig.gamma2);
-		case 1:	do_pal_convert(localPal+0x40/2, Pico.est.SonicPal+0x40, currentConfig.gamma, currentConfig.gamma2);
+		case 3: do_pal_convert(localPal+0xc0, Pico.est.SonicPal+0xc0, currentConfig.gamma, currentConfig.gamma2);
+		case 2: do_pal_convert(localPal+0x80, Pico.est.SonicPal+0x80, currentConfig.gamma, currentConfig.gamma2);
+		case 1:	do_pal_convert(localPal+0x40, Pico.est.SonicPal+0x40, currentConfig.gamma, currentConfig.gamma2);
 		default:do_pal_convert(localPal, Pico.est.SonicPal, currentConfig.gamma, currentConfig.gamma2);
 		}
 	}
@@ -267,9 +277,12 @@ static void blitscreen_clut(void)
 	sceGuTexMode(is_16bit_mode() ? GU_PSM_5650:GU_PSM_T8,0,0,0);
 	sceGuTexImage(0,512,512,512,g_screen_ptr);
 
-	if (!is_16bit_mode() && Pico.m.dirtyPal)
-		do_pal_update();
-
+	if (!is_16bit_mode() && Pico.m.dirtyPal) {
+		if (PicoIn.AHW & PAHW_SMS)
+			do_pal_update_sms();
+		else
+			do_pal_update();
+	}
 
 	if (need_pal_upload) {
 		need_pal_upload = 0;
@@ -666,6 +679,12 @@ void plat_update_volume(int has_changed, int is_up)
 /* prepare for MD screen mode change */
 void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)
 {
+	/* NTSC always has 224 visible lines, anything smaller has bars */
+	if (line_count < 224 && line_count > 144) {
+		start_line -= (224-line_count) /2;
+		line_count = 224;
+	}
+
 	out_y = start_line; out_x = start_col;
 	out_h = line_count; out_w = col_count;