Browse Source

z80, some cz80 cleanup
sms, memory mapping improvements
sms vdp, add zoomed sprites, column vscroll blocking, mode 2 graphics

root 2 years ago
parent
commit
8e52ec95f8
9 changed files with 442 additions and 161 deletions
  1. 12 24
      cpu/cz80/cz80_opED.c
  2. 1 1
      package
  3. 1 1
      pico/debug.c
  4. 3 3
      pico/draw.c
  5. 8 3
      pico/media.c
  6. 4 2
      pico/memory.c
  7. 348 83
      pico/mode4.c
  8. 6 5
      pico/pico_int.h
  9. 59 39
      pico/sms.c

+ 12 - 24
cpu/cz80/cz80_opED.c

@@ -491,27 +491,23 @@ OP_LDX:
 -----------------------------------------*/
 
 	OPED(0xb0): // LDIR
-		if (zBC != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL++);
 			WRITE_MEM8(zDE++, val);
 			zBC--;
 			USE_CYCLES(21)
-		} while (zBC > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zBC && (CPU->ICount > -4) && !CPU->Status);
 		goto OP_LDXR;
 
 	OPED(0xb8): // LDDR
-		if (zBC != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL--);
 			WRITE_MEM8(zDE--, val);
 			zBC--;
 			USE_CYCLES(21)
-		} while (zBC > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zBC && (CPU->ICount > -4) && !CPU->Status);
 
 OP_LDXR:
 		F = zF & (SF | ZF | CF);
@@ -520,6 +516,7 @@ OP_LDXR:
 		if (zBC)
 		{
 			zF = F | VF;
+			PC -= 2;
 #if CZ80_EMULATE_R_EXACTLY
 			zR--;
 #endif
@@ -556,8 +553,6 @@ OP_CPX:
 -----------------------------------------*/
 
 	OPED(0xb1): // CPIR
-		if (zBC != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL++);
@@ -570,12 +565,10 @@ OP_CPX:
 			if (zBC) F |= VF;
 			zF = F;
 			USE_CYCLES(21)
-		} while (zBC > 1 && !(F & ZF) && (CPU->ICount > 0) && !CPU->Status);
+		} while (zBC && !(F & ZF) && (CPU->ICount > -4) && !CPU->Status);
 		goto OP_CPXR;
 
 	OPED(0xb9): // CPDR
-		if (zBC != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL--);
@@ -588,11 +581,12 @@ OP_CPX:
 			if (zBC) F |= VF;
 			zF = F;
 			USE_CYCLES(21)
-		} while (zBC > 1 && !(F & ZF) && (CPU->ICount > 0) && !CPU->Status);
+		} while (zBC && !(F & ZF) && (CPU->ICount > -4) && !CPU->Status);
 
 OP_CPXR:
 		if (zBC && !(F & ZF))
 		{
+			PC -= 2;
 #if CZ80_EMULATE_R_EXACTLY
 			zR--;
 #endif
@@ -631,27 +625,23 @@ OP_INX:
 -----------------------------------------*/
 
 	OPED(0xb2): // INIR
-		if (zB != 1)
-			PC -= 2;
 		do
 		{
 			val = IN(zBC);
 			zB--;
 			WRITE_MEM8(zHL++, val);
 			USE_CYCLES(21)
-		} while (zB > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zB && (CPU->ICount > -4) && !CPU->Status);
 		goto OP_INXR;
 
 	OPED(0xba): // INDR
-		if (zB != 1)
-			PC -= 2;
 		do
 		{
 			val = IN(zBC);
 			zB--;
 			WRITE_MEM8(zHL--, val);
 			USE_CYCLES(21)
-		} while (zB > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zB && (CPU->ICount > -4) && !CPU->Status);
 
 OP_INXR:
 		F = SZ[zB];
@@ -662,6 +652,7 @@ OP_INXR:
 		zF = F;
 		if (zB)
 		{
+			PC -= 2;
 #if CZ80_EMULATE_R_EXACTLY
 			zR--;
 #endif
@@ -700,27 +691,23 @@ OP_OUTX:
 -----------------------------------------*/
 
 	OPED(0xb3): // OTIR
-		if (zB != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL++);
 			zB--;
 			OUT(zBC, val);
 			USE_CYCLES(21)
-		} while (zB > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zB && (CPU->ICount > -4) && !CPU->Status);
 		goto OP_OTXR;
 
 	OPED(0xbb): // OTDR
-		if (zB != 1)
-			PC -= 2;
 		do
 		{
 			val = READ_MEM8(zHL--);
 			zB--;
 			OUT(zBC, val);
 			USE_CYCLES(21)
-		} while (zB > 1 && (CPU->ICount > 0) && !CPU->Status);
+		} while (zB && (CPU->ICount > -4) && !CPU->Status);
 
 OP_OTXR:
 		F = SZ[zB];
