\r
   // SAT scanning is one line ahead, but don't overshoot. Technically, SAT\r
   // parsing for line 0 is on the last line of the previous frame.\r
-  int first_line = Pico.est.DrawScanline + !!Pico.est.DrawScanline;\r
+  int first_line = est->DrawScanline + !!est->DrawScanline;\r
   if (max_lines > rendlines-1)\r
     max_lines = rendlines-1;\r
 \r
 \r
 // --------------------------------------------\r
 \r
-void BackFill(int reg7, int sh, struct PicoEState *est)\r
+void BackFill(int bgc, int sh, struct PicoEState *est)\r
 {\r
-  unsigned int back;\r
+  u32 back = bgc;\r
 \r
   // Start with a blank scanline (background colour):\r
-  back=reg7&0x3f;\r
   back|=sh<<7; // shadow\r
   back|=back<<8;\r
   back|=back<<16;\r
   PicoDrawUpdateHighPal();\r
 \r
   len = 256;\r
-  if ((PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_LCD))\r
-    len = 160;\r
-  else if (!(PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[12]&1))\r
+  if (!(PicoIn.AHW & PAHW_8BIT) && (Pico.video.reg[12]&1))\r
     len = 320;\r
-  if ((PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[0] & 0x20) && len == 256)\r
+  else if ((PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_LCD))\r
+    len = 160;\r
+  else if ((PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[0] & 0x20))\r
     len -= 8, ps += 8;\r
 \r
   if ((*est->PicoOpt & POPT_EN_SOFTSCALE) && len < 320) {\r
   // a hack for mid-frame palette changes\r
   if (Pico.m.dirtyPal == 1)\r
   {\r
-    // store a maximum of 2 additional palettes in SonicPal\r
-    if (est->SonicPalCount < 2 &&\r
-        (!(est->rendstatus & PDRAW_SONIC_MODE) || (line - dirty_line > 4))) {\r
+    // store a maximum of 3 additional palettes in SonicPal\r
+    if (est->SonicPalCount < 3 &&\r
+        (!(est->rendstatus & PDRAW_SONIC_MODE) || (line - dirty_line >= 4))) {\r
       est->SonicPalCount ++;\r
       dirty_line = line;\r
       est->rendstatus |= PDRAW_SONIC_MODE;\r
   }\r
 \r
   len = 256;\r
-  if ((PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_LCD))\r
-    len = 160;\r
-  else if (!(PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[12]&1))\r
+  if (!(PicoIn.AHW & PAHW_8BIT) && (Pico.video.reg[12]&1))\r
     len = 320;\r
-  if ((PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[0] & 0x20) && len == 256)\r
+  else if ((PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_LCD))\r
+    len = 160;\r
+  else if ((PicoIn.AHW & PAHW_SMS) && (Pico.video.reg[0] & 0x20))\r
     len -= 8, ps += 8;\r
 \r
   if (DrawLineDestIncrement == 0)\r
     for (a = 0, c = HighCacheA; *c; c+=2, a++);\r
     for (b = 0, c = HighCacheB; *c; c+=2, b++);\r
     printf("%i:%03i: a=%i, b=%i\n", Pico.m.frame_count,\r
-           Pico.est.DrawScanline, a, b);\r
+           est->DrawScanline, a, b);\r
   }\r
 #endif\r
 \r
 // MUST be called every frame\r
 PICO_INTERNAL void PicoFrameStart(void)\r
 {\r
+  struct PicoEState *est = &Pico.est;\r
   int loffs = 8, lines = 224, coffs = 0, columns = 320;\r
-  int sprep = Pico.est.rendstatus & (PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES);\r
-  int skipped = Pico.est.rendstatus & PDRAW_SKIP_FRAME;\r
+  int sprep = est->rendstatus & PDRAW_DIRTY_SPRITES;\r
+  int skipped = est->rendstatus & PDRAW_SKIP_FRAME;\r
 \r
   // prepare to do this frame\r
-  Pico.est.rendstatus = 0;\r
+  est->rendstatus = 0;\r
   if ((Pico.video.reg[12] & 6) == 6)\r
-    Pico.est.rendstatus |= PDRAW_INTERLACE; // interlace mode\r
+    est->rendstatus |= PDRAW_INTERLACE; // interlace mode\r
   if (!(Pico.video.reg[12] & 1)) {\r
-    Pico.est.rendstatus |= PDRAW_32_COLS;\r
+    est->rendstatus |= PDRAW_32_COLS;\r
     if (!(PicoIn.opt & POPT_EN_SOFTSCALE)) {\r
       columns = 256;\r
       coffs = 32;\r
     }\r
   }\r
   if (Pico.video.reg[1] & 8) {\r
-    Pico.est.rendstatus |= PDRAW_30_ROWS;\r
+    est->rendstatus |= PDRAW_30_ROWS;\r
     lines = 240;\r
     loffs = 0;\r
   }\r
   if (PicoIn.opt & POPT_DIS_32C_BORDER)\r
     coffs = 0;\r
 \r
-  if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {\r
+  if (est->rendstatus != rendstatus_old || lines != rendlines) {\r
     rendlines = lines;\r
     // mode_change() might reset rendstatus_old by calling SetColorFormat\r
     emu_video_mode_change(loffs, lines, coffs, columns);\r
-    rendstatus_old = Pico.est.rendstatus;\r
+    rendstatus_old = est->rendstatus & (PDRAW_INTERLACE|PDRAW_32_COLS|PDRAW_30_ROWS);\r
   }\r
   if (PicoIn.skipFrame) // preserve this until something is rendered at last\r
-    Pico.est.rendstatus |= PDRAW_SKIP_FRAME;\r
+    est->rendstatus |= PDRAW_SKIP_FRAME;\r
   if (sprep | skipped)\r
-    Pico.est.rendstatus |= PDRAW_PARSE_SPRITES;\r
+    est->rendstatus |= PDRAW_PARSE_SPRITES;\r
   if (PicoIn.AHW & PAHW_32X)\r
-    Pico.est.rendstatus |= PDRAW_32X_SCALE;\r
+    est->rendstatus |= PDRAW_32X_SCALE;\r
 \r
-  Pico.est.HighCol = HighColBase + loffs * HighColIncrement;\r
-  Pico.est.DrawLineDest = (char *)DrawLineDestBase + loffs * DrawLineDestIncrement;\r
-  Pico.est.DrawScanline = 0;\r
+  est->HighCol = HighColBase + loffs * HighColIncrement;\r
+  est->DrawLineDest = (char *)DrawLineDestBase + loffs * DrawLineDestIncrement;\r
+  est->DrawScanline = 0;\r
   skip_next_line = 0;\r
 \r
   if (FinalizeLine == FinalizeLine8bit) {\r
     // make a backup of the current palette in case Sonic mode is detected later\r
-    Pico.m.dirtyPal = (Pico.m.dirtyPal || Pico.est.SonicPalCount ? 2 : 0);\r
-    blockcpy(Pico.est.SonicPal, PicoMem.cram, 0x40*2);\r
+    Pico.m.dirtyPal = (Pico.m.dirtyPal || est->SonicPalCount ? 2 : 0);\r
+    blockcpy(est->SonicPal, PicoMem.cram, 0x40*2);\r
   }\r
-  Pico.est.SonicPalCount = 0;\r
+  est->SonicPalCount = 0;\r
 }\r
 \r
 static void DrawBlankedLine(int line, int offs, int sh, int bgc)\r
 {\r
+  struct PicoEState *est = &Pico.est;\r
   int skip = skip_next_line;\r
 \r
   if (PicoScanBegin != NULL && skip == 0)\r
     return;\r
   }\r
 \r
-  BackFill(bgc, sh, &Pico.est);\r
+  BackFill(bgc, sh, est);\r
 \r
   if (FinalizeLine != NULL)\r
-    FinalizeLine(sh, line, &Pico.est);\r
+    FinalizeLine(sh, line, est);\r
 \r
   if (PicoScanEnd != NULL)\r
     skip_next_line = PicoScanEnd(line + offs);\r
 \r
-  Pico.est.HighCol += HighColIncrement;\r
-  Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;\r
+  est->HighCol += HighColIncrement;\r
+  est->DrawLineDest = (char *)est->DrawLineDest + DrawLineDestIncrement;\r
 }\r
 \r
 static void PicoLine(int line, int offs, int sh, int bgc)\r
 {\r
+  struct PicoEState *est = &Pico.est;\r
   int skip = skip_next_line;\r
 \r
-  Pico.est.DrawScanline = line;\r
+  est->DrawScanline = line;\r
   if (PicoScanBegin != NULL && skip == 0)\r
     skip = PicoScanBegin(line + offs);\r
 \r
     bgc = 0x3f;\r
 \r
   // Draw screen:\r
-  BackFill(bgc, sh, &Pico.est);\r
+  BackFill(bgc, sh, est);\r
   if (Pico.video.reg[1]&0x40)\r
     DrawDisplay(sh);\r
 \r
   if (FinalizeLine != NULL)\r
-    FinalizeLine(sh, line, &Pico.est);\r
+    FinalizeLine(sh, line, est);\r
 \r
   if (PicoScanEnd != NULL)\r
     skip_next_line = PicoScanEnd(line + offs);\r
 \r
-  Pico.est.HighCol += HighColIncrement;\r
-  Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;\r
+  est->HighCol += HighColIncrement;\r
+  est->DrawLineDest = (char *)est->DrawLineDest + DrawLineDestIncrement;\r
 }\r
 \r
 void PicoDrawSync(int to, int blank_last_line, int limit_sprites)\r
   struct PicoEState *est = &Pico.est;\r
   int line, offs = 0;\r
   int sh = (Pico.video.reg[0xC] & 8) >> 3; // shadow/hilight?\r
-  int bgc = Pico.video.reg[7];\r
+  int bgc = Pico.video.reg[7] & 0x3f;\r
 \r
   pprof_start(draw);\r
 \r
     if (to > 223)\r
       to = 223;\r
   }\r
-  if (est->DrawScanline <= to && (est->rendstatus &\r
-                (PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))\r
+  if (est->DrawScanline <= to &&\r
+                (est->rendstatus & (PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))\r
     ParseSprites(to + 1, limit_sprites);\r
 \r
   for (line = est->DrawScanline; line < to; line++)\r
   pprof_end(draw);\r
 }\r
 \r
-void PicoDrawRefreshSprites()\r
+void PicoDrawRefreshSprites(void)\r
 {\r
-  unsigned char *sprited = &HighLnSpr[Pico.est.DrawScanline][0];\r
+  struct PicoEState *est = &Pico.est;\r
+  unsigned char *sprited = &HighLnSpr[est->DrawScanline][0];\r
   int i;\r
 \r
-  if (Pico.est.DrawScanline == 0 || Pico.est.DrawScanline >= rendlines) return;\r
+  if (est->DrawScanline == 0 || est->DrawScanline >= rendlines) return;\r
 \r
   // compute sprite row. The VDP does this by subtracting the sprite y pos from\r
   // the current line and treating the lower 5 bits as the row number. Y pos\r
     int sy = (CPU_LE2(VdpSATCache[2*link]) & 0x1ff) - 0x80;\r
     if (sy != (s16)sp[0]) {\r
       // Y info in SAT cache has really changed\r
-      sy = Pico.est.DrawScanline - ((Pico.est.DrawScanline - sy) & 0x1f);\r
+      sy = est->DrawScanline - ((est->DrawScanline - sy) & 0x1f);\r
       sp[0] = (sp[0] & 0xffff0000) | (u16)sy;\r
     }\r
   }\r
       blockcpy(est->HighPal+0x40, est->HighPal, 0x40*2);\r
       blockcpy(est->HighPal+0x80, est->HighPal, 0x80*2);\r
     }\r
-    Pico.est.HighPal[0xe0] = 0x0000; // black and white, reserved for OSD\r
-    Pico.est.HighPal[0xf0] = 0xffff;\r
+    est->HighPal[0xe0] = 0x0000; // black and white, reserved for OSD\r
+    est->HighPal[0xf0] = 0xffff;\r
   }\r
 }\r
 \r
 
 \r
   const unsigned short *fifo_cyc2sl;\r
   const unsigned short *fifo_sl2cyc;\r
