sms wip: initial graphics support
[picodrive.git] / pico / mode4.c
diff --git a/pico/mode4.c b/pico/mode4.c
new file mode 100644 (file)
index 0000000..7062a17
--- /dev/null
@@ -0,0 +1,218 @@
+#include "pico_int.h"
+
+static void (*FinalizeLineM4)(void);
+static int skip_next_line;
+
+#define PLANAR_PIXEL(x,p) \
+  t = pack & (0x80808080 >> p); \
+  if (t) { \
+    t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \
+    pd[x] = pal|t; \
+  }
+
+static int TileNormM4(int sx, int addr, int pal)
+{
+  unsigned char *pd = HighCol + sx;
+  unsigned int pack, t;
+
+  pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
+  if (pack)
+  {
+    PLANAR_PIXEL(0, 0)
+    PLANAR_PIXEL(1, 1)
+    PLANAR_PIXEL(2, 2)
+    PLANAR_PIXEL(3, 3)
+    PLANAR_PIXEL(4, 4)
+    PLANAR_PIXEL(5, 5)
+    PLANAR_PIXEL(6, 6)
+    PLANAR_PIXEL(7, 7)
+    return 0;
+  }
+
+  return 1; /* Tile blank */
+}
+
+static int TileFlipM4(int sx,int addr,int pal)
+{
+  unsigned char *pd = HighCol + sx;
+  unsigned int pack, t;
+
+  pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
+  if (pack)
+  {
+    PLANAR_PIXEL(0, 7)
+    PLANAR_PIXEL(1, 6)
+    PLANAR_PIXEL(2, 5)
+    PLANAR_PIXEL(3, 4)
+    PLANAR_PIXEL(4, 3)
+    PLANAR_PIXEL(5, 2)
+    PLANAR_PIXEL(6, 1)
+    PLANAR_PIXEL(7, 0)
+    return 0;
+  }
+
+  return 1; /* Tile blank */
+}
+
+struct TileStrip
+{
+  int nametab; // Position in VRAM of name table (for this tile line)
+  int line;    // Line number in pixels 0x000-0x3ff within the virtual tilemap
+  int hscroll; // Horizontal scroll value in pixels for the line
+  int xmask;   // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap
+  int *hc;     // cache for high tile codes and their positions
+  int cells;   // cells (tiles) to draw (32 col mode doesn't need to update whole 320)
+};
+
+static void DrawStrip(struct TileStrip *ts, int cellskip)
+{
+  int tilex,dx,ty,code=0,addr=0,cells;
+  int oldcode=-1,blank=-1; // The tile we know is blank
+  int pal=0;
+
+  // Draw tiles across screen:
+  tilex=((-ts->hscroll)>>3)+cellskip;
+  ty=(ts->line&7)<<1; // Y-Offset into tile
+  dx=((ts->hscroll-1)&7)+1;
+  cells = ts->cells - cellskip;
+  if (dx != 8) cells++; // have hscroll, need to draw 1 cell more
+  dx+=cellskip<<3;
+
+  for (; cells > 0; dx+=8,tilex++,cells--)
+  {
+    int zero;
+
+    code=Pico.vram[ts->nametab + (tilex & 0x1f)];
+    if (code==blank) continue;
+
+    if (code!=oldcode) {
+      oldcode = code;
+      // Get tile address/2:
+      addr=(code&0x1ff)<<4;
+      addr+=ty;
+      if (code&0x0400) addr^=0xe; // Y-flip
+
+      pal=((code>>7)&0x10);
+    }
+
+    if (code&0x0200) zero=TileFlipM4(dx,addr,pal);
+    else             zero=TileNormM4(dx,addr,pal);
+
+    if (zero) blank=code; // We know this tile is blank now
+  }
+}
+
+static void DrawLayer(int cellskip, int maxcells)
+{
+  struct PicoVideo *pvid=&Pico.video;
+  struct TileStrip ts;
+  int vscroll;
+
+  ts.cells=maxcells;
+
+  // Find name table:
+  ts.nametab=(pvid->reg[2]&0x0e) << (10-1);
+
+  // Get horizontal scroll value, will be masked later
+  ts.hscroll=0;//pvid->reg[8];
+  vscroll=0;//pvid->reg[9]; // Get vertical scroll value
+
+  // Find the line in the name table
+  ts.line=(vscroll+DrawScanline)&0xff;
+  ts.nametab+=(ts.line>>3) << (6-1);
+
+  DrawStrip(&ts, cellskip);
+}
+
+static void DrawDisplayM4(void)
+{
+  DrawLayer(0, 32);
+}
+
+void PicoFrameStartMode4(void)
+{
+  DrawScanline = 0;
+  skip_next_line = 0;
+}
+
+void PicoLineMode4(int line)
+{
+  if (skip_next_line > 0) {
+    skip_next_line--;
+    return;
+  }
+
+  DrawScanline = line;
+
+  if (PicoScanBegin != NULL)
+    skip_next_line = PicoScanBegin(DrawScanline);
+
+  // Draw screen:
+  BackFill((Pico.video.reg[7] & 0x0f) | 0x10, 0);
+  if (Pico.video.reg[1] & 0x40)
+    DrawDisplayM4();
+
+  if (FinalizeLineM4 != NULL)
+    FinalizeLineM4();
+
+  if (PicoScanEnd != NULL)
+    skip_next_line = PicoScanEnd(DrawScanline);
+}
+
+void PicoDoHighPal555M4(void)
+{
+  unsigned int *spal=(void *)Pico.cram;
+  unsigned int *dpal=(void *)HighPal;
+  unsigned int t;
+  int i;
+
+  Pico.m.dirtyPal = 0;
+
+  /* cram is always stored as shorts, even though real hardware probably uses bytes */
+  for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
+    t = *spal;
+#ifdef USE_BGR555
+    t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)<<10);
+#else
+    t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1);
+#endif
+    t |= t >> 2;
+    t |= (t >> 4) & 0x08610861;
+    *dpal = t;
+  }
+}
+
+static void FinalizeLineRGB555M4(void)
+{
+  unsigned short *pd=DrawLineDest;
+  unsigned char  *ps=HighCol+8;
+  unsigned short *pal=HighPal;
+  int i;
+
+  if (Pico.m.dirtyPal)
+    PicoDoHighPal555M4();
+
+  if (!(PicoOpt & POPT_DIS_32C_BORDER))
+    pd += 32;
+
+  for (i = 256/4; i > 0; i--) {
+    *pd++ = pal[*ps++];
+    *pd++ = pal[*ps++];
+    *pd++ = pal[*ps++];
+    *pd++ = pal[*ps++];
+  }
+}
+
+void PicoDrawSetColorFormatMode4(int which)
+{
+  switch (which)
+  {
+    case 1: FinalizeLineM4 = FinalizeLineRGB555M4; break;
+    default:FinalizeLineM4 = NULL; break;
+  }
+#if OVERRIDE_HIGHCOL
+  if (which)
+    HighCol = DefHighCol;
+#endif
+}
+