@@ -731,6 +718,7 @@ OP_OTXR:
 		zF = F;
 		if (zB)
 		{
+			PC -= 2;
 #if CZ80_EMULATE_R_EXACTLY
 			zR--;
 #endif

+ 1 - 1
package

@@ -32,7 +32,7 @@ Comment=MS/MD/MCD/32X emulator
 Exec=picodrive.sh %f
 Icon=picodrive
 Categories=emulators;
-SelectorFilter=zip,md,smd,sms,32x,cue,cso,chd' > opk/picodrive.funkey-s.desktop
+SelectorFilter=zip,bin,md,smd,sms,32x,cue,cso,chd' > opk/picodrive.funkey-s.desktop
 
 rm -f *.opk
 mksquashfs opk/ picodrive_v1.98_$(md5sum opk/PicoDrive | cut -c1-7)_funkey-s.opk -all-root -noappend -no-exports -no-xattrs

+ 1 - 1
pico/debug.c

@@ -212,7 +212,7 @@ void PDebugShowPalette(unsigned short *screen, int stride)
 
   Pico.m.dirtyPal = 1;
   if (PicoIn.AHW & PAHW_SMS)
-    PicoDoHighPal555M4();
+    PicoDoHighPal555SMS();
   else
     PicoDoHighPal555(1, 0, est);
   Pico.m.dirtyPal = 1;

+ 3 - 3
pico/draw.c

@@ -1988,7 +1988,7 @@ void PicoDrawUpdateHighPal(void)
       sh = 0; // no s/h support
 
     if (PicoIn.AHW & PAHW_SMS)
-      PicoDoHighPal555M4();
+      PicoDoHighPal555SMS();
     else if (FinalizeLine == FinalizeLine8bit)
       PicoDoHighPal555_8bit(sh, 0, est);
     else
@@ -2027,7 +2027,7 @@ void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode)
   }
   if (PicoIn.AHW & PAHW_32X)
     PicoDrawSetOutFormat32x(which, use_32x_line_mode);
-  PicoDrawSetOutputMode4(which);
+  PicoDrawSetOutputSMS(which);
   rendstatus_old = -1;
   Pico.m.dirtyPal = 1;
 }