+  const unsigned char  *fifo_hcounts;\r
 } VdpFIFO;\r
 \r
 enum { FQ_BYTE = 1, FQ_BGDMA = 2, FQ_FGDMA = 4 }; // queue flags, NB: BYTE = 1!\r
 \r
 // NB should limit cyc2sl to table size in case 68k overdraws its aim. That can\r
 // happen if the last op is a blocking acess to VDP, or for exceptions (e.g.irq)\r
-#define        Cyc2Sl(vf,lc)   (vf->fifo_cyc2sl[(lc)/clkdiv])\r
-#define Sl2Cyc(vf,sl)   (vf->fifo_sl2cyc[sl]*clkdiv)\r
+#define Cyc2Sl(vf,lc)   ((vf)->fifo_cyc2sl[(lc)/clkdiv])\r
+#define Sl2Cyc(vf,sl)   ((vf)->fifo_sl2cyc[sl]*clkdiv)\r
 \r
 // do the FIFO math\r
 static int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, int slots)\r
 \r
   // reset slot to start of scanline\r
   vf->fifo_slot = 0;\r
+  // only need to refresh sprite position if we are synced\r
   if (Pico.est.DrawScanline == Pico.m.scanline)\r
     PicoDrawRefreshSprites();\r
  \r
       { {vdpcyc2sl_32_bl, vdpcyc2sl_40_bl},{vdpcyc2sl_32_ac, vdpcyc2sl_40_ac} };\r
   static const unsigned short *vdpsl2cyc[2][2] =\r
       { {vdpsl2cyc_32_bl, vdpsl2cyc_40_bl},{vdpsl2cyc_32_ac, vdpsl2cyc_40_ac} };\r
