#define SPRL_HAVE_MASK0 0x02 // have sprite with x == 0 in 1st slot\r
#define SPRL_MASKED 0x01 // lo prio masking by sprite with x == 0 active\r
\r
-unsigned char HighLnSpr[240][4+MAX_LINE_SPRITES+1]; // sprite_count, ^flags, tile_count, sprites_total, [spritep]..., last_width\r
+// sprite cache. stores results of sprite parsing for each display line:\r
+// [visible_sprites_count, sprl_flags, tile_count, sprites_processed, sprite_idx[sprite_count], last_width]\r
+unsigned char HighLnSpr[240][4+MAX_LINE_SPRITES+1];\r
\r
int rendstatus_old;\r
int rendlines;\r
pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \\r
if (likely(m & (1<<(x+8)))) { \\r
m &= ~(1<<(x+8)); \\r
- if (t<0xe) pd[x] &= pal|t; \\r
+ /* if (!t) pd[x] |= 0x40; as per titan hw notes? */ \\r
+ pd[x] &= pal|t; \\r
}\r
\r
TileNormMakerAS(TileNormSH_AS_and, pix_sh_as_and)\r
unsigned char *p;\r
int cnt, w;\r
\r
- cnt = sprited[0] & 0x7f;\r
+ cnt = sprited[0];\r
if (cnt == 0) return;\r
\r
p = &sprited[4];\r
unsigned m;\r
int entry, cnt;\r
\r
- cnt = sprited[0] & 0x7f;\r
+ cnt = sprited[0];\r
if (cnt == 0) return;\r
\r
memset(mb, 0xff, sizeof(mb));\r
unsigned m;\r
int entry, cnt;\r
\r
- cnt = sprited[0] & 0x7f;\r
+ cnt = sprited[0];\r
if (cnt == 0) { memset(pd, 0, sizeof(DefHighCol)); return; }\r
\r
memset(mb, 0xff, sizeof(mb));\r
*mp = m; // write last mask byte\r
}\r
\r
- // anything not covered by a sprite is off (XXX or bg?)\r
+ // anything not covered by a sprite is off \r
+ // XXX Titan hw notes say that transparent pixels remove shadow. Is this also\r
+ // the case in areas where no sprites are displayed?\r
for (cnt = 1; cnt < sizeof(mb)-1; cnt++)\r
if (mb[cnt] == 0xff) {\r
*(u32 *)(pd+8*cnt+0) = 0;\r
// Index + 0 : hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size\r
// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
\r
-static NOINLINE void ParseSprites(int max_lines)\r
+static NOINLINE void ParseSprites(int max_lines, int limit)\r
{\r
const struct PicoVideo *pvid=&Pico.video;\r
const struct PicoEState *est=&Pico.est;\r
if (max_lines > rendlines-1)\r
max_lines = rendlines-1;\r
\r
+ // look-ahead SAT parsing for next line and sprite pixel fetching for current\r
+ // line are limited if display was disabled during HBLANK before current line\r
+ if (limit) limit = 16; // max sprites/pixels processed\r
+\r
if (!(Pico.video.reg[12]&1))\r
max_sprites = 64, max_line_sprites = 16, max_width = 264;\r
if (*est->PicoOpt & POPT_DIS_SPRITE_LIM)\r
if (sy <= max_lines && sy + (height<<3) >= first_line) // sprite onscreen (y)?\r
{\r
int entry, y, w, sx_min, onscr_x, maybe_op = 0;\r
+ // omit look-ahead line if sprite parsing limit reached\r
+ int last_line = (limit && u >= 2*limit ? max_lines-1 : max_lines);\r
\r
sx_min = 8-(width<<3);\r
onscr_x = sx_min < sx && sx < max_width;\r
\r
entry = ((pd - HighPreSpr) / 2) | ((code2>>8)&0x80);\r
y = (sy >= first_line) ? sy : first_line;\r
- for (; y < sy + (height<<3) && y <= max_lines; y++)\r
+ for (; y < sy + (height<<3) && y <= last_line; y++)\r
{\r
unsigned char *p = &HighLnSpr[y][0];\r
int cnt = p[0];\r
- if (p[3] >= max_line_sprites) continue; // sprite limit?\r
if (p[1] & SPRL_MASKED) continue; // masked?\r
\r
+ if (p[3] >= max_line_sprites) continue; // sprite limit?\r
+ p[3] ++;\r
+\r
w = width;\r
if (p[2] + width > max_line_sprites*2) { // tile limit?\r
if (y+1 < 240) HighLnSpr[y+1][1] |= SPRL_TILE_OVFL;\r
w = max_line_sprites*2 - p[2];\r
}\r
p[2] += w;\r
- p[3] ++;\r
\r
if (sx == -0x78) {\r
if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL))\r
\r
if (!onscr_x) continue; // offscreen x\r
\r
- p[4+cnt] = entry;\r
- p[5+cnt] = w; // width clipped by tile limit for sprite renderer\r
- p[0] = cnt + 1;\r
+ // sprite is (partly) visible, store info for renderer\r
p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO;\r
p[1] |= maybe_op; // there might be op sprites on this line\r
if (cnt > 0 && (code2 & 0x8000) && !(p[4+cnt-1]&0x80))\r
p[1] |= SPRL_LO_ABOVE_HI;\r
+\r
+ p[4+cnt] = entry;\r
+ p[5+cnt] = w; // width clipped by tile limit for sprite renderer\r
+ p[0] = cnt + 1;\r
}\r
}\r
\r
}\r
*pd = 0;\r
\r
+ // fetching sprite pixels isn't done while display is disabled during HBLANK\r
+ if (limit) {\r
+ int w = 0;\r
+ unsigned char *sprited = &HighLnSpr[max_lines-1][0]; // current render line\r
+\r
+ for (u = 0; u < sprited[0]; u++) {\r
+ s32 *sp = HighPreSpr + (sprited[4+u] & 0x7f) * 2;\r
+ int sw = sp[0] >> 28;\r
+ if (w + sw > limit) {\r
+ sprited[0] = u;\r
+ sprited[4+u] = limit-w;\r
+ break;\r
+ }\r
+ w += sw;\r
+ }\r
+ }\r
+\r
#if 0\r
for (u = first_line; u <= max_lines; u++)\r
{\r
unsigned char *p;\r
int cnt, w;\r
\r
- cnt = sprited[0] & 0x7f;\r
+ cnt = sprited[0];\r
if (cnt == 0) return;\r
\r
p = &sprited[4];\r
Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;\r
}\r
\r
-void PicoDrawSync(int to, int blank_last_line)\r
+void PicoDrawSync(int to, int blank_last_line, int limit_sprites)\r
{\r
struct PicoEState *est = &Pico.est;\r
int line, offs = 0;\r
}\r
if (est->DrawScanline <= to && (est->rendstatus &\r
(PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))\r
- ParseSprites(to + 1);\r
+ ParseSprites(to + 1, limit_sprites);\r
\r
for (line = est->DrawScanline; line < to; line++)\r
PicoLine(line, offs, sh, bgc);\r
\r
\r
static int blankline; // display disabled for this line\r
+static int limitsprites;\r
\r
u32 SATaddr, SATmask; // VRAM addr of sprite attribute table\r
\r
\r
// VDP interface\r
\r
+static inline int InHblank(int offs)\r
+{\r
+ return SekCyclesDone() - Pico.t.m68c_line_start <= 488-offs;\r
+}\r
+\r
static void DrawSync(int skip)\r
{\r
int lines = Pico.video.reg[1]&0x08 ? 240 : 224;\r
!PicoIn.skipFrame && Pico.est.DrawScanline <= last) {\r
//elprintf(EL_ANOMALY, "sync");\r
if (blankline >= 0 && blankline < last) {\r
- PicoDrawSync(blankline, 1);\r
+ PicoDrawSync(blankline, 1, 0);\r
blankline = -1;\r
}\r
- PicoDrawSync(last, 0);\r
+ PicoDrawSync(last, 0, last == limitsprites);\r
+ if (last >= limitsprites)\r
+ limitsprites = -1;\r
}\r
}\r
\r
switch (a)\r
{\r
case 0x00: // Data port 0 or 2\r
- // try avoiding the sync..\r
- if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&\r
- !(!pvid->pending && ((pvid->command & 0xc00000f0) == 0x40000010 &&\r
- PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))\r
- )\r
- DrawSync(0); // XXX it's unclear when vscroll data is fetched from vsram?\r
-\r
if (pvid->pending) {\r
CommandChange(pvid);\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
+ !(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
+\r
if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))\r
{\r
VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;\r
CommandChange(pvid);\r
// Check for dma:\r
if (d & 0x80) {\r
- DrawSync(SekCyclesDone() - Pico.t.m68c_line_start <= 488-390);\r
+ DrawSync(InHblank(390));\r
CommandDma();\r
}\r
}\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 (SekCyclesDone() - Pico.t.m68c_line_start <= 488-390)\r
- blankline = d&0x40 ? -1 : Pico.m.scanline;\r
+ if (InHblank(390)) {\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
+ blankline = (d&0x40) ? -1 : Pico.m.scanline;\r
+ }\r
}\r
if (num == 12 && ((pvid->reg[12]^d)&0x01))\r
PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);\r
- DrawSync(SekCyclesDone() - Pico.t.m68c_line_start <= 488-390);\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
switch (num)\r