@@ -2038,7 +2038,7 @@ void PicoDrawSetOutBufMD(void *dest, int increment)
     // kludge for no-copy mode, using ALT_RENDERER layout
     PicoDrawSetInternalBuf(dest, increment);
   } else if (FinalizeLine == NULL) {
-    PicoDrawSetInternalBuf(dest, increment); // needed for Mode4
+    PicoDrawSetInternalBuf(dest, increment); // needed for SMS
     PicoDraw2SetOutBuf(dest, increment);
   } else if (dest != NULL) {
     DrawLineDestBase = dest;

+ 8 - 3
pico/media.c

@@ -35,8 +35,8 @@ static int detect_media(const char *fname)
 {
   static const short sms_offsets[] = { 0x7ff0, 0x3ff0, 0x1ff0 };
   static const char *sms_exts[] = { "sms", "gg", "sg" };
-  static const char *md_exts[] = { "gen", "bin", "smd" };
-  char buff0[32], buff[32];
+  static const char *md_exts[] = { "gen", "smd" };
+  char buff0[512], buff[32];
   unsigned short *d16;
   pm_file *pmf;
   char ext[5];
@@ -56,7 +56,7 @@ static int detect_media(const char *fname)
   if (pmf == NULL)
     return PM_BAD_DETECT;
 
-  if (pm_read(buff0, 32, pmf) != 32) {
+  if (pm_read(buff0, 512, pmf) != 512) {
     pm_close(pmf);
     return PM_BAD_DETECT;
   }
@@ -111,6 +111,11 @@ extension_check:
     lprintf("bad MD reset vector, assuming SMS\n");
     goto looks_like_sms;
   }
+  d16 = (unsigned short *)(buff0 + 0x1a0);
+  if ((((d16[0] << 16) | d16[1]) & 0xffffff) != 0) {
+    lprintf("bad MD rom start, assuming SMS\n");
+    goto looks_like_sms;
+  }
 
 looks_like_md:
   pm_close(pmf);

+ 4 - 2
pico/memory.c

@@ -57,6 +57,10 @@ void z80_map_set(uptr *map, int start_addr, int end_addr,
     const void *func_or_mh, int is_func)
 {
   xmap_set(map, Z80_MEM_SHIFT, start_addr, end_addr, func_or_mh, is_func);
+#ifdef _USE_CZ80
+  if (!is_func)
+    Cz80_Set_Fetch(&CZ80, start_addr, end_addr, (FPTR)func_or_mh);
+#endif
 }
 
 void cpu68k_map_set(uptr *map, int start_addr, int end_addr,
@@ -1290,8 +1294,6 @@ static void z80_mem_setup(void)
   drZ80.z80_out = z80_md_out;
 #endif
 #ifdef _USE_CZ80
-  Cz80_Set_Fetch(&CZ80, 0x0000, 0x1fff, (FPTR)PicoMem.zram); // main RAM
-  Cz80_Set_Fetch(&CZ80, 0x2000, 0x3fff, (FPTR)PicoMem.zram); // mirror
   Cz80_Set_INPort(&CZ80, z80_md_in);
   Cz80_Set_OUTPort(&CZ80, z80_md_out);
 #endif

+ 348 - 83
pico/mode4.c

@@ -1,40 +1,45 @@
 /*
- * mode4/SMS renderer
+ * SMS renderer
  * (C) notaz, 2009-2010
+ * (C) kub, 2021
+ *
+ * currently supports VDP mode 4 (SMS and GG) and mode 2
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
  */
 /*
  * TODO:
- * - TMS9918 modes?
- * - gg mode?
- * - column scroll (reg 0 bit7)
- * - 224/240 line modes
- * - doubled sprites
+ * - other TMS9918 modes?
  */
 #include "pico_int.h"
 #include <platform/common/upscale.h>
 
-static void (*FinalizeLineM4)(int line);
+static void (*FinalizeLineSMS)(int line);
 static int skip_next_line;
 static int screen_offset, line_offset;
 
-static void TileBGM4(int sx, int pal)
+
+/* Mode 4 */
+/*========*/
+
+static void TileBGM4(u16 sx, int pal)
 {
   u32 *pd = (u32 *)(Pico.est.HighCol + sx);
   pd[0] = pd[1] = pal ? 0x10101010 : 0;
 }
 
+// 8 pixels are arranged to have 1 bit in each byte of a 32 bit word. To pull
+// the 4 bitplanes together multiply with each bit distance (multiples of 1<<7)
 #define PLANAR_PIXELL(x,p) \
-  t = pack & (0x80808080 >> p); \
-  t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \
+  t = (pack>>(7-p)) & 0x01010101; \
+  t = (t*0x10204080) >> 28; \
   pd[x] = pal|t;
 
-static void TileNormM4Low(int sx, unsigned int pack, int pal)
+static void TileNormLowM4(u16 sx, unsigned int pack, int pal)
 {
-  unsigned char *pd = Pico.est.HighCol + sx;
-  unsigned int t;
+  u8 *pd = Pico.est.HighCol + sx;
+  u32 t;
 
   PLANAR_PIXELL(0, 0)
   PLANAR_PIXELL(1, 1)
@@ -46,10 +51,10 @@ static void TileNormM4Low(int sx, unsigned int pack, int pal)
   PLANAR_PIXELL(7, 7)
 }
 
-static void TileFlipM4Low(int sx, unsigned int pack, int pal)
+static void TileFlipLowM4(u16 sx, unsigned int pack, int pal)
 {
-  unsigned char *pd = Pico.est.HighCol + sx;
-  unsigned int t;
+  u8 *pd = Pico.est.HighCol + sx;
+  u32 t;
 
   PLANAR_PIXELL(0, 7)
   PLANAR_PIXELL(1, 6)
@@ -62,16 +67,16 @@ static void TileFlipM4Low(int sx, unsigned int pack, int pal)
 }
 
 #define PLANAR_PIXEL(x,p) \
-  t = pack & (0x80808080 >> p); \
+  t = (pack>>(7-p)) & 0x01010101; \
   if (t) { \
-    t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \
+    t = (t*0x10204080) >> 28; \
     pd[x] = pal|t; \
   }
 
-static void TileNormM4(int sx, unsigned int pack, int pal)
+static void TileNormM4(u16 sx, unsigned int pack, int pal)
 {
-  unsigned char *pd = Pico.est.HighCol + sx;
-  unsigned int t;
+  u8 *pd = Pico.est.HighCol + sx;
+  u32 t;
 
   PLANAR_PIXEL(0, 0)
   PLANAR_PIXEL(1, 1)
@@ -83,10 +88,10 @@ static void TileNormM4(int sx, unsigned int pack, int pal)
   PLANAR_PIXEL(7, 7)
 }
 
-static void TileFlipM4(int sx, unsigned int pack, int pal)
+static void TileFlipM4(u16 sx, unsigned int pack, int pal)
 {
-  unsigned char *pd = Pico.est.HighCol + sx;
-  unsigned int t;
+  u8 *pd = Pico.est.HighCol + sx;
+  u32 t;
 
   PLANAR_PIXEL(0, 7)
   PLANAR_PIXEL(1, 6)
@@ -98,34 +103,59 @@ static void TileFlipM4(int sx, unsigned int pack, int pal)
   PLANAR_PIXEL(7, 0)
 }
 
-static void draw_sprites(int scanline)
+static void TileDoubleM4(int sx, unsigned int pack, int pal)
+{
+  u8 *pd = Pico.est.HighCol + sx;
+  u32 t;
+
+  PLANAR_PIXEL(0, 0)
+  PLANAR_PIXEL(1, 0)
+  PLANAR_PIXEL(2, 1)
+  PLANAR_PIXEL(3, 1)
+  PLANAR_PIXEL(4, 2)
+  PLANAR_PIXEL(5, 2)
+  PLANAR_PIXEL(6, 3)
+  PLANAR_PIXEL(7, 3)
+  PLANAR_PIXEL(8, 4)
+  PLANAR_PIXEL(9, 4)
+  PLANAR_PIXEL(10, 5)
+  PLANAR_PIXEL(11, 5)
+  PLANAR_PIXEL(12, 6)
+  PLANAR_PIXEL(13, 6)
+  PLANAR_PIXEL(14, 7)
+  PLANAR_PIXEL(15, 7)
+}
+
+static void DrawSpritesM4(int scanline)
 {
   struct PicoVideo *pv = &Pico.video;
   unsigned int sprites_addr[8];
   unsigned int sprites_x[8];
   unsigned int pack;
-  unsigned char *sat;
+  u8 *sat;
   int xoff = 8; // relative to HighCol, which is (screen - 8)
   int sprite_base, addr_mask;
+  int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim
   int i, s, h;
 
   if (pv->reg[0] & 8)
     xoff = 0;
   xoff += line_offset;
 
-  sat = (unsigned char *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
+  sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
   if (pv->reg[1] & 2) {
     addr_mask = 0xfe; h = 16;
   } else {
     addr_mask = 0xff; h = 8;
   }
+  if (zoomed) h *= 2;
   sprite_base = (pv->reg[6] & 4) << (13-2-1);
 
   for (i = s = 0; i < 64; i++)
   {
     int y;
-    y = sat[MEM_LE2(i)] + 1;
-    if (y == 0xd1)
+    y = (sat[MEM_LE2(i)] + 1) & 0xff;
+    if (y == 0xd1 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)))
       break;
     if (y + h <= scanline || scanline < y)
       continue; // not on this line
@@ -136,7 +166,7 @@ static void draw_sprites(int scanline)
 
     sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)];
     sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) +
-      ((scanline - y) << (2-1));
+      ((scanline - y) >> zoomed << (2-1));
     s++;
   }
 
@@ -147,30 +177,32 @@ static void draw_sprites(int scanline)
   // now draw all sprites backwards
   for (--s; s >= 0; s--) {
     pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s]));
