\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