+  static const unsigned char *vdphcounts[2] =\r
+      { hcounts_32, hcounts_40 };\r
 \r
   struct VdpFIFO *vf = &VdpFIFO;\r
   struct PicoVideo *pv = &Pico.video;\r
 \r
   vf->fifo_cyc2sl = vdpcyc2sl[active][h40];\r
   vf->fifo_sl2cyc = vdpsl2cyc[active][h40];\r
+  vf->fifo_hcounts = vdphcounts[h40];\r
   // recalculate FIFO slot for new mode\r
   vf->fifo_slot = Cyc2Sl(vf, lc);\r
   vf->fifo_maxslot = Cyc2Sl(vf, 488);\r
 static void DmaSlow(int len, u32 source)\r
 {\r
   u32 inc = Pico.video.reg[0xf];\r
-  u32 a = Pico.video.addr | (Pico.video.addr_u << 16);\r
+  u32 a = Pico.video.addr | (Pico.video.addr_u << 16), e;\r
   u16 *r, *base = NULL;\r
   u32 mask = 0x1ffff;\r
 \r
   switch (Pico.video.type)\r
   {\r
     case 1: // vram\r
+      e = a + len*2-1;\r
       r = PicoMem.vram;\r
-      if (inc == 2 && !(a & 1) && (a & ~0xffff) == ((a + len*2-1) & ~0xffff) &&\r
-          ((a >= SATaddr+0x280) | ((a + len*2-1) < SATaddr)) &&\r
-          (source & ~mask) == ((source + len-1) & ~mask))\r
+      if (inc == 2 && !(a & 1) && !((a ^ e) >> 16) &&\r
+          ((a >= SATaddr + 0x280) | (e < SATaddr)) &&\r
+          !((source ^ (source + len-1)) & ~mask))\r
       {\r
         // most used DMA mode\r
         memcpy((char *)r + a, base + (source & mask), len * 2);\r
 \r
 static NOINLINE void DmaFill(int data)\r
 {\r
-  u32 a = Pico.video.addr | (Pico.video.addr_u << 16);\r
+  u32 a = Pico.video.addr | (Pico.video.addr_u << 16), e;\r
   u8 *vr = (u8 *)PicoMem.vram;\r
   u8 high = (u8)(data >> 8);\r
   u8 inc = Pico.video.reg[0xf];\r
   switch (Pico.video.type)\r
   {\r
     case 1: // vram\r
-      if (inc == 1 && (a & ~0xffff) == ((a + len-1) & ~0xffff) &&\r
-          ((a >= SATaddr+0x280) | ((a + len-1) < SATaddr)))\r
+      e = a + len-1;\r
+      if (inc == 1 && !((a ^ e) >> 16) &&\r
+          ((a >= SATaddr + 0x280) | (e < SATaddr)))\r
       {\r
         // most used DMA mode\r
         memset(vr + (u16)a, high, len);\r
  \r
 static inline int InHblank(int offs)\r
 {\r
-  return SekCyclesDone() - Pico.t.m68c_line_start <= 488-offs;\r
+  // check if in left border (14 pixels) or HBLANK (86 pixels), 116 68k cycles\r
+  return SekCyclesDone() - Pico.t.m68c_line_start <= offs;\r
 }\r
 \r
 static void DrawSync(int skip)\r
       pvid->pending=0;\r
     }\r
 \r
-    // try avoiding the sync. can't easily do this for VRAM writes since they\r
-    // might update the SAT cache\r
-    if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&\r
+    // try avoiding the sync if the data doesn't change.\r
+    // Writes to the SAT in VRAM are special since they update the SAT cache.\r
+    if ((pvid->reg[1]&0x40) &&\r
+        !(pvid->type == 1 && !(pvid->addr&1) && ((pvid->addr^SATaddr)&SATmask) && PicoMem.vram[pvid->addr>>1] == d) &&\r
         !(pvid->type == 3 && PicoMem.cram[(pvid->addr>>1) & 0x3f] == (d & 0xeee)) &&\r
         !(pvid->type == 5 && PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))\r
-      DrawSync(InHblank(440)); // experimentally, Overdrive 2\r
+      DrawSync(InHblank(48)); // experimentally, Overdrive 2\r
 \r
     if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))\r
     {\r
       CommandChange(pvid);\r
       // Check for dma:\r
       if (d & 0x80) {\r
-        DrawSync(InHblank(390));\r
+        DrawSync(InHblank(93));\r
         CommandDma();\r
       }\r
     }\r
           return;\r
         }\r
 \r
+        d &= 0xff;\r
         if (num == 0 && !(pvid->reg[0]&2) && (d&2))\r
           pvid->hv_latch = PicoVideoRead(0x08);\r
         if (num == 1 && ((pvid->reg[1]^d)&0x40)) {\r
           PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);\r
           // handle line blanking before line rendering\r
-          if (InHblank(390)) {\r
+          if (InHblank(93)) {\r
             // sprite rendering is limited if display is disabled and reenabled\r
             // in HBLANK of the same scanline (Overdrive)\r
             limitsprites = (d&0x40) && blankline == Pico.m.scanline ? Pico.m.scanline : -1;\r
         }\r
         if (num == 12 && ((pvid->reg[12]^d)&0x01))\r
           PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);\r
-        if (num <= 18) // no sync needed for DMA setup registers\r
-          DrawSync(InHblank(390));\r
-        d &= 0xff;\r
-        pvid->reg[num]=(unsigned char)d;\r
+        if (num <= 18 && pvid->reg[num] != d) // no sync for DMA setup\r
+          DrawSync(InHblank(93)); // Toy Story\r
+        pvid->reg[num]=d;\r
+\r
         switch (num)\r
         {\r
           case 0x00:\r
             goto update_irq;\r
           case 0x05:\r
           case 0x06:\r
-            if (d^dold) Pico.est.rendstatus |= PDRAW_SPRITES_MOVED;\r
+            if (d^dold) Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
             break;\r
           case 0x0c:\r
             // renderers should update their palettes if sh/hi mode is changed\r
     c = SekCyclesDone() - Pico.t.m68c_line_start;\r
     if (Pico.video.reg[0]&2)\r
          d = Pico.video.hv_latch;\r
-    else if (Pico.video.reg[12]&1)\r
-         d = hcounts_40[c/clkdiv] | (Pico.video.v_counter << 8);\r
-    else d = hcounts_32[c/clkdiv] | (Pico.video.v_counter << 8);\r
+    else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (Pico.video.v_counter << 8);\r
 \r
     elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, Pico.video.v_counter, SekCyclesDone(), SekPc);\r
     return d;\r
   u32 d = SekCyclesDone() - Pico.t.m68c_line_start;\r
   if (Pico.video.reg[0]&2)\r
        d = Pico.video.hv_latch;\r
-  else if (Pico.video.reg[12]&1)\r
-       d = hcounts_40[d/clkdiv];\r
-  else d = hcounts_32[d/clkdiv];\r
+  else d = VdpFIFO.fifo_hcounts[d/clkdiv];\r
   elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);\r
   return d;\r
 }\r
     ((u16 *)VdpSATCache)[l*2 + 1] = PicoMem.vram[(addr>>1) + 1];\r
   }\r
 \r
-  Pico.est.rendstatus |= PDRAW_SPRITES_MOVED;\r
+  Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;\r
 }\r
 \r
 void PicoVideoSave(void)\r