-    TileNormM4(sprites_x[s], pack, 0x10);
+    if (zoomed) TileDoubleM4(sprites_x[s], pack, 0x10);
+    else        TileNormM4(sprites_x[s], pack, 0x10);
   }
 }
 
-
-// tilex_ty_prio merged to reduce register pressure
-static void draw_strip_low(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio)
+// cells_dx, tilex_ty merged to reduce register pressure
+static void DrawStripLowM4(const u16 *nametab, int cells_dx, int tilex_ty)
 {
   int oldcode = -1;
   int addr = 0, pal = 0;
 
   // Draw tiles across screen:
-  for (; cells > 0; dx += 8, tilex_ty_prio++, cells--)
+  for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
   {
     unsigned int pack;
     unsigned code;
 
-    code = nametab[tilex_ty_prio & 0x1f];
+    code = nametab[tilex_ty& 0x1f];
+    if (code & 0x1000) // priority high?
+      continue;
 
     if (code != oldcode) {
       oldcode = code;
       // Get tile address/2:
       addr = (code & 0x1ff) << 4;
-      addr += tilex_ty_prio >> 16;
+      addr += tilex_ty>> 16;
       if (code & 0x0400)
         addr ^= 0xe; // Y-flip
 
@@ -178,34 +210,34 @@ static void draw_strip_low(const unsigned short *nametab, int dx, int cells, int
     }
 
     pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); /* Get 4 bitplanes / 8 pixels */
-    if (pack == 0)          TileBGM4(dx, pal);
-    else if (code & 0x0200) TileFlipM4Low(dx, pack, pal);
-    else                    TileNormM4Low(dx, pack, pal);
+    if (pack == 0)          TileBGM4(cells_dx, pal);
+    else if (code & 0x0200) TileFlipLowM4(cells_dx, pack, pal);
+    else                    TileNormLowM4(cells_dx, pack, pal);
   }
 }
-// tilex_ty_prio merged to reduce register pressure
-static void draw_strip_high(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio)
+
+static void DrawStripHighM4(const u16 *nametab, int cells_dx, int tilex_ty)
 {
   int oldcode = -1, blank = -1; // The tile we know is blank
   int addr = 0, pal = 0;
 
   // Draw tiles across screen:
-  for (; cells > 0; dx += 8, tilex_ty_prio++, cells--)
+  for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
   {
     unsigned int pack;
     unsigned code;
 
-    code = nametab[tilex_ty_prio & 0x1f];
+    code = nametab[tilex_ty& 0x1f];
     if (code == blank)
       continue;
-    if ((code ^ tilex_ty_prio) & 0x1000) // priority differs?
+    if (!(code & 0x1000)) // priority low?
       continue;
 
     if (code != oldcode) {
       oldcode = code;
       // Get tile address/2:
       addr = (code & 0x1ff) << 4;
-      addr += tilex_ty_prio >> 16;
+      addr += tilex_ty>> 16;
       if (code & 0x0400)
         addr ^= 0xe; // Y-flip
 
@@ -217,15 +249,15 @@ static void draw_strip_high(const unsigned short *nametab, int dx, int cells, in
       blank = code;
       continue;
     }
-    if (code & 0x0200) TileFlipM4(dx, pack, pal);
-    else               TileNormM4(dx, pack, pal);
+    if (code & 0x0200) TileFlipM4(cells_dx, pack, pal);
+    else               TileNormM4(cells_dx, pack, pal);
   }
 }
 
 static void DrawDisplayM4(int scanline)
 {
   struct PicoVideo *pv = &Pico.video;
-  unsigned short *nametab;
+  u16 *nametab, *nametab2;
   int line, tilex, dx, ty, cells;
   int cellskip = 0; // XXX
   int maxcells = 32;
@@ -236,6 +268,7 @@ static void DrawDisplayM4(int scanline)
   // Find name table:
   nametab = PicoMem.vram;
   if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) {
+    // 224/240 line mode
     line &= 0xff;
     nametab += ((pv->reg[2] & 0x0c) << (10-1)) + (0x700 >> 1);
   } else {
@@ -244,11 +277,12 @@ static void DrawDisplayM4(int scanline)
     // old SMS only, masks line:7 with reg[2]:0 for address calculation
     //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f;
   }
-  nametab += (line>>3) << (6-1);
+  nametab2 = nametab + ((scanline>>3) << (6-1));
+  nametab  = nametab + ((line>>3)     << (6-1));
 
   dx = pv->reg[8]; // hscroll
   if (scanline < 16 && (pv->reg[0] & 0x40))
-    dx = 0; // hscroll disabled for top 2 rows
+    dx = 0; // hscroll disabled for top 2 rows (e.g. Fantasy Zone II)
 
   tilex = ((-dx >> 3) + cellskip) & 0x1f;
   ty = (line & 7) << 1; // Y-Offset into tile
@@ -261,41 +295,250 @@ static void DrawDisplayM4(int scanline)
   dx += line_offset;
 
   // low priority tiles
-  if (!(pv->debug_p & PVD_KILL_B))
-    draw_strip_low(nametab, dx, cells, tilex | 0x0000 | (ty << 16));
+  if (!(pv->debug_p & PVD_KILL_B)) {
+    if (pv->reg[0] & 0x80) {
+      // vscroll disabled for rightmost 8 columns (e.g. Gauntlet)
+      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline & 7;
+      DrawStripLowM4(nametab,  dx | ((cells-8) << 16), tilex  | (ty  << 16));
+      DrawStripLowM4(nametab2, dx2 |       (8  << 16), tilex2 | (ty2 << 17));
+    } else
+      DrawStripLowM4(nametab , dx | ( cells    << 16), tilex  | (ty  << 16));
+  }
 
   // sprites
   if (!(pv->debug_p & PVD_KILL_S_LO))
-    draw_sprites(scanline);
+    DrawSpritesM4(scanline);
 
   // high priority tiles (use virtual layer switch just for fun)
-  if (!(pv->debug_p & PVD_KILL_A))
-    draw_strip_high(nametab, dx, cells, tilex | 0x1000 | (ty << 16));
+  if (!(pv->debug_p & PVD_KILL_A)) {
+    if (pv->reg[0] & 0x80) {
+      int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline & 7;
+      DrawStripHighM4(nametab,  dx | ((cells-8) << 16), tilex  | (ty  << 16));
+      DrawStripHighM4(nametab2, dx2 |       (8  << 16), tilex2 | (ty2 << 17));
+    } else
+      DrawStripHighM4(nametab , dx | ( cells    << 16), tilex  | (ty  << 16));
+  }
 
   if (pv->reg[0] & 0x20) {
-    // first column masked, caculate offset to start of line
+    // first column masked with background, caculate offset to start of line
     dx = (dx&~0x1f) / 4;
-    ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = 0xe0e0e0e0;
+    ty = 0xe0e0e0e0; // really (pv->reg[7]&0x3f) * 0x01010101, but the looks...
+    ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = ty;
   }
 }
 
-static void FinalizeLineRGB555M4(int line);
-void PicoFrameStartMode4(void)
+
+/* Mode 2 */
+/*========*/
+
+/* Background */
+
+#define TMS_PIXELBG(x,p) \
+  t = (pack>>(7-p)) & 0x01; \
+  t = (pal >> (t << 2)) & 0x0f; \
+  pd[x] = t;
+
+static void TileNormBgM2(u16 sx, unsigned int pack, int pal)
+{
+  u8 *pd = Pico.est.HighCol + sx;
+  unsigned int t;
+
+  TMS_PIXELBG(0, 0)
+  TMS_PIXELBG(1, 1)
+  TMS_PIXELBG(2, 2)
+  TMS_PIXELBG(3, 3)
+  TMS_PIXELBG(4, 4)
+  TMS_PIXELBG(5, 5)
+  TMS_PIXELBG(6, 6)
+  TMS_PIXELBG(7, 7)
+}
+
+/* Sprites */
+
+#define TMS_PIXELSP(x,p) \
+  t = (pack>>(7-p)) & 0x01; \
+  if (t) \
+    pd[x] = pal;
+
+static void TileNormSprM2(u16 sx, unsigned int pack, int pal)
+{
+  u8 *pd = Pico.est.HighCol + sx;
+  unsigned int t;
+
+  TMS_PIXELSP(0, 0)
+  TMS_PIXELSP(1, 1)
+  TMS_PIXELSP(2, 2)
+  TMS_PIXELSP(3, 3)
+  TMS_PIXELSP(4, 4)
+  TMS_PIXELSP(5, 5)
+  TMS_PIXELSP(6, 6)
+  TMS_PIXELSP(7, 7)
+}
+
+static void TileDoubleSprM2(u16 sx, unsigned int pack, int pal)
+{
+  u8 *pd = Pico.est.HighCol + sx;
+  unsigned int t;
+
+  TMS_PIXELSP(0, 0)
+  TMS_PIXELSP(1, 0)
+  TMS_PIXELSP(2, 1)
+  TMS_PIXELSP(3, 1)
+  TMS_PIXELSP(4, 2)
+  TMS_PIXELSP(5, 2)
+  TMS_PIXELSP(6, 3)
+  TMS_PIXELSP(7, 3)
+  TMS_PIXELSP(8, 4)
+  TMS_PIXELSP(9, 4)
+  TMS_PIXELSP(10, 5)
+  TMS_PIXELSP(11, 5)
+  TMS_PIXELSP(12, 6)
+  TMS_PIXELSP(13, 6)
+  TMS_PIXELSP(14, 7)
+  TMS_PIXELSP(15, 7)
+}
+
+/* Draw sprites into a scanline, max 4 */
+static void DrawSpritesM2(int scanline)
+{
+  struct PicoVideo *pv = &Pico.video;
+  unsigned int sprites_addr[4];
+  unsigned int sprites_x[4];
+  unsigned int pack;
+  u8 *sat;
+  int xoff = 8; // relative to HighCol, which is (screen - 8)
+  int sprite_base, addr_mask;
+  int zoomed = pv->reg[1] & 0x1; // zoomed sprites
+  int i, s, h;
+
+  xoff += line_offset;
+
+  sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7);
+  if (pv->reg[1] & 2) {
+    addr_mask = 0xfc; h = 16;
+  } else {
+    addr_mask = 0xff; h = 8;
+  }
+  if (zoomed) h *= 2;
+
+  sprite_base = (pv->reg[6] & 0x7) << 11;
+
+  /* find sprites on this scanline */
+  for (i = s = 0; i < 32; i++)
+  {
+    int y;
+    y = (sat[MEM_LE2(4*i)] + 1) & 0xff;
+    if (y == 0xd1)
+      break;
+    if (y > 0xe0)
+      y -= 256;
+    if (y + h <= scanline || scanline < y)
+      continue; // not on this line
+    if (s >= 4) {
+      pv->status |= SR_SOVR | i;
+      break;
+    }
+
+    sprites_x[s] = 4*i;
+    sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) +
+      ((scanline - y) >> zoomed);
+    s++;
+  }
+
+  // really half-assed but better than nothing
+  if (s > 1)
+    pv->status |= SR_C;
+
+  // now draw all sprites backwards
+  for (--s; s >= 0; s--) {
+    int x, w = (zoomed ? 16: 8);
+    i = sprites_x[s];
+    x = sat[MEM_LE2(i+1)] + xoff;
+    if (sat[MEM_LE2(i+3)] & 0x80)
+      x -= 32;
+    if (x > 0) {
+      pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
+      if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+      else        TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+    }
+    if((pv->reg[1] & 0x2) && (x+=w) > 0) {
+      pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
+      if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+      else        TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf);
+    }
+  }
+}
+
+/* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
+static void DrawStripM2(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty)
+{
+  // Draw tiles across screen:
+  for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000)
+  {
+    unsigned int pack, pal;
+    unsigned code;
+
+    code = nametab[tilex_ty& 0x1f] << 3;
+    pal  = coltab[code];
+    pack = pattab[code];
+    TileNormBgM2(cells_dx, pack, pal);
+  }
+}
+
+/* Draw a scanline */
+static void DrawDisplayM2(int scanline)
+{
+  struct PicoVideo *pv = &Pico.video;
+  u8 *nametab, *coltab, *pattab;
+  int tilex, dx, cells;
+  int cellskip = 0; // XXX
+  int maxcells = 32;
+
+  // name, color, pattern table:
+  nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00);
+  coltab  = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x2000);
+  pattab  = PicoMem.vramb + ((pv->reg[4]<<11) & 0x2000);
+
+  nametab += ((scanline>>3) << 5);
+  coltab  += ((scanline>>6) <<11) + (scanline & 0x7);
+  pattab  += ((scanline>>6) <<11) + (scanline & 0x7);
+
+  tilex = cellskip & 0x1f;
+  cells = maxcells - cellskip;
+  dx = (cellskip << 3) + line_offset + 8;
+
+  // tiles
+  if (!(pv->debug_p & PVD_KILL_B))
+    DrawStripM2(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16));
+
+  // sprites
+  if (!(pv->debug_p & PVD_KILL_S_LO))
+    DrawSpritesM2(scanline);
+}
+
+
+/* Common/global */
+/*===============*/
+
+static void FinalizeLineRGB555SMS(int line);
+
+void PicoFrameStartSMS(void)
 {
   int lines = 192, columns = 256, coffs;
   skip_next_line = 0;
   screen_offset = 24;
   Pico.est.rendstatus = PDRAW_32_COLS;
 
-  if ((Pico.video.reg[0] & 6) == 6 && (Pico.video.reg[1] & 0x18)) {
-    if (Pico.video.reg[1] & 0x08) {
+  switch ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18)) {
+  // SMS2 only 224/240 line modes, e.g. Micro Machines
+  case 0x06|0x08:
       screen_offset = 0;
       lines = 240;
-    }
-    else {
+      break;
+  case 0x06|0x10:
       screen_offset = 8;
       lines = 224;
-    }
+      break;
   }
   if (PicoIn.opt & POPT_EN_SOFTSCALE) {
     line_offset = 0;
@@ -304,7 +547,7 @@ void PicoFrameStartMode4(void)
     line_offset = PicoIn.opt & POPT_DIS_32C_BORDER ? 0 : 32;
 
   coffs = line_offset;
-  if (FinalizeLineM4 == FinalizeLineRGB555M4)
+  if (FinalizeLineSMS == FinalizeLineRGB555SMS)
     line_offset = 0 /* done in FinalizeLine */;
 
   if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {
@@ -317,7 +560,7 @@ void PicoFrameStartMode4(void)
   Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
 }
 
-void PicoLineMode4(int line)
+void PicoLineSMS(int line)
 {
   if (skip_next_line > 0) {
     skip_next_line--;
@@ -329,11 +572,13 @@ void PicoLineMode4(int line)
 
   // Draw screen:
   BackFill(Pico.video.reg[7] & 0x0f, 0, &Pico.est);
-  if (Pico.video.reg[1] & 0x40)
-    DrawDisplayM4(line);
+  if (Pico.video.reg[1] & 0x40) {
+    if (Pico.video.reg[0] & 0x04) DrawDisplayM4(line);
+    else                          DrawDisplayM2(line);
+  }
 
-  if (FinalizeLineM4 != NULL)
-    FinalizeLineM4(line);
+  if (FinalizeLineSMS != NULL)
+    FinalizeLineSMS(line);
 
   if (PicoScanEnd != NULL)
     skip_next_line = PicoScanEnd(line + screen_offset);
@@ -342,7 +587,12 @@ void PicoLineMode4(int line)
   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
 }
 
-void PicoDoHighPal555M4(void)
+/* Fixed palette for TMS9918 modes */
+static u16 tmspal[32] = {
+ 0x00,0x00,0x08,0x0c,0x10,0x30,0x01,0x3c,0x02,0x03,0x05,0x0f,0x04,0x33,0x15,0x3f
+};
+
+void PicoDoHighPal555SMS(void)
 {
   unsigned int *spal=(void *)PicoMem.cram;
   unsigned int *dpal=(void *)Pico.est.HighPal;
@@ -350,9 +600,11 @@ void PicoDoHighPal555M4(void)
   int i;
 
   Pico.m.dirtyPal = 0;
+  if (!(Pico.video.reg[0] & 0x4))
+    spal = (u32 *)tmspal;
 
   /* cram is always stored as shorts, even though real hardware probably uses bytes */
-  for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
+  if (PicoIn.AHW & PAHW_SMS) for (i = 0x20/2; i > 0; i--, spal++, dpal++) { 
     t = *spal;
 #if defined(USE_BGR555)
     t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<6) | ((t & 0x00300030)<<9);
@@ -363,42 +615,55 @@ void PicoDoHighPal555M4(void)
 #else
     t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1);
     t |= (t >> 2) | ((t >> 4) & 0x08610861);
+#endif
+    *dpal = t;
+  } else for (i = 0x20/2; i > 0; i--, spal++, dpal++) { // GG palette 4 bit/col
+    t = *spal;
+#if defined(USE_BGR555)
+    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;
+#else
+    t = ((t & 0x000f000f)<<12) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7);
+    t |= (t >> 4) & 0x08610861;
 #endif
     *dpal = t;
   }
   Pico.est.HighPal[0xe0] = 0;
 }
 
-static void FinalizeLineRGB555M4(int line)
+static void FinalizeLineRGB555SMS(int line)
 {
   if (Pico.m.dirtyPal)
-    PicoDoHighPal555M4();
+    PicoDoHighPal555SMS();
 
   // standard FinalizeLine can finish it for us,
   // with features like scaling and such
   FinalizeLine555(0, line, &Pico.est);
 }
 
-static void FinalizeLine8bitM4(int line)
+static void FinalizeLine8bitSMS(int line)
 {
-  unsigned char *pd = Pico.est.DrawLineDest + line_offset;
-  unsigned char *ps = Pico.est.HighCol + line_offset + 8;
+  u8 *pd = Pico.est.DrawLineDest + line_offset;
+  u8 *ps = Pico.est.HighCol + line_offset + 8;
 
   if (DrawLineDestIncrement) {
     if (PicoIn.opt & POPT_EN_SOFTSCALE)
       rh_upscale_nn_4_5(pd, 320, ps, 256, 256, f_nop);
-    else
+    else if (pd != ps)
       memcpy(pd, ps, 256);
   }
 }
 
-void PicoDrawSetOutputMode4(pdso_t which)
+void PicoDrawSetOutputSMS(pdso_t which)
 {
   switch (which)
   {
-    case PDF_8BIT:   FinalizeLineM4 = FinalizeLine8bitM4; break;
-    case PDF_RGB555: FinalizeLineM4 = FinalizeLineRGB555M4; break;
-    default:         FinalizeLineM4 = NULL;
+    case PDF_8BIT:   FinalizeLineSMS = FinalizeLine8bitSMS; break;
+    case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break;
+    default:         FinalizeLineSMS = NULL;
                      PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break;
   }
   rendstatus_old = -1;

+ 6 - 5
pico/pico_int.h

@@ -347,7 +347,8 @@ struct PicoMS
   unsigned char carthw[0x10];
   unsigned char io_ctl;
   unsigned char nmi_state;
-  unsigned char pad[0x4e];
+  unsigned char mapper;
+  unsigned char pad[0x4d];
 };
 
 // emu state and data for the asm code
@@ -686,10 +687,10 @@ void PicoDraw2Init(void);
 PICO_INTERNAL void PicoFrameFull();
 
 // mode4.c
-void PicoFrameStartMode4(void);
-void PicoLineMode4(int line);
-void PicoDoHighPal555M4(void);
-void PicoDrawSetOutputMode4(pdso_t which);
+void PicoFrameStartSMS(void);
+void PicoLineSMS(int line);
+void PicoDoHighPal555SMS(void);
+void PicoDrawSetOutputSMS(pdso_t which);
 
 // memory.c
 PICO_INTERNAL void PicoMemSetup(void);

+ 59 - 39
pico/sms.c

@@ -8,7 +8,6 @@
 /*
  * TODO:
  * - start in a state as if BIOS ran
- * - RAM support in mapper
  * - region support
  * - H counter
  */
@@ -231,7 +230,8 @@ static void write_sram(unsigned short a, unsigned char d)
   Pico.sv.data[a] = d;
 }
 
-static void write_bank(unsigned short a, unsigned char d)
+// 16KB bank mapping for Sega mapper
+static void write_bank_sega(unsigned short a, unsigned char d)
 {
   elprintf(EL_Z80BNK, "bank %04x %02x @ %04x", a, d, z80_pc());
   Pico.ms.carthw[a & 0x0f] = d;
@@ -240,16 +240,10 @@ static void write_bank(unsigned short a, unsigned char d)
     case 0x0d:
       d &= bank_mask;
       z80_map_set(z80_read_map, 0x0400, 0x3fff, Pico.rom+0x400 + (d << 14), 0);
-#ifdef _USE_CZ80
-      Cz80_Set_Fetch(&CZ80, 0x0400, 0x3fff, (FPTR)Pico.rom+0x400 + (d << 14));
-#endif
       break;
     case 0x0e:
       d &= bank_mask;
       z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0);
-#ifdef _USE_CZ80
-      Cz80_Set_Fetch(&CZ80, 0x4000, 0x7fff, (FPTR)Pico.rom + (d << 14));
-#endif
       break;
 
     case 0x0c:
@@ -260,39 +254,50 @@ static void write_bank(unsigned short a, unsigned char d)
       if (Pico.ms.carthw[0xc] & 0x08) {
         d = (Pico.ms.carthw[0xc] & 0x04) >> 2;
         z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.sv.data + d*0x4000, 0);
-#ifdef _USE_CZ80
-        Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.sv.data + d*0x4000);
-#endif
         z80_map_set(z80_write_map, 0x8000, 0xbfff, write_sram, 1);
       } else {
         d = Pico.ms.carthw[0xf] & bank_mask;
         z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0);
-#ifdef _USE_CZ80
-        Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.rom + (d << 14));
-#endif
         z80_map_set(z80_write_map, 0x8000, 0xbfff, xwrite, 1);
       }
       break;
   }
 }
 
+// 8KB ROM mapping for MSX mapper
+static void write_bank_msx(unsigned short a, unsigned char d)
+{
+  Pico.ms.mapper = 1; // TODO define (more) mapper types
+  Pico.ms.carthw[a] = d;
+
+  a = (a^2)*0x2000 + 0x4000;
+  d &= 2*bank_mask + 1;
+  z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0);
+}
+
+// TODO mapping is currently auto-selecting, but that's not very reliable.
 static void xwrite(unsigned int a, unsigned char d)
 {
   elprintf(EL_IO, "z80 write [%04x] %02x", a, d);
   if (a >= 0xc000)
     PicoMem.zram[a & 0x1fff] = d;
-  if (a >= 0xfff8)
-    write_bank(a, d);
-  // codemasters
-  if (a == 0x0000)
-    write_bank(0xfffd, d);
+
+  // Sega. Maps 4 bank 16KB each
+  if (a >= 0xfff8 /*&& !Pico.ms.mapper*/)
+    write_bank_sega(a, d);
+  // Codemasters. Similar to Sega, but different addresses
+  if (a == 0x0000 && !Pico.ms.mapper)
+    write_bank_sega(0xfffd, d);
   if (a == 0x4000)
-    write_bank(0xfffe, d);
+    write_bank_sega(0xfffe, d);
   if (a == 0x8000)
-    write_bank(0xffff, d);
-  // korean
+    write_bank_sega(0xffff, d);
+  // Korean. 1 selectable 16KB bank at the top
   if (a == 0xa000)
-    write_bank(0xffff, d);
+    write_bank_sega(0xffff, d);
+  // MSX. 4 selectable 8KB banks at the top
+  if (a <= 0x0003 && (a || Pico.ms.mapper))
+    write_bank_msx(a, d);
 }
 
 void PicoResetMS(void)
@@ -300,6 +305,10 @@ void PicoResetMS(void)
   z80_reset();
   PsndReset(); // pal must be known here
   ymflag = 0xffff;
+  Pico.m.dirtyPal = 1;
+
+  // reset memory mapping
+  PicoMemSetupMS();
 }
 
 void PicoPowerMS(void)
@@ -321,11 +330,6 @@ void PicoPowerMS(void)
   tmp = 1 << s;
   bank_mask = (tmp - 1) >> 14;
 
-  Pico.ms.carthw[0x0c] = 0;
-  Pico.ms.carthw[0x0d] = 0;
-  Pico.ms.carthw[0x0e] = 1;
-  Pico.ms.carthw[0x0f] = 2;
-
   PicoReset();
 }
 
@@ -344,20 +348,36 @@ void PicoMemSetupMS(void)
   drZ80.z80_out = z80_sms_out;
 #endif
 #ifdef _USE_CZ80
-  Cz80_Set_Fetch(&CZ80, 0x0000, 0xbfff, (FPTR)Pico.rom);
-  Cz80_Set_Fetch(&CZ80, 0xc000, 0xdfff, (FPTR)PicoMem.zram);
-  Cz80_Set_Fetch(&CZ80, 0xe000, 0xffff, (FPTR)PicoMem.zram);
   Cz80_Set_INPort(&CZ80, z80_sms_in);
   Cz80_Set_OUTPort(&CZ80, z80_sms_out);
 #endif
+
+  // memory mapper setup, linearly mapped, default is Sega mapper
+  Pico.ms.carthw[0x00] = 4;
+  Pico.ms.carthw[0x01] = 5;
+  Pico.ms.carthw[0x02] = 2;
+  Pico.ms.carthw[0x03] = 3;
+
+  Pico.ms.carthw[0x0c] = 0;
+  Pico.ms.carthw[0x0d] = 0;
+  Pico.ms.carthw[0x0e] = 1;
+  Pico.ms.carthw[0x0f] = 2;
+  Pico.ms.mapper = 0;
 }
 
 void PicoStateLoadedMS(void)
 {
-  write_bank(0xfffc, Pico.ms.carthw[0x0c]);
-  write_bank(0xfffd, Pico.ms.carthw[0x0d]);
-  write_bank(0xfffe, Pico.ms.carthw[0x0e]);
-  write_bank(0xffff, Pico.ms.carthw[0x0f]);
+  if (Pico.ms.mapper) {
+    xwrite(0x0000, Pico.ms.carthw[0]);
+    xwrite(0x0001, Pico.ms.carthw[1]);
+    xwrite(0x0002, Pico.ms.carthw[2]);
+    xwrite(0x0003, Pico.ms.carthw[3]);
+  } else {
+    xwrite(0xfffc, Pico.ms.carthw[0x0c]);
+    xwrite(0xfffd, Pico.ms.carthw[0x0d]);
+    xwrite(0xfffe, Pico.ms.carthw[0x0e]);
+    xwrite(0xffff, Pico.ms.carthw[0x0f]);
+  }
 }
 
 void PicoFrameMS(void)
@@ -382,7 +402,7 @@ void PicoFrameMS(void)
 
   if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))
     lines_vis = (pv->reg[1] & 0x08) ? 240 : 224;
-  PicoFrameStartMode4();
+  PicoFrameStartSMS();
   hint = pv->reg[0x0a];
 
   for (y = 0; y < lines; y++)
@@ -392,7 +412,7 @@ void PicoFrameMS(void)
       pv->v_counter = y - 6;
 
     if (y < lines_vis && !skip)
-      PicoLineMode4(y);
+      PicoLineSMS(y);
 
     if (y <= lines_vis)
     {
@@ -428,10 +448,10 @@ void PicoFrameDrawOnlyMS(void)
   int lines_vis = 192;
   int y;
 
-  PicoFrameStartMode4();
+  PicoFrameStartSMS();
 
   for (y = 0; y < lines_vis; y++)
-    PicoLineMode4(y);
+    PicoLineSMS(y);
 }
 
 // vim:ts=2:sw=2:expandtab