platform ps2, handle audio similar to psp
[picodrive.git] / pico / draw.c
CommitLineData
cff531af 1/*\r
2 * line renderer\r
3 * (c) Copyright Dave, 2004\r
4 * (C) notaz, 2006-2010\r
7bf552b5 5 * (C) irixxxx, 2019-2024\r
cff531af 6 *\r
7 * This work is licensed under the terms of MAME license.\r
8 * See COPYING file in the top-level directory.\r
9 */\r
e5fa9817 10/*\r
11 * The renderer has 4 modes now:\r
12 * - normal\r
13 * - shadow/hilight (s/h)\r
40644bfe 14 * - "sonic mode" for midline palette changes (8bit mode only)\r
15 * - accurate sprites (AS) [+ s/h]\r
e5fa9817 16 *\r
ee3c39ef 17 * s/h uses upper bits for both priority and shadow/hilight flags.\r
e5fa9817 18 * "sonic mode" is autodetected, shadow/hilight is enabled by emulated game.\r
19 * AS is enabled by user and takes priority over "sonic mode".\r
40644bfe 20 *\r
bfa12428 21 * since renderer always draws line in 8bit mode, there are 2 spare bits:\r
6bfa97ff 22 * b \ mode: s/h sonic\r
23 * 00 normal pal index\r
24 * 01 hilight pal index\r
25 * 10 shadow pal index\r
26 * 11 hilight|shadow=normal pal index\r
27 *\r
71a2e205 28 * sprite s/h can only be correctly done after the plane rendering s/h state is\r
29 * known since the s/h result changes if there's at least one high prio plane.\r
6bfa97ff 30 * sprite op rendering is deferred until this is known, and hilight is used as\r
31 * mark since it can't occur before sprite ops:\r
32 * x1 op marker pal index\r
33 *\r
34 * low prio s/h rendering:\r
35 * - plane and non-op sprite pixels have shadow\r
33cb1ab0 36 * - s/h sprite op pixel rendering is marked with hilight (deferred)\r
6bfa97ff 37 * high prio s/h rendering:\r
38 * - plane and non-op sprite pixels are normal\r
33cb1ab0 39 * - all s/h sprite op pixels (either marked or high prio) are rendered\r
bfa12428 40 *\r
40644bfe 41 * not handled properly:\r
33cb1ab0 42 * - high prio s/h sprite op overlapping low prio sprite shows sprite, not A,B,G\r
43 * - in debug sprite-masked, transparent high-prio sprite px don't remove shadow\r
e5fa9817 44 */\r
cc68a136 45\r
efcba75f 46#include "pico_int.h"\r
d5d17782 47#include <platform/common/upscale.h>\r
48\r
6dd553c7 49#define FORCE // layer forcing via debug register?\r
cc68a136 50\r
602133e1 51int (*PicoScanBegin)(unsigned int num) = NULL;\r
52int (*PicoScanEnd) (unsigned int num) = NULL;\r
cc68a136 53\r
ea8c405f 54static unsigned char DefHighCol[8+320+8];\r
7980d477 55unsigned char *HighColBase = DefHighCol;\r
56int HighColIncrement;\r
5a681086 57\r
f821bb70 58static u16 DefOutBuff[320*2] ALIGNED(4);\r
5a681086 59void *DrawLineDestBase = DefOutBuff;\r
60int DrawLineDestIncrement;\r
ea8c405f 61\r
15eed405 62static u32 HighCacheA[41*2+1]; // caches for high layers\r
63static u32 HighCacheB[41*2+1];\r
ae9a76c9 64static s32 HighPreSpr[128*2*2]; // slightly preprocessed sprites (2 banks a 128)\r
65static int HighPreSprBank;\r
381eea9b 66\r
ac891449 67u32 VdpSATCache[2*128]; // VDP sprite cache (1st 32 sprite attr bits)\r
25be5c52 68\r
6bfa97ff 69// NB don't change any defines without checking their usage in ASM\r
70\r
16b11d91 71#if defined(USE_BGR555)\r
72#define PXCONV(t) ((t & 0x000e000e)<< 1) | ((t & 0x00e000e0)<<2) | ((t & 0x0e000e00)<<3)\r
73#define PXMASKL 0x04210421 // 0x0c630c63, LSB for all colours\r
74#define PXMASKH 0x39ce39ce // 0x3def3def, all but MSB for all colours\r
cdc6aac4 75#elif defined(USE_BGR565)\r
76#define PXCONV(t) ((t & 0x000e000e)<< 1) | ((t & 0x00e000e0)<<3) | ((t & 0x0e000e00)<<4)\r
77#define PXMASKL 0x08610861 // 0x18e318e3\r
78#define PXMASKH 0x738e738e // 0x7bef7bef\r
16b11d91 79#else // RGB565\r
80#define PXCONV(t) ((t & 0x000e000e)<<12) | ((t & 0x00e000e0)<<3) | ((t & 0x0e000e00)>>7)\r
81#define PXMASKL 0x08610861 // 0x18e318e3\r
82#define PXMASKH 0x738e738e // 0x7bef7bef\r
83#endif\r
84\r
6bfa97ff 85#define LF_PLANE (1 << 0) // must be = 1\r
e0bcb7a9 86#define LF_SH (1 << 1) // must be = 2\r
71a2e205 87//#define LF_FORCE (1 << 2)\r
e0bcb7a9 88\r
6bfa97ff 89#define LF_PLANE_A 0\r
90#define LF_PLANE_B 1\r
91\r
fbc65db7 92#define SPRL_HAVE_HI 0x80 // have hi priority sprites\r
93#define SPRL_HAVE_LO 0x40 // *lo*\r
94#define SPRL_MAY_HAVE_OP 0x20 // may have operator sprites on the line\r
95#define SPRL_LO_ABOVE_HI 0x10 // low priority sprites may be on top of hi\r
5f0d224e 96#define SPRL_HAVE_X 0x08 // have sprites with x != 0\r
97#define SPRL_TILE_OVFL 0x04 // tile limit exceeded on previous line\r
98#define SPRL_HAVE_MASK0 0x02 // have sprite with x == 0 in 1st slot\r
99#define SPRL_MASKED 0x01 // lo prio masking by sprite with x == 0 active\r
6bfa97ff 100\r
d515a352 101// sprite cache. stores results of sprite parsing for each display line:\r
102// [visible_sprites_count, sprl_flags, tile_count, sprites_processed, sprite_idx[sprite_count], last_width]\r
103unsigned char HighLnSpr[240][4+MAX_LINE_SPRITES+1];\r
d9fc2fe1 104\r
ea38612f 105int rendstatus_old;\r
ae87bffa 106int rendlines;\r
cc68a136 107\r
602133e1 108static int skip_next_line=0;\r
109\r
cc68a136 110struct TileStrip\r
111{\r
112 int nametab; // Position in VRAM of name table (for this tile line)\r
6d7acf9e 113 int line; // Line number in pixels 0x000-0x3ff within the virtual tilemap\r
cc68a136 114 int hscroll; // Horizontal scroll value in pixels for the line\r
115 int xmask; // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap\r
15eed405 116 u32 *hc; // cache for high tile codes and their positions\r
cc68a136 117 int cells; // cells (tiles) to draw (32 col mode doesn't need to update whole 320)\r
118};\r
119\r
120// stuff available in asm:\r
121#ifdef _ASM_DRAW_C\r
ea38612f 122void DrawWindow(int tstart, int tend, int prio, int sh,\r
123 struct PicoEState *est);\r
124void DrawAllSprites(unsigned char *sprited, int prio, int sh,\r
125 struct PicoEState *est);\r
15eed405 126void DrawTilesFromCache(u32 *hc, int sh, int rlim,\r
ea38612f 127 struct PicoEState *est);\r
128void DrawSpritesSHi(unsigned char *sprited, struct PicoEState *est);\r
15eed405 129void DrawLayer(int plane_sh, u32 *hcache, int cellskip, int maxcells,\r
ea38612f 130 struct PicoEState *est);\r
7b802576 131void *blockcpy(void *dst, const void *src, size_t n);\r
cc68a136 132void blockcpy_or(void *dst, void *src, size_t n, int pat);\r
133#else\r
134// utility\r
135void blockcpy_or(void *dst, void *src, size_t n, int pat)\r
136{\r
137 unsigned char *pd = dst, *ps = src;\r
d2e3f475 138 if (dst > src) {\r
139 for (pd += n, ps += n; n; n--)\r
140 *--pd = (unsigned char) (*--ps | pat);\r
141 } else\r
142 for (; n; n--)\r
143 *pd++ = (unsigned char) (*ps++ | pat);\r
cc68a136 144}\r
466fa079 145#define blockcpy memmove\r
cc68a136 146#endif\r
147\r
d9e12ee7 148#define TileNormMaker_(pix_func,ret) \\r
07abbab1 149{ \\r
15eed405 150 unsigned char t; \\r
07abbab1 151 \\r
1a08dec0 152 t = (pack&0x0000f000)>>12; pix_func(0); \\r
153 t = (pack&0x00000f00)>> 8; pix_func(1); \\r
154 t = (pack&0x000000f0)>> 4; pix_func(2); \\r
155 t = (pack&0x0000000f) ; pix_func(3); \\r
156 t = (pack&0xf0000000)>>28; pix_func(4); \\r
157 t = (pack&0x0f000000)>>24; pix_func(5); \\r
158 t = (pack&0x00f00000)>>20; pix_func(6); \\r
159 t = (pack&0x000f0000)>>16; pix_func(7); \\r
d9e12ee7 160 return ret; \\r
cc68a136 161}\r
162\r
d9e12ee7 163#define TileFlipMaker_(pix_func,ret) \\r
07abbab1 164{ \\r
15eed405 165 unsigned char t; \\r
07abbab1 166 \\r
1a08dec0 167 t = (pack&0x000f0000)>>16; pix_func(0); \\r
168 t = (pack&0x00f00000)>>20; pix_func(1); \\r
169 t = (pack&0x0f000000)>>24; pix_func(2); \\r
170 t = (pack&0xf0000000)>>28; pix_func(3); \\r
171 t = (pack&0x0000000f) ; pix_func(4); \\r
172 t = (pack&0x000000f0)>> 4; pix_func(5); \\r
173 t = (pack&0x00000f00)>> 8; pix_func(6); \\r
174 t = (pack&0x0000f000)>>12; pix_func(7); \\r
d9e12ee7 175 return ret; \\r
cc68a136 176}\r
cc68a136 177\r
ee3c39ef 178#define TileNormMaker(funcname, pix_func) \\r
15eed405 179static void funcname(unsigned char *pd, unsigned int pack, unsigned char pal) \\r
d9e12ee7 180TileNormMaker_(pix_func,)\r
ee3c39ef 181\r
182#define TileFlipMaker(funcname, pix_func) \\r
15eed405 183static void funcname(unsigned char *pd, unsigned int pack, unsigned char pal) \\r
d9e12ee7 184TileFlipMaker_(pix_func,)\r
ee3c39ef 185\r
186#define TileNormMakerAS(funcname, pix_func) \\r
15eed405 187static unsigned funcname(unsigned m, unsigned char *pd, unsigned int pack, unsigned char pal) \\r
d9e12ee7 188TileNormMaker_(pix_func,m)\r
ee3c39ef 189\r
190#define TileFlipMakerAS(funcname, pix_func) \\r
15eed405 191static unsigned funcname(unsigned m, unsigned char *pd, unsigned int pack, unsigned char pal) \\r
d9e12ee7 192TileFlipMaker_(pix_func,m)\r
cc68a136 193\r
6bfa97ff 194// draw layer or non-s/h sprite pixels (no operator colors)\r
07abbab1 195#define pix_just_write(x) \\r
15eed405 196 if (likely(t)) pd[x]=pal|t\r
cc68a136 197\r
6bfa97ff 198TileNormMaker(TileNorm, pix_just_write)\r
199TileFlipMaker(TileFlip, pix_just_write)\r
cc68a136 200\r
e352c3af 201#ifndef _ASM_DRAW_C\r
202\r
08bbe7f8 203// draw low prio sprite non-s/h pixels in s/h mode\r
204#define pix_nonsh(x) \\r
15eed405 205 if (likely(t)) { \\r
206 pd[x]=pal|t; \\r
207 if (unlikely(t==0xe)) pd[x]&=~0x80; /* disable shadow for color 14 (hw bug?) */ \\r
208 }\r
08bbe7f8 209\r
210TileNormMaker(TileNormNonSH, pix_nonsh)\r
211TileFlipMaker(TileFlipNonSH, pix_nonsh)\r
212\r
6bfa97ff 213// draw sprite pixels, process operator colors\r
07abbab1 214#define pix_sh(x) \\r
15eed405 215 if (likely(t)) \\r
f821bb70 216 pd[x]=(likely(t<0xe) ? pal|t : pd[x]|((t-1)<<6))\r
e5fa9817 217\r
07abbab1 218TileNormMaker(TileNormSH, pix_sh)\r
219TileFlipMaker(TileFlipSH, pix_sh)\r
cc68a136 220\r
6bfa97ff 221// draw sprite pixels, mark but don't process operator colors\r
40644bfe 222#define pix_sh_markop(x) \\r
15eed405 223 if (likely(t)) \\r
f821bb70 224 pd[x]=(likely(t<0xe) ? pal|t : pd[x]|0x40)\r
cc68a136 225\r
40644bfe 226TileNormMaker(TileNormSH_markop, pix_sh_markop)\r
227TileFlipMaker(TileFlipSH_markop, pix_sh_markop)\r
6c254710 228\r
ee3c39ef 229#endif\r
230\r
6bfa97ff 231// draw low prio sprite operator pixels if visible (i.e. marked)\r
07abbab1 232#define pix_sh_onlyop(x) \\r
15eed405 233 if (unlikely(t>=0xe && (pd[x]&0x40))) \\r
71a2e205 234 pd[x]=(pd[x]&~0x40)|((t-1)<<6)\r
cc68a136 235\r
ee3c39ef 236#ifndef _ASM_DRAW_C\r
237\r
07abbab1 238TileNormMaker(TileNormSH_onlyop_lp, pix_sh_onlyop)\r
239TileFlipMaker(TileFlipSH_onlyop_lp, pix_sh_onlyop)\r
cc68a136 240\r
e352c3af 241#endif\r
242\r
d9e12ee7 243// AS: sprite mask bits in m shifted to bits 8-15, see DrawSpritesHiAS\r
244\r
6bfa97ff 245// draw high prio sprite pixels (AS)\r
07abbab1 246#define pix_as(x) \\r
15eed405 247 if (likely(t && (m & (1<<(x+8))))) \\r
248 m &= ~(1<<(x+8)), pd[x] = pal|t\r
cc68a136 249\r
ee3c39ef 250TileNormMakerAS(TileNormAS, pix_as)\r
251TileFlipMakerAS(TileFlipAS, pix_as)\r
cc68a136 252\r
6bfa97ff 253// draw high prio sprite pixels, process operator colors (AS)\r
71a2e205 254// NB sprite+planes: h+s->n, h+[nh]->h, s+[nhs]->s, hence mask h before op\r
ee3c39ef 255#define pix_sh_as(x) \\r
15eed405 256 if (likely(t && (m & (1<<(x+8))))) { \\r
d9e12ee7 257 m &= ~(1<<(x+8)); \\r
15eed405 258 pd[x]=(likely(t<0xe) ? pal|t : (pd[x]&~0x40)|((t-1)<<6)); \\r
ee3c39ef 259 }\r
260\r
261TileNormMakerAS(TileNormSH_AS, pix_sh_as)\r
262TileFlipMakerAS(TileFlipSH_AS, pix_sh_as)\r
263\r
6bfa97ff 264// draw only sprite operator pixels (AS)\r
ee3c39ef 265#define pix_sh_as_onlyop(x) \\r
15eed405 266 if (likely(t && (m & (1<<(x+8))))) { \\r
d9e12ee7 267 m &= ~(1<<(x+8)); \\r
ee3c39ef 268 pix_sh_onlyop(x); \\r
269 }\r
cc68a136 270\r
ee3c39ef 271TileNormMakerAS(TileNormSH_AS_onlyop_lp, pix_sh_as_onlyop)\r
272TileFlipMakerAS(TileFlipSH_AS_onlyop_lp, pix_sh_as_onlyop)\r
cc68a136 273\r
6bfa97ff 274// mark low prio sprite pixels (AS)\r
07abbab1 275#define pix_sh_as_onlymark(x) \\r
15eed405 276 if (likely(t)) m &= ~(1<<(x+8))\r
cc68a136 277\r
ee3c39ef 278TileNormMakerAS(TileNormAS_onlymark, pix_sh_as_onlymark)\r
279TileFlipMakerAS(TileFlipAS_onlymark, pix_sh_as_onlymark)\r
cc68a136 280\r
6dd553c7 281#ifdef FORCE\r
71a2e205 282// NB s/h already resolved by non-forced drawing\r
ee3c39ef 283// forced both layer draw (through debug reg)\r
e0bcb7a9 284#define pix_and(x) \\r
33cb1ab0 285 pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \\r
15eed405 286 pd[x] &= pal|t\r
e0bcb7a9 287\r
288TileNormMaker(TileNorm_and, pix_and)\r
289TileFlipMaker(TileFlip_and, pix_and)\r
e5fa9817 290\r
2d5b6a66 291// forced sprite draw (through debug reg)\r
71a2e205 292#define pix_sh_as_and(x) \\r
33cb1ab0 293 pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \\r
15eed405 294 if (likely(m & (1<<(x+8)))) { \\r
6dd553c7 295 m &= ~(1<<(x+8)); \\r
d515a352 296 /* if (!t) pd[x] |= 0x40; as per titan hw notes? */ \\r
297 pd[x] &= pal|t; \\r
6dd553c7 298 }\r
ae9a76c9 299\r
6dd553c7 300TileNormMakerAS(TileNormSH_AS_and, pix_sh_as_and)\r
301TileFlipMakerAS(TileFlipSH_AS_and, pix_sh_as_and)\r
302#endif\r
2d5b6a66 303\r
cc68a136 304// --------------------------------------------\r
305\r
306#ifndef _ASM_DRAW_C\r
11a1966b 307#define DrawTile(mask) { \\r
308 if (code!=oldcode) { \\r
309 oldcode = code; \\r
310 \\r
311 pack = 0; \\r
312 if (code != blank) { \\r
313 /* Get tile address/2: */ \\r
314 u32 addr = ((code&0x7ff)<<4) + ty; \\r
315 if (code & 0x1000) addr ^= 0xe; /* Y-flip */ \\r
316 \\r
317 pal = ((code>>9)&0x30) | sh; /* shadow */ \\r
318 \\r
319 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); \\r
320 if (!pack) \\r
321 blank = code; \\r
322 } \\r
323 } \\r
324 \\r
325 if (code & 0x8000) { /* (un-forced) high priority tile */ \\r
326 if (sh | (pack&mask)) { \\r
327 code |= (dx<<16) | (ty<<25); \\r
328 if (code & 0x1000) code ^= 0xe<<25; \\r
329 *hc++ = code, *hc++ = pack&mask; /* cache it */ \\r
330 } \\r
331 } else if (pack&mask) { \\r
332 if (code & 0x0800) TileFlip(pd + dx, pack&mask, pal); \\r
333 else TileNorm(pd + dx, pack&mask, pal); \\r
334 } \\r
1fad746a 335}\r
336\r
e0bcb7a9 337static void DrawStrip(struct TileStrip *ts, int lflags, int cellskip)\r
cc68a136 338{\r
ee3c39ef 339 unsigned char *pd = Pico.est.HighCol;\r
15eed405 340 u32 *hc = ts->hc;\r
71a2e205 341 int tilex, dx, ty, cells;\r
1fad746a 342 u32 code, oldcode = -1, blank = -1; // The tile we know is blank\r
343 unsigned int pal = 0, pack = 0, sh, mask = ~0;\r
cc68a136 344\r
345 // Draw tiles across screen:\r
7165b73c 346 sh = (lflags & LF_SH) << 6; // shadow\r
83c093a4 347 tilex=((-ts->hscroll)>>3)+cellskip;\r
cc68a136 348 ty=(ts->line&7)<<1; // Y-Offset into tile\r
349 dx=((ts->hscroll-1)&7)+1;\r
83c093a4 350 cells = ts->cells - cellskip;\r
83c093a4 351 dx+=cellskip<<3;\r
cc68a136 352\r
1fad746a 353 if (dx & 7) {\r
354 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
355 mask = 0xffffffff<<((dx&7)*4);\r
356 if (code & 0x0800) mask = 0xffffffff>>((dx&7)*4);\r
357 mask = (~mask << 16) | (~mask >> 16);\r
358\r
359 DrawTile(mask);\r
360 dx += 8, tilex++, cells--;\r
361 }\r
362\r
71a2e205 363// int force = (lflags&LF_FORCE) << 13;\r
e0bcb7a9 364 for (; cells > 0; dx+=8, tilex++, cells--)\r
cc68a136 365 {\r
1fad746a 366 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
71a2e205 367// code &= ~force; // forced always draw everything\r
368\r
7165b73c 369 if (code == blank && !((code & 0x8000) && sh))\r
71a2e205 370 continue;\r
cc68a136 371\r
1fad746a 372 DrawTile(~0);\r
373 }\r
47677a2a 374\r
1fad746a 375 if (dx & 7) {\r
376 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
377// code &= ~force; // forced always draw everything\r
378 if (!(code == blank && !((code & 0x8000) && sh))) {\r
379 mask = 0xffffffff<<((dx&7)*4);\r
380 if (code & 0x0800) mask = 0xffffffff>>((dx&7)*4);\r
381 mask = (mask << 16) | (mask >> 16);\r
cc68a136 382\r
1fad746a 383 DrawTile(mask);\r
1a08dec0 384 }\r
cc68a136 385 }\r
386\r
387 // terminate the cache list\r
71a2e205 388 *hc = 0;\r
389\r
740da8c6 390 // if oldcode wasn't changed, it means all layer is hi priority\r
ea38612f 391 if (oldcode == -1) Pico.est.rendstatus |= PDRAW_PLANE_HI_PRIO;\r
cc68a136 392}\r
cc68a136 393\r
394// this is messy\r
1a08dec0 395static void DrawStripVSRam(struct TileStrip *ts, int plane_sh, int cellskip)\r
cc68a136 396{\r
ee3c39ef 397 unsigned char *pd = Pico.est.HighCol;\r
15eed405 398 u32 *hc = ts->hc;\r
71a2e205 399 int tilex, dx, ty = 0, addr = 0, cell = 0, nametabadd = 0;\r
4cc0fcaf 400 u32 oldcode = -1, blank = -1; // The tile we know is blank\r
7165b73c 401 unsigned int pal = 0, scan = Pico.est.DrawScanline, sh, plane;\r
cc68a136 402\r
403 // Draw tiles across screen:\r
7165b73c 404 sh = (plane_sh & LF_SH) << 6; // shadow\r
405 plane = (plane_sh & LF_PLANE); // plane to draw\r
cc68a136 406 tilex=(-ts->hscroll)>>3;\r
407 dx=((ts->hscroll-1)&7)+1;\r
cf07a88d 408 if (ts->hscroll & 0x0f) {\r
409 int adj = ((ts->hscroll ^ dx) >> 3) & 1;\r
410 cell -= adj + 1;\r
411 ts->cells -= adj;\r
6dd553c7 412 PicoMem.vsram[0x3e] = PicoMem.vsram[0x3f] = plane_sh >> 16;\r
cf07a88d 413 }\r
83c093a4 414 cell+=cellskip;\r
415 tilex+=cellskip;\r
416 dx+=cellskip<<3;\r
cc68a136 417\r
71a2e205 418// int force = (plane_sh&LF_FORCE) << 13;\r
33cb1ab0 419 if ((cell&1)==1)\r
420 {\r
421 int line,vscroll;\r
422 vscroll = PicoMem.vsram[plane + (cell&0x3e)];\r
423\r
424 // Find the line in the name table\r
425 line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
426 nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width]\r
427 ty=(line&7)<<1; // Y-Offset into tile\r
428 }\r
cc68a136 429 for (; cell < ts->cells; dx+=8,tilex++,cell++)\r
430 {\r
71a2e205 431 u32 code, pack;\r
cc68a136 432\r
33cb1ab0 433 if ((cell&1)==0)\r
83c093a4 434 {\r
cc68a136 435 int line,vscroll;\r
7165b73c 436 vscroll = PicoMem.vsram[plane + (cell&0x3e)];\r
cc68a136 437\r
438 // Find the line in the name table\r
439 line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
440 nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width]\r
441 ty=(line&7)<<1; // Y-Offset into tile\r
442 }\r
443\r
71a2e205 444 code= PicoMem.vram[ts->nametab + nametabadd + (tilex & ts->xmask)];\r
445// code &= ~force; // forced always draw everything\r
4cc0fcaf 446 code |= ty<<25; // add ty since that can change pixel row for every 2nd tile\r
71a2e205 447\r
7165b73c 448 if (code == blank && !((code & 0x8000) && sh))\r
71a2e205 449 continue;\r
450\r
cc68a136 451 if (code!=oldcode) {\r
452 oldcode = code;\r
453 // Get tile address/2:\r
47677a2a 454 addr = (code&0x7ff)<<4;\r
cc68a136 455\r
7165b73c 456 pal = ((code>>9)&0x30) | sh; // shadow\r
cc68a136 457 }\r
458\r
71a2e205 459 pack = (code & 0x1000 ? ty^0xe : ty); // Y-flip\r
57c5a5e5 460 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr+pack));\r
47677a2a 461 if (!pack)\r
462 blank = code;\r
29d99d6b 463\r
71a2e205 464 if (code & 0x8000) { // (un-forced) high priority tile\r
4cc0fcaf 465 code |= (dx<<16);\r
466 if (code & 0x1000) code ^= 0xe<<25;\r
467 *hc++ = code, *hc++ = pack; // cache it\r
71a2e205 468 } else if (code != blank) {\r
469 if (code & 0x0800) TileFlip(pd + dx, pack, pal);\r
470 else TileNorm(pd + dx, pack, pal);\r
1a08dec0 471 }\r
cc68a136 472 }\r
473\r
474 // terminate the cache list\r
71a2e205 475 *hc = 0;\r
476\r
ea38612f 477 if (oldcode == -1) Pico.est.rendstatus |= PDRAW_PLANE_HI_PRIO;\r
cc68a136 478}\r
6d7acf9e 479#endif\r
cc68a136 480\r
11a1966b 481#define DrawTileInterlace(mask) { \\r
482 if (code!=oldcode) { \\r
483 oldcode = code; \\r
484 \\r
485 pack = 0; \\r
486 if (code != blank) { \\r
487 /* Get tile address/2: */ \\r
488 u32 addr = ((code&0x3ff)<<5) + ty; \\r
489 if (code & 0x1000) addr ^= 0x1e; /* Y-flip */ \\r
490 \\r
491 pal = ((code>>9)&0x30) | sh; /* shadow */ \\r
492 \\r
493 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); \\r
494 if (!pack) \\r
495 blank = code; \\r
496 } \\r
497 } \\r
498 \\r
499 if (code & 0x8000) { /* high priority tile */ \\r
500 if (sh | (pack&mask)) { \\r
501 code = (code&0xfc00) | ((code&0x3ff)<<1) | (dx<<16) | (ty<<25); \\r
502 if (code & 0x1000) code ^= 0x1e<<25; \\r
503 *hc++ = code, *hc++ = pack&mask; /* cache it */ \\r
504 } \\r
505 } else if (pack&mask) { \\r
506 if (code & 0x0800) TileFlip(pd + dx, pack&mask, pal); \\r
507 else TileNorm(pd + dx, pack&mask, pal); \\r
508 } \\r
1fad746a 509}\r
510\r
cc68a136 511#ifndef _ASM_DRAW_C\r
512static\r
513#endif\r
6bfa97ff 514void DrawStripInterlace(struct TileStrip *ts, int plane_sh)\r
cc68a136 515{\r
ee3c39ef 516 unsigned char *pd = Pico.est.HighCol;\r
15eed405 517 u32 *hc = ts->hc;\r
71a2e205 518 int tilex = 0, dx = 0, ty = 0, cells;\r
1fad746a 519 u32 code, oldcode = -1, blank = -1; // The tile we know is blank\r
520 unsigned int pal = 0, pack = 0, sh, mask = ~0;\r
cc68a136 521\r
522 // Draw tiles across screen:\r
7165b73c 523 sh = (plane_sh & LF_SH) << 6; // shadow\r
cc68a136 524 tilex=(-ts->hscroll)>>3;\r
525 ty=(ts->line&15)<<1; // Y-Offset into tile\r
526 dx=((ts->hscroll-1)&7)+1;\r
527 cells = ts->cells;\r
1fad746a 528\r
529 if (dx & 7) {\r
530 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
531 mask = 0xffffffff<<(dx*4);\r
532 if (code & 0x0800) mask = 0xffffffff>>(dx*4);\r
533 mask = (~mask << 16) | (~mask >> 16);\r
534\r
535 DrawTileInterlace(mask);\r
536 dx += 8, tilex++, cells--;\r
537 }\r
cc68a136 538\r
71a2e205 539// int force = (plane_sh&LF_FORCE) << 13;\r
cc68a136 540 for (; cells; dx+=8,tilex++,cells--)\r
541 {\r
71a2e205 542 u32 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
543// code &= ~force; // forced always draw everything\r
544\r
545 if (code == blank && !(code & 0x8000))\r
546 continue;\r
cc68a136 547\r
1fad746a 548 DrawTileInterlace(~0);\r
549 }\r
47677a2a 550\r
1fad746a 551 if (dx & 7) {\r
552 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
553// code &= ~force; // forced always draw everything\r
554 if (!(code == blank && !((code & 0x8000) && sh))) {\r
555 mask = 0xffffffff<<((dx&7)*4);\r
556 if (code & 0x0800) mask = 0xffffffff>>((dx&7)*4);\r
557 mask = (mask << 16) | (mask >> 16);\r
cc68a136 558\r
1fad746a 559 DrawTileInterlace(mask);\r
1a08dec0 560 }\r
cc68a136 561 }\r
562\r
563 // terminate the cache list\r
71a2e205 564 *hc = 0;\r
cc68a136 565}\r
566\r
567// --------------------------------------------\r
568\r
569#ifndef _ASM_DRAW_C\r
15eed405 570static void DrawLayer(int plane_sh, u32 *hcache, int cellskip, int maxcells,\r
ea38612f 571 struct PicoEState *est)\r
cc68a136 572{\r
17c1401b 573 struct PicoVideo *pvid=&est->Pico->video;\r
cc68a136 574 const char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps (2 is invalid)\r
575 struct TileStrip ts;\r
576 int width, height, ymask;\r
577 int vscroll, htab;\r
578\r
579 ts.hc=hcache;\r
580 ts.cells=maxcells;\r
581\r
582 // Work out the TileStrip to draw\r
583\r
584 // Work out the name table size: 32 64 or 128 tiles (0-3)\r
585 width=pvid->reg[16];\r
586 height=(width>>4)&3; width&=3;\r
587\r
588 ts.xmask=(1<<shift[width])-1; // X Mask in tiles (0x1f-0x7f)\r
589 ymask=(height<<8)|0xff; // Y Mask in pixels\r
eced0190 590 switch (width) {\r
591 case 1: ymask &= 0x1ff; break;\r
592 case 2: ymask = 0x007; break;\r
593 case 3: ymask = 0x0ff; break;\r
594 }\r
cc68a136 595\r
596 // Find name table:\r
6bfa97ff 597 if (plane_sh&LF_PLANE) ts.nametab=(pvid->reg[4]&0x07)<<12; // B\r
598 else ts.nametab=(pvid->reg[2]&0x38)<< 9; // A\r
cc68a136 599\r
600 htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
29d99d6b 601 switch (pvid->reg[11]&3) {\r
602 case 1: htab += (est->DrawScanline<<1) & 0x0f; break;\r
603 case 2: htab += (est->DrawScanline<<1) & ~0x0f; break; // Offset by tile\r
604 case 3: htab += (est->DrawScanline<<1); break; // Offset by line\r
605 }\r
6bfa97ff 606 htab+=plane_sh&LF_PLANE; // A or B\r
cc68a136 607\r
608 // Get horizontal scroll value, will be masked later\r
88fd63ad 609 ts.hscroll = PicoMem.vram[htab & 0x7fff];\r
cc68a136 610\r
611 if((pvid->reg[12]&6) == 6) {\r
612 // interlace mode 2\r
6bfa97ff 613 vscroll = PicoMem.vsram[plane_sh&LF_PLANE]; // Get vertical scroll value\r
cc68a136 614\r
615 // Find the line in the name table\r
ea38612f 616 ts.line=(vscroll+(est->DrawScanline<<1))&((ymask<<1)|1);\r
cc68a136 617 ts.nametab+=(ts.line>>4)<<shift[width];\r
618\r
6bfa97ff 619 DrawStripInterlace(&ts, plane_sh);\r
cbb9a098 620 } else if (pvid->reg[11]&4) {\r
cc68a136 621 // shit, we have 2-cell column based vscroll\r
622 // luckily this doesn't happen too often\r
623 ts.line=ymask|(shift[width]<<24); // save some stuff instead of line\r
61114cd8 624 // vscroll value for leftmost cells in case of hscroll not on 16px boundary\r
625 // XXX it's unclear what exactly the hw is doing. Continue reading where it\r
626 // stopped last seems to work best (H40: 0x50 (wrap->0x00), H32 0x40).\r
6bfa97ff 627 plane_sh |= PicoMem.vsram[(pvid->reg[12]&1?0x00:0x20) + (plane_sh&LF_PLANE)] << 16;\r
83c093a4 628 DrawStripVSRam(&ts, plane_sh, cellskip);\r
cc68a136 629 } else {\r
6bfa97ff 630 vscroll = PicoMem.vsram[plane_sh&LF_PLANE]; // Get vertical scroll value\r
cc68a136 631\r
632 // Find the line in the name table\r
ea38612f 633 ts.line=(vscroll+est->DrawScanline)&ymask;\r
cc68a136 634 ts.nametab+=(ts.line>>3)<<shift[width];\r
635\r
83c093a4 636 DrawStrip(&ts, plane_sh, cellskip);\r
cc68a136 637 }\r
638}\r
639\r
640\r
641// --------------------------------------------\r
642\r
643// tstart & tend are tile pair numbers\r
ea38612f 644static void DrawWindow(int tstart, int tend, int prio, int sh,\r
645 struct PicoEState *est)\r
cc68a136 646{\r
17c1401b 647 unsigned char *pd = est->HighCol;\r
648 struct PicoVideo *pvid = &est->Pico->video;\r
07abbab1 649 int tilex,ty,nametab,code=0;\r
cc68a136 650 int blank=-1; // The tile we know is blank\r
651\r
652 // Find name table line:\r
653 if (pvid->reg[12]&1)\r
654 {\r
655 nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode\r
ea38612f 656 nametab+=(est->DrawScanline>>3)<<6;\r
cc68a136 657 }\r
658 else\r
659 {\r
660 nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode\r
ea38612f 661 nametab+=(est->DrawScanline>>3)<<5;\r
cc68a136 662 }\r
663\r
664 tilex=tstart<<1;\r
cc68a136 665\r
47677a2a 666 if (prio && !(est->rendstatus & PDRAW_WND_DIFF_PRIO)) {\r
47548249 667 // all tiles processed in low prio pass\r
668 return;\r
cc68a136 669 }\r
670\r
07abbab1 671 tend<<=1;\r
ea38612f 672 ty=(est->DrawScanline&7)<<1; // Y-Offset into tile\r
07abbab1 673\r
cc68a136 674 // Draw tiles across screen:\r
81fda4e8 675 if (!sh)\r
cc68a136 676 {\r
81fda4e8 677 for (; tilex < tend; tilex++)\r
678 {\r
1a08dec0 679 unsigned int pack;\r
680 int dx, addr;\r
81fda4e8 681 int pal;\r
682\r
88fd63ad 683 code = PicoMem.vram[nametab + tilex];\r
602133e1 684 if ((code>>15) != prio) {\r
47677a2a 685 est->rendstatus |= PDRAW_WND_DIFF_PRIO;\r
81fda4e8 686 continue;\r
687 }\r
47548249 688 if (code==blank) continue;\r
cc68a136 689\r
81fda4e8 690 // Get tile address/2:\r
691 addr=(code&0x7ff)<<4;\r
692 if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
693\r
57c5a5e5 694 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr));\r
1a08dec0 695 if (!pack) {\r
696 blank = code;\r
697 continue;\r
698 }\r
699\r
700 pal = ((code >> 9) & 0x30);\r
701 dx = 8 + (tilex << 3);\r
81fda4e8 702\r
ee3c39ef 703 if (code & 0x0800) TileFlip(pd + dx, pack, pal);\r
704 else TileNorm(pd + dx, pack, pal);\r
cc68a136 705 }\r
81fda4e8 706 }\r
707 else\r
708 {\r
709 for (; tilex < tend; tilex++)\r
710 {\r
1a08dec0 711 unsigned int pack;\r
712 int dx, addr;\r
07abbab1 713 int pal;\r
81fda4e8 714\r
88fd63ad 715 code = PicoMem.vram[nametab + tilex];\r
81fda4e8 716 if((code>>15) != prio) {\r
47677a2a 717 est->rendstatus |= PDRAW_WND_DIFF_PRIO;\r
81fda4e8 718 continue;\r
719 }\r
cc68a136 720\r
81fda4e8 721 pal=((code>>9)&0x30);\r
cc68a136 722\r
07abbab1 723 if (prio) {\r
99bdfd31 724 int *zb = (int *)(est->HighCol+8+(tilex<<3));\r
6bfa97ff 725 *zb++ &= 0x7f7f7f7f;\r
726 *zb &= 0x7f7f7f7f;\r
cc68a136 727 } else {\r
6bfa97ff 728 pal |= 0x80;\r
cc68a136 729 }\r
47548249 730 if(code==blank) continue;\r
cc68a136 731\r
81fda4e8 732 // Get tile address/2:\r
733 addr=(code&0x7ff)<<4;\r
734 if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
cc68a136 735\r
57c5a5e5 736 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr));\r
1a08dec0 737 if (!pack) {\r
738 blank = code;\r
739 continue;\r
740 }\r
741\r
742 dx = 8 + (tilex << 3);\r
cc68a136 743\r
ee3c39ef 744 if (code & 0x0800) TileFlip(pd + dx, pack, pal);\r
745 else TileNorm(pd + dx, pack, pal);\r
81fda4e8 746 }\r
cc68a136 747 }\r
cc68a136 748}\r
749\r
750// --------------------------------------------\r
751\r
15eed405 752static void DrawTilesFromCache(u32 *hc, int sh, int rlim, struct PicoEState *est)\r
cc68a136 753{\r
17c1401b 754 unsigned char *pd = est->HighCol;\r
4cc0fcaf 755 u32 code, dx;\r
756 u32 pack;\r
cc68a136 757 int pal;\r
cc68a136 758\r
759 // *ts->hc++ = code | (dx<<16) | (ty<<25); // cache it\r
760\r
ea38612f 761 if (sh && (est->rendstatus & (PDRAW_SHHI_DONE|PDRAW_PLANE_HI_PRIO)))\r
740da8c6 762 {\r
1fad746a 763 if (!(est->rendstatus & PDRAW_SHHI_DONE)) {\r
764 // as some layer has covered whole line with hi priority tiles,\r
765 // we can process whole line and then act as if sh/hi mode was off,\r
766 // but leave lo pri op sprite markers alone\r
767 int *zb = (int *)(Pico.est.HighCol+8);\r
768 int c = rlim / 4;\r
769 while (c--)\r
770 {\r
771 *zb++ &= 0x7f7f7f7f;\r
772 }\r
773 Pico.est.rendstatus |= PDRAW_SHHI_DONE;\r
774 }\r
740da8c6 775 sh = 0;\r
776 }\r
cc68a136 777\r
81fda4e8 778 if (!sh)\r
740da8c6 779 {\r
7a7c6476 780 while ((code=*hc++)) {\r
47677a2a 781 pack = *hc++;\r
1fad746a 782 if (rlim-dx < 0)\r
783 goto last_cut_tile;\r
2a2b4e7e 784 if (!pack)\r
1a08dec0 785 continue;\r
740da8c6 786\r
1a08dec0 787 dx = (code >> 16) & 0x1ff;\r
788 pal = ((code >> 9) & 0x30);\r
81fda4e8 789\r
ee3c39ef 790 if (code & 0x0800) TileFlip(pd + dx, pack, pal);\r
791 else TileNorm(pd + dx, pack, pal);\r
cc68a136 792 }\r
740da8c6 793 }\r
794 else\r
795 {\r
7a7c6476 796 while ((code=*hc++)) {\r
81fda4e8 797 unsigned char *zb;\r
1a08dec0 798\r
47677a2a 799 dx = (code >> 16) & 0x1ff;\r
99bdfd31 800 zb = est->HighCol+dx;\r
6bfa97ff 801 *zb++ &= 0x7f; *zb++ &= 0x7f; *zb++ &= 0x7f; *zb++ &= 0x7f;\r
802 *zb++ &= 0x7f; *zb++ &= 0x7f; *zb++ &= 0x7f; *zb++ &= 0x7f;\r
cc68a136 803\r
47677a2a 804 pack = *hc++;\r
1fad746a 805 if (rlim - dx < 0)\r
806 goto last_cut_tile;\r
1a08dec0 807 if (!pack)\r
808 continue;\r
809\r
810 pal = ((code >> 9) & 0x30);\r
cc68a136 811\r
ee3c39ef 812 if (code & 0x0800) TileFlip(pd + dx, pack, pal);\r
813 else TileNorm(pd + dx, pack, pal);\r
7a7c6476 814 }\r
815 }\r
816 return;\r
817\r
818last_cut_tile:\r
1a08dec0 819 // for vertical window cutoff\r
7a7c6476 820 {\r
1fad746a 821 unsigned int t, mask;\r
822\r
823 // rlim-dx + 8 px to draw -> mask shift 8-(rlim-dx + 8)\r
824 t = -(rlim - dx);\r
825 if (t < 8) {\r
826 mask = 0xffffffff<<(t*4);\r
827 if (code & 0x0800) mask = 0xffffffff>>(t*4);\r
828 mask = (mask << 16) | (mask >> 16);\r
829\r
830 if (pack&mask) {\r
831 if (code & 0x0800) TileFlip(pd + dx, pack&mask, pal);\r
832 else TileNorm(pd + dx, pack&mask, pal);\r
7a7c6476 833 }\r
740da8c6 834 }\r
cc68a136 835 }\r
836}\r
837\r
838// --------------------------------------------\r
839\r
840// Index + 0 : hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size\r
841// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
842\r
b010d7b4 843static void DrawSprite(s32 *sprite, int sh, int w)\r
cc68a136 844{\r
15eed405 845 void (*fTileFunc)(unsigned char *pd, unsigned int pack, unsigned char pal);\r
ee3c39ef 846 unsigned char *pd = Pico.est.HighCol;\r
cc68a136 847 int width=0,height=0;\r
15eed405 848 int row=0;\r
b010d7b4 849 s32 code=0;\r
cc68a136 850 int pal;\r
851 int tile=0,delta=0;\r
852 int sx, sy;\r
cc68a136 853\r
854 // parse the sprite data\r
855 sy=sprite[0];\r
856 code=sprite[1];\r
857 sx=code>>16; // X\r
858 width=sy>>28;\r
859 height=(sy>>24)&7; // Width and height in tiles\r
ae9a76c9 860 sy=(s16)sy; // Y\r
cc68a136 861\r
ea38612f 862 row=Pico.est.DrawScanline-sy; // Row of the sprite we are on\r
cc68a136 863\r
864 if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
865\r
07abbab1 866 tile=code + (row>>3); // Tile number increases going down\r
cc68a136 867 delta=height; // Delta to increase tile by going right\r
868 if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
869\r
07abbab1 870 tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
ee4f03ae 871 delta<<=4; // Delta of address\r
07abbab1 872\r
873 pal=(code>>9)&0x30;\r
6bfa97ff 874 pal|=sh<<7; // shadow\r
e5fa9817 875\r
876 if (sh && (code&0x6000) == 0x6000) {\r
40644bfe 877 if(code&0x0800) fTileFunc=TileFlipSH_markop;\r
878 else fTileFunc=TileNormSH_markop;\r
08bbe7f8 879 } else if (sh) {\r
880 if(code&0x0800) fTileFunc=TileFlipNonSH;\r
881 else fTileFunc=TileNormNonSH;\r
e5fa9817 882 } else {\r
883 if(code&0x0800) fTileFunc=TileFlip;\r
884 else fTileFunc=TileNorm;\r
cc68a136 885 }\r
886\r
5f0d224e 887 if (w) width = w; // tile limit\r
cc68a136 888 for (; width; width--,sx+=8,tile+=delta)\r
889 {\r
1a08dec0 890 unsigned int pack;\r
891\r
cc68a136 892 if(sx<=0) continue;\r
893 if(sx>=328) break; // Offscreen\r
894\r
57c5a5e5 895 pack = CPU_LE2(*(u32 *)(PicoMem.vram + (tile & 0x7fff)));\r
ee3c39ef 896 fTileFunc(pd + sx, pack, pal);\r
cc68a136 897 }\r
898}\r
e5fa9817 899#endif\r
cc68a136 900\r
4cc0fcaf 901static void DrawSpriteInterlace(u32 *sprite)\r
cc68a136 902{\r
ee3c39ef 903 unsigned char *pd = Pico.est.HighCol;\r
cc68a136 904 int width=0,height=0;\r
905 int row=0,code=0;\r
906 int pal;\r
907 int tile=0,delta=0;\r
908 int sx, sy;\r
909\r
910 // parse the sprite data\r
57c5a5e5 911 sy=CPU_LE2(sprite[0]);\r
cc68a136 912 height=sy>>24;\r
913 sy=(sy&0x3ff)-0x100; // Y\r
914 width=(height>>2)&3; height&=3;\r
915 width++; height++; // Width and height in tiles\r
916\r
ea38612f 917 row=(Pico.est.DrawScanline<<1)-sy; // Row of the sprite we are on\r
cc68a136 918\r
57c5a5e5 919 code=CPU_LE2(sprite[1]);\r
cc68a136 920 sx=((code>>16)&0x1ff)-0x78; // X\r
921\r
922 if (code&0x1000) row^=(16<<height)-1; // Flip Y\r
923\r
924 tile=code&0x3ff; // Tile number\r
925 tile+=row>>4; // Tile number increases going down\r
926 delta=height; // Delta to increase tile by going right\r
927 if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
928\r
929 tile<<=5; tile+=(row&15)<<1; // Tile address\r
930\r
931 delta<<=5; // Delta of address\r
932 pal=((code>>9)&0x30); // Get palette pointer\r
933\r
934 for (; width; width--,sx+=8,tile+=delta)\r
935 {\r
1a08dec0 936 unsigned int pack;\r
937\r
cc68a136 938 if(sx<=0) continue;\r
939 if(sx>=328) break; // Offscreen\r
940\r
57c5a5e5 941 pack = CPU_LE2(*(u32 *)(PicoMem.vram + (tile & 0x7fff)));\r
ee3c39ef 942 if (code & 0x0800) TileFlip(pd + sx, pack, pal);\r
943 else TileNorm(pd + sx, pack, pal);\r
cc68a136 944 }\r
945}\r
946\r
947\r
e0bcb7a9 948static NOINLINE void DrawAllSpritesInterlace(int pri, int sh)\r
cc68a136 949{\r
950 struct PicoVideo *pvid=&Pico.video;\r
ea38612f 951 int i,u,table,link=0,sline=Pico.est.DrawScanline<<1;\r
4cc0fcaf 952 u32 *sprites[80]; // Sprite index\r
17c1401b 953 int max_sprites = pvid->reg[12]&1 ? 80 : 64;\r
cc68a136 954\r
955 table=pvid->reg[5]&0x7f;\r
956 if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
957 table<<=8; // Get sprite table address/2\r
958\r
5f0d224e 959 for (i = u = 0; u < max_sprites && link < max_sprites; u++)\r
cc68a136 960 {\r
4cc0fcaf 961 u32 *sprite;\r
cc68a136 962 int code, sx, sy, height;\r
963\r
15eed405 964 sprite=(u32 *)(PicoMem.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
cc68a136 965\r
966 // get sprite info\r
57c5a5e5 967 code = CPU_LE2(sprite[0]);\r
968 sx = CPU_LE2(sprite[1]);\r
cc68a136 969 if(((sx>>15)&1) != pri) goto nextsprite; // wrong priority sprite\r
970\r
971 // check if it is on this line\r
972 sy = (code&0x3ff)-0x100;\r
973 height = (((code>>24)&3)+1)<<4;\r
47677a2a 974 if((sline < sy) | (sline >= sy+height)) goto nextsprite; // no\r
cc68a136 975\r
976 // check if sprite is not hidden offscreen\r
977 sx = (sx>>16)&0x1ff;\r
978 sx -= 0x78; // Get X coordinate + 8\r
47677a2a 979 if((sx <= -8*3) | (sx >= 328)) goto nextsprite;\r
cc68a136 980\r
981 // sprite is good, save it's pointer\r
982 sprites[i++]=sprite;\r
983\r
984 nextsprite:\r
985 // Find next sprite\r
986 link=(code>>16)&0x7f;\r
987 if(!link) break; // End of sprites\r
988 }\r
989\r
990 // Go through sprites backwards:\r
991 for (i-- ;i>=0; i--)\r
992 DrawSpriteInterlace(sprites[i]);\r
993}\r
994\r
995\r
996#ifndef _ASM_DRAW_C\r
40644bfe 997/*\r
998 * s/h drawing: lo_layers|40, lo_sprites|40 && mark_op,\r
999 * hi_layers&=~40, hi_sprites\r
1000 *\r
1001 * Index + 0 : hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size\r
1002 * Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
1003 */\r
ea38612f 1004static void DrawSpritesSHi(unsigned char *sprited, const struct PicoEState *est)\r
cc68a136 1005{\r
15eed405 1006 static void (*tilefuncs[2][2][2])(unsigned char *, unsigned, unsigned char) = {\r
d9e12ee7 1007 { {NULL, NULL}, {TileNorm, TileFlip} },\r
1008 { {TileNormSH_onlyop_lp, TileFlipSH_onlyop_lp}, {TileNormSH, TileFlipSH} }\r
1009 }; // [sh?][hi?][flip?]\r
15eed405 1010 void (*fTileFunc)(unsigned char *pd, unsigned int pack, unsigned char pal);\r
ee3c39ef 1011 unsigned char *pd = Pico.est.HighCol;\r
ee4f03ae 1012 unsigned char *p;\r
5f0d224e 1013 int cnt, w;\r
ee4f03ae 1014\r
ae9a76c9 1015 cnt = sprited[0] & 0x7f;\r
ee4f03ae 1016 if (cnt == 0) return;\r
cc68a136 1017\r
5f0d224e 1018 p = &sprited[4];\r
1019 if ((sprited[1] & (SPRL_TILE_OVFL|SPRL_HAVE_MASK0)) == (SPRL_TILE_OVFL|SPRL_HAVE_MASK0))\r
1020 return; // masking effective due to tile overflow\r
cc68a136 1021\r
ee4f03ae 1022 // Go through sprites backwards:\r
5f0d224e 1023 w = p[cnt]; // possibly clipped width of last sprite\r
1024 for (cnt--; cnt >= 0; cnt--, w = 0)\r
07abbab1 1025 {\r
b010d7b4 1026 s32 *sprite, code;\r
15eed405 1027 int pal, tile, sx, sy;\r
ee4f03ae 1028 int offs, delta, width, height, row;\r
1029\r
1030 offs = (p[cnt] & 0x7f) * 2;\r
99bdfd31 1031 sprite = est->HighPreSpr + offs;\r
ee4f03ae 1032 code = sprite[1];\r
1033 pal = (code>>9)&0x30;\r
cc68a136 1034\r
d9e12ee7 1035 fTileFunc = tilefuncs[pal == 0x30][!!(code & 0x8000)][!!(code & 0x800)];\r
1036 if (fTileFunc == NULL) continue; // non-operator low sprite, already drawn\r
cc68a136 1037\r
ee4f03ae 1038 // parse remaining sprite data\r
1039 sy=sprite[0];\r
1040 sx=code>>16; // X\r
1041 width=sy>>28;\r
1042 height=(sy>>24)&7; // Width and height in tiles\r
ae9a76c9 1043 sy=(s16)sy; // Y\r
ee4f03ae 1044\r
ea38612f 1045 row=est->DrawScanline-sy; // Row of the sprite we are on\r
ee4f03ae 1046\r
1047 if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
1048\r
1049 tile=code + (row>>3); // Tile number increases going down\r
1050 delta=height; // Delta to increase tile by going right\r
1051 if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
1052\r
1053 tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
1054 delta<<=4; // Delta of address\r
1055\r
5f0d224e 1056 if (w) width = w; // tile limit\r
cc68a136 1057 for (; width; width--,sx+=8,tile+=delta)\r
1058 {\r
1a08dec0 1059 unsigned int pack;\r
1060\r
cc68a136 1061 if(sx<=0) continue;\r
1062 if(sx>=328) break; // Offscreen\r
1063\r
57c5a5e5 1064 pack = CPU_LE2(*(u32 *)(PicoMem.vram + (tile & 0x7fff)));\r
ee3c39ef 1065 fTileFunc(pd + sx, pack, pal);\r
cc68a136 1066 }\r
1067 }\r
1068}\r
f8af9634 1069#endif // !_ASM_DRAW_C\r
cc68a136 1070\r
fbc65db7 1071static void DrawSpritesHiAS(unsigned char *sprited, int sh)\r
e5fa9817 1072{\r
15eed405 1073 static unsigned (*tilefuncs[2][2][2])(unsigned, unsigned char *, unsigned, unsigned char) = {\r
d9e12ee7 1074 { {TileNormAS_onlymark, TileFlipAS_onlymark}, {TileNormAS, TileFlipAS} },\r
1075 { {TileNormSH_AS_onlyop_lp, TileFlipSH_AS_onlyop_lp}, {TileNormSH_AS, TileFlipSH_AS} }\r
1076 }; // [sh?][hi?][flip?]\r
15eed405 1077 unsigned (*fTileFunc)(unsigned m, unsigned char *pd, unsigned int pack, unsigned char pal);\r
ee3c39ef 1078 unsigned char *pd = Pico.est.HighCol;\r
6bfa97ff 1079 unsigned char mb[sizeof(DefHighCol)/8];\r
d9e12ee7 1080 unsigned char *p, *mp;\r
1081 unsigned m;\r
ee3c39ef 1082 int entry, cnt;\r
ee4f03ae 1083\r
ae9a76c9 1084 cnt = sprited[0] & 0x7f;\r
ee4f03ae 1085 if (cnt == 0) return;\r
e5fa9817 1086\r
ee3c39ef 1087 memset(mb, 0xff, sizeof(mb));\r
5f0d224e 1088 p = &sprited[4];\r
1089 if ((sprited[1] & (SPRL_TILE_OVFL|SPRL_HAVE_MASK0)) == (SPRL_TILE_OVFL|SPRL_HAVE_MASK0))\r
1090 return; // masking effective due to tile overflow\r
e5fa9817 1091\r
ee4f03ae 1092 // Go through sprites:\r
1093 for (entry = 0; entry < cnt; entry++)\r
07abbab1 1094 {\r
b010d7b4 1095 s32 *sprite, code;\r
15eed405 1096 int pal, tile, sx, sy;\r
ee4f03ae 1097 int offs, delta, width, height, row;\r
1098\r
1099 offs = (p[entry] & 0x7f) * 2;\r
ae9a76c9 1100 sprite = Pico.est.HighPreSpr + offs;\r
ee4f03ae 1101 code = sprite[1];\r
1102 pal = (code>>9)&0x30;\r
1103\r
d9e12ee7 1104 fTileFunc = tilefuncs[(sh && pal == 0x30)][!!(code&0x8000)][!!(code&0x800)];\r
ee4f03ae 1105\r
1106 // parse remaining sprite data\r
1107 sy=sprite[0];\r
1108 sx=code>>16; // X\r
1109 width=sy>>28;\r
1110 height=(sy>>24)&7; // Width and height in tiles\r
ae9a76c9 1111 sy=(s16)sy; // Y\r
ee4f03ae 1112\r
ea38612f 1113 row=Pico.est.DrawScanline-sy; // Row of the sprite we are on\r
ee4f03ae 1114\r
1115 if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
1116\r
1117 tile=code + (row>>3); // Tile number increases going down\r
1118 delta=height; // Delta to increase tile by going right\r
1119 if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
1120\r
1121 tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
1122 delta<<=4; // Delta of address\r
e5fa9817 1123\r
5f0d224e 1124 if (entry+1 == cnt) width = p[entry+1]; // last sprite width limited?\r
6badfabe 1125 while (sx <= 0 && width) width--, sx+=8, tile+=delta; // Offscreen\r
ea431e9e 1126 mp = mb+(sx>>3);\r
6badfabe 1127 for (m = *mp; width; width--, sx+=8, tile+=delta, *mp++ = m, m >>= 8)\r
07abbab1 1128 {\r
1a08dec0 1129 unsigned int pack;\r
1130\r
07abbab1 1131 if(sx>=328) break; // Offscreen\r
1132\r
57c5a5e5 1133 pack = CPU_LE2(*(u32 *)(PicoMem.vram + (tile & 0x7fff)));\r
d9e12ee7 1134\r
26643d27 1135 m |= mp[1] << 8; // next mask byte\r
d9e12ee7 1136 // shift mask bits to bits 8-15 for easier load/store handling\r
6bfa97ff 1137 m = fTileFunc(m << (8-(sx&0x7)), pd + sx, pack, pal) >> (8-(sx&0x7));\r
d9e12ee7 1138 } \r
26643d27 1139 *mp = m; // write last mask byte\r
07abbab1 1140 }\r
e5fa9817 1141}\r
1142\r
6dd553c7 1143#ifdef FORCE\r
71a2e205 1144// NB lots of duplicate code, all for the sake of a small performance gain.\r
1145\r
1146static void DrawStripForced(struct TileStrip *ts, int cellskip)\r
6dd553c7 1147{\r
1148 unsigned char *pd = Pico.est.HighCol;\r
4cc0fcaf 1149 int tilex, dx, ty, addr=0, cells;\r
1150 u32 code = 0, oldcode = -1;\r
71a2e205 1151 int pal = 0;\r
6dd553c7 1152\r
1153 // Draw tiles across screen:\r
6dd553c7 1154 tilex=((-ts->hscroll)>>3)+cellskip;\r
1155 ty=(ts->line&7)<<1; // Y-Offset into tile\r
1156 dx=((ts->hscroll-1)&7)+1;\r
1157 cells = ts->cells - cellskip;\r
1158 if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
1159 dx+=cellskip<<3;\r
1160\r
1161 for (; cells > 0; dx+=8, tilex++, cells--)\r
1162 {\r
4cc0fcaf 1163 u32 pack;\r
6dd553c7 1164\r
1165 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
1166\r
1167 if (code!=oldcode) {\r
1168 oldcode = code;\r
1169 // Get tile address/2:\r
71a2e205 1170 addr = ((code&0x7ff)<<4) + ty;\r
1171 if (code & 0x1000) addr^=0xe; // Y-flip\r
6dd553c7 1172\r
71a2e205 1173 pal = (code>>9)&0x30;\r
6dd553c7 1174 }\r
1175\r
57c5a5e5 1176 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr));\r
6dd553c7 1177\r
1178 if (code & 0x0800) TileFlip_and(pd + dx, pack, pal);\r
1179 else TileNorm_and(pd + dx, pack, pal);\r
1180 }\r
1181}\r
1182\r
6dd553c7 1183static void DrawStripVSRamForced(struct TileStrip *ts, int plane_sh, int cellskip)\r
1184{\r
1185 unsigned char *pd = Pico.est.HighCol;\r
4cc0fcaf 1186 int tilex, dx, ty=0, addr=0, cell=0, nametabadd=0;\r
1187 u32 code=0, oldcode=-1;\r
7165b73c 1188 int pal=0, scan=Pico.est.DrawScanline, plane;\r
6dd553c7 1189\r
1190 // Draw tiles across screen:\r
7165b73c 1191 plane = plane_sh & LF_PLANE;\r
6dd553c7 1192 tilex=(-ts->hscroll)>>3;\r
1193 dx=((ts->hscroll-1)&7)+1;\r
1194 if (ts->hscroll & 0x0f) {\r
1195 int adj = ((ts->hscroll ^ dx) >> 3) & 1;\r
1196 cell -= adj + 1;\r
1197 ts->cells -= adj;\r
1198 PicoMem.vsram[0x3e] = PicoMem.vsram[0x3f] = plane_sh >> 16;\r
1199 }\r
1200 cell+=cellskip;\r
1201 tilex+=cellskip;\r
1202 dx+=cellskip<<3;\r
1203\r
33cb1ab0 1204 if ((cell&1)==1)\r
1205 {\r
1206 int line,vscroll;\r
1207 vscroll = PicoMem.vsram[plane + (cell&0x3e)];\r
1208\r
1209 // Find the line in the name table\r
1210 line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
1211 nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width]\r
1212 ty=(line&7)<<1; // Y-Offset into tile\r
1213 }\r
6dd553c7 1214 for (; cell < ts->cells; dx+=8,tilex++,cell++)\r
1215 {\r
6dd553c7 1216 unsigned int pack;\r
1217\r
33cb1ab0 1218 if ((cell&1)==0)\r
6dd553c7 1219 {\r
1220 int line,vscroll;\r
7165b73c 1221 vscroll = PicoMem.vsram[plane + (cell&0x3e)];\r
6dd553c7 1222\r
1223 // Find the line in the name table\r
1224 line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
1225 nametabadd=(line>>3)<<(ts->line>>24); // .. and shift[width]\r
1226 ty=(line&7)<<1; // Y-Offset into tile\r
1227 }\r
1228\r
1229 code=PicoMem.vram[ts->nametab+nametabadd+(tilex&ts->xmask)];\r
1230\r
1231 if (code!=oldcode) {\r
1232 oldcode = code;\r
1233 // Get tile address/2:\r
1234 addr=(code&0x7ff)<<4;\r
1235\r
71a2e205 1236 pal = (code>>9)&0x30; // shadow\r
6dd553c7 1237 }\r
1238\r
71a2e205 1239 pack = code & 0x1000 ? ty^0xe : ty; // Y-flip\r
57c5a5e5 1240 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr+pack));\r
71a2e205 1241\r
1242 if (code & 0x0800) TileFlip_and(pd + dx, pack, pal);\r
1243 else TileNorm_and(pd + dx, pack, pal);\r
1244 }\r
1245}\r
1246\r
1247void DrawStripInterlaceForced(struct TileStrip *ts)\r
1248{\r
1249 unsigned char *pd = Pico.est.HighCol;\r
1250 int tilex = 0, dx = 0, ty = 0, cells;\r
1251 int oldcode = -1;\r
1252 unsigned int pal = 0, pack = 0;\r
1253\r
1254 // Draw tiles across screen:\r
1255 tilex=(-ts->hscroll)>>3;\r
1256 ty=(ts->line&15)<<1; // Y-Offset into tile\r
1257 dx=((ts->hscroll-1)&7)+1;\r
1258 cells = ts->cells;\r
1259 if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
1260\r
1261 for (; cells; dx+=8,tilex++,cells--)\r
1262 {\r
1263 u32 code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
1264\r
1265 if (code!=oldcode) {\r
1266 oldcode = code;\r
1267\r
1268 // Get tile address/2:\r
1269 u32 addr = ((code&0x3ff)<<5) + ty;\r
1270 if (code & 0x1000) addr ^= 0x1e; // Y-flip\r
1271\r
1272 pal = (code>>9)&0x30; // shadow\r
1273\r
57c5a5e5 1274 pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr));\r
71a2e205 1275 }\r
6dd553c7 1276\r
1277 if (code & 0x0800) TileFlip_and(pd + dx, pack, pal);\r
1278 else TileNorm_and(pd + dx, pack, pal);\r
1279 }\r
1280}\r
1281\r
71a2e205 1282// XXX only duplicated to avoid ARM asm hassles\r
6dd553c7 1283static void DrawLayerForced(int plane_sh, int cellskip, int maxcells,\r
1284 struct PicoEState *est)\r
1285{\r
17c1401b 1286 struct PicoVideo *pvid=&est->Pico->video;\r
6dd553c7 1287 const char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps (2 is invalid)\r
1288 struct TileStrip ts;\r
1289 int width, height, ymask;\r
1290 int vscroll, htab;\r
1291\r
1292 ts.cells=maxcells;\r
1293\r
1294 // Work out the TileStrip to draw\r
1295\r
1296 // Work out the name table size: 32 64 or 128 tiles (0-3)\r
1297 width=pvid->reg[16];\r
1298 height=(width>>4)&3; width&=3;\r
1299\r
1300 ts.xmask=(1<<shift[width])-1; // X Mask in tiles (0x1f-0x7f)\r
1301 ymask=(height<<8)|0xff; // Y Mask in pixels\r
1302 switch (width) {\r
1303 case 1: ymask &= 0x1ff; break;\r
1304 case 2: ymask = 0x007; break;\r
1305 case 3: ymask = 0x0ff; break;\r
1306 }\r
1307\r
1308 // Find name table:\r
1309 if (plane_sh&1) ts.nametab=(pvid->reg[4]&0x07)<<12; // B\r
1310 else ts.nametab=(pvid->reg[2]&0x38)<< 9; // A\r
1311\r
1312 htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
1313 switch (pvid->reg[11]&3) {\r
1314 case 1: htab += (est->DrawScanline<<1) & 0x0f; break;\r
1315 case 2: htab += (est->DrawScanline<<1) & ~0x0f; break; // Offset by tile\r
1316 case 3: htab += (est->DrawScanline<<1); break; // Offset by line\r
1317 }\r
1318 htab+=plane_sh&1; // A or B\r
1319\r
1320 // Get horizontal scroll value, will be masked later\r
1321 ts.hscroll = PicoMem.vram[htab & 0x7fff];\r
1322\r
1323 if((pvid->reg[12]&6) == 6) {\r
1324 // interlace mode 2\r
1325 vscroll = PicoMem.vsram[plane_sh & 1]; // Get vertical scroll value\r
1326\r
1327 // Find the line in the name table\r
1328 ts.line=(vscroll+(est->DrawScanline<<1))&((ymask<<1)|1);\r
1329 ts.nametab+=(ts.line>>4)<<shift[width];\r
1330\r
71a2e205 1331 DrawStripInterlaceForced(&ts);\r
6dd553c7 1332 } else if( pvid->reg[11]&4) {\r
1333 // shit, we have 2-cell column based vscroll\r
1334 // luckily this doesn't happen too often\r
1335 ts.line=ymask|(shift[width]<<24); // save some stuff instead of line\r
61114cd8 1336 // vscroll value for leftmost cells in case of hscroll not on 16px boundary\r
1337 // XXX it's unclear what exactly the hw is doing. Continue reading where it\r
1338 // stopped last seems to work best (H40: 0x50 (wrap->0x00), H32 0x40).\r
1339 plane_sh |= PicoMem.vsram[(pvid->reg[12]&1?0x00:0x20) + (plane_sh&1)] << 16;\r
6dd553c7 1340 DrawStripVSRamForced(&ts, plane_sh, cellskip);\r
1341 } else {\r
1342 vscroll = PicoMem.vsram[plane_sh & 1]; // Get vertical scroll value\r
1343\r
1344 // Find the line in the name table\r
1345 ts.line=(vscroll+est->DrawScanline)&ymask;\r
1346 ts.nametab+=(ts.line>>3)<<shift[width];\r
1347\r
71a2e205 1348 DrawStripForced(&ts, cellskip);\r
6dd553c7 1349 }\r
1350}\r
1351\r
2d5b6a66 1352static void DrawSpritesForced(unsigned char *sprited)\r
1353{\r
15eed405 1354 unsigned (*fTileFunc)(unsigned m, unsigned char *pd, unsigned int pack, unsigned char pal);\r
2d5b6a66 1355 unsigned char *pd = Pico.est.HighCol;\r
6bfa97ff 1356 unsigned char mb[sizeof(DefHighCol)/8];\r
6dd553c7 1357 unsigned char *p, *mp;\r
1358 unsigned m;\r
2d5b6a66 1359 int entry, cnt;\r
1360\r
ae9a76c9 1361 cnt = sprited[0] & 0x7f;\r
6dd553c7 1362 if (cnt == 0) { memset(pd, 0, sizeof(DefHighCol)); return; }\r
ae9a76c9 1363\r
6dd553c7 1364 memset(mb, 0xff, sizeof(mb));\r
2d5b6a66 1365 p = &sprited[4];\r
1366 if ((sprited[1] & (SPRL_TILE_OVFL|SPRL_HAVE_MASK0)) == (SPRL_TILE_OVFL|SPRL_HAVE_MASK0))\r
1367 return; // masking effective due to tile overflow\r
1368\r
1369 // Go through sprites:\r
1370 for (entry = 0; entry < cnt; entry++)\r
1371 {\r
b010d7b4 1372 s32 *sprite, code;\r
15eed405 1373 int pal, tile, sx, sy;\r
2d5b6a66 1374 int offs, delta, width, height, row;\r
1375\r
1376 offs = (p[entry] & 0x7f) * 2;\r
ae9a76c9 1377 sprite = Pico.est.HighPreSpr + offs;\r
2d5b6a66 1378 code = sprite[1];\r
1379 pal = (code>>9)&0x30;\r
1380\r
6dd553c7 1381 if (code&0x800) fTileFunc = TileFlipSH_AS_and;\r
1382 else fTileFunc = TileNormSH_AS_and;\r
2d5b6a66 1383\r
1384 // parse remaining sprite data\r
1385 sy=sprite[0];\r
1386 sx=code>>16; // X\r
1387 width=sy>>28;\r
1388 height=(sy>>24)&7; // Width and height in tiles\r
ae9a76c9 1389 sy=(s16)sy; // Y\r
2d5b6a66 1390\r
1391 row=Pico.est.DrawScanline-sy; // Row of the sprite we are on\r
1392\r
1393 if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
1394\r
1395 tile=code + (row>>3); // Tile number increases going down\r
1396 delta=height; // Delta to increase tile by going right\r
1397 if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
1398\r
1399 tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
1400 delta<<=4; // Delta of address\r
1401\r
1402 if (entry+1 == cnt) width = p[entry+1]; // last sprite width limited?\r
6badfabe 1403 while (sx <= 0 && width) width--, sx+=8, tile+=delta; // Offscreen\r
6dd553c7 1404 mp = mb+(sx>>3);\r
6badfabe 1405 for (m = *mp; width; width--, sx+=8, tile+=delta, *mp++ = m, m >>= 8)\r
2d5b6a66 1406 {\r
57c5a5e5 1407 u32 pack;\r
2d5b6a66 1408\r
2d5b6a66 1409 if(sx>=328) break; // Offscreen\r
1410\r
57c5a5e5 1411 pack = CPU_LE2(*(u32 *)(PicoMem.vram + (tile & 0x7fff)));\r
6dd553c7 1412\r
1413 m |= mp[1] << 8; // next mask byte\r
1414 // shift mask bits to bits 8-15 for easier load/store handling\r
6bfa97ff 1415 m = fTileFunc(m << (8-(sx&0x7)), pd + sx, pack, pal) >> (8-(sx&0x7));\r
6dd553c7 1416 } \r
1417 *mp = m; // write last mask byte\r
2d5b6a66 1418 }\r
6dd553c7 1419\r
d515a352 1420 // anything not covered by a sprite is off \r
1421 // XXX Titan hw notes say that transparent pixels remove shadow. Is this also\r
1422 // the case in areas where no sprites are displayed?\r
6dd553c7 1423 for (cnt = 1; cnt < sizeof(mb)-1; cnt++)\r
71a2e205 1424 if (mb[cnt] == 0xff) {\r
1425 *(u32 *)(pd+8*cnt+0) = 0;\r
1426 *(u32 *)(pd+8*cnt+4) = 0;\r
1427 } else if (mb[cnt])\r
6dd553c7 1428 for (m = 0; m < 8; m++)\r
1429 if (mb[cnt] & (1<<m))\r
1430 pd[8*cnt+m] = 0;\r
2d5b6a66 1431}\r
6dd553c7 1432#endif\r
2d5b6a66 1433\r
cc68a136 1434\r
ae9a76c9 1435// sprite info in SAT:\r
cc68a136 1436// Index + 0 : ----hhvv -lllllll -------y yyyyyyyy\r
1437// Index + 4 : -------x xxxxxxxx pccvhnnn nnnnnnnn\r
ae9a76c9 1438// sprite info in HighPreSpr:\r
1439// Index + 0 : hhhhvvvv -lllllll yyyyyyyy yyyyyyyy // v/h size, link, y\r
1440// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x+8, prio, palette, flip, tile\r
cc68a136 1441\r
ae9a76c9 1442// Sprite parsing 1 line in advance: determine sprites on line by Y pos\r
d515a352 1443static NOINLINE void ParseSprites(int max_lines, int limit)\r
cc68a136 1444{\r
ea38612f 1445 const struct PicoEState *est=&Pico.est;\r
17c1401b 1446 const struct PicoVideo *pvid=&est->Pico->video;\r
fbc65db7 1447 int u,link=0,sh;\r
cc68a136 1448 int table=0;\r
ae9a76c9 1449 s32 *pd = HighPreSpr + HighPreSprBank*2;\r
25be5c52 1450 int max_sprites = 80, max_width = 328;\r
d9fc2fe1 1451 int max_line_sprites = 20; // 20 sprites, 40 tiles\r
1452\r
ac891449 1453 // SAT scanning is one line ahead, but don't overshoot. Technically, SAT\r
1454 // parsing for line 0 is on the last line of the previous frame.\r
0c9c8e47 1455 int first_line = est->DrawScanline + !!est->DrawScanline;\r
ac891449 1456 if (max_lines > rendlines-1)\r
1457 max_lines = rendlines-1;\r
1458\r
d515a352 1459 // look-ahead SAT parsing for next line and sprite pixel fetching for current\r
1460 // line are limited if display was disabled during HBLANK before current line\r
1461 if (limit) limit = 16; // max sprites/pixels processed\r
1462\r
17c1401b 1463 if (!(pvid->reg[12]&1))\r
947fb5f9 1464 max_sprites = 64, max_line_sprites = 16, max_width = 264;\r
d5d17782 1465 if (*est->PicoOpt & POPT_DIS_SPRITE_LIM)\r
d9fc2fe1 1466 max_line_sprites = MAX_LINE_SPRITES;\r
1467\r
17c1401b 1468 sh = pvid->reg[0xC]&8; // shadow/hilight?\r
cc68a136 1469\r
1470 table=pvid->reg[5]&0x7f;\r
1471 if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
1472 table<<=8; // Get sprite table address/2\r
1473\r
ac891449 1474 for (u = first_line; u <= max_lines; u++)\r
25be5c52 1475 *((int *)&HighLnSpr[u][0]) = 0;\r
cc68a136 1476\r
25be5c52 1477 for (u = 0; u < max_sprites && link < max_sprites; u++)\r
1478 {\r
4cc0fcaf 1479 u32 *sprite;\r
25be5c52 1480 int code, code2, sx, sy, hv, height, width;\r
cc68a136 1481\r
15eed405 1482 sprite=(u32 *)(PicoMem.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
cc68a136 1483\r
25be5c52 1484 // parse sprite info. the 1st half comes from the VDPs internal cache,\r
1485 // the 2nd half is read from VRAM\r
ac891449 1486 code = CPU_LE2(VdpSATCache[2*link]); // normally same as sprite[0]\r
25be5c52 1487 sy = (code&0x1ff)-0x80;\r
1488 hv = (code>>24)&0xf;\r
1489 height = (hv&3)+1;\r
1490 width = (hv>>2)+1;\r
cc68a136 1491\r
57c5a5e5 1492 code2 = CPU_LE2(sprite[1]);\r
25be5c52 1493 sx = (code2>>16)&0x1ff;\r
1494 sx -= 0x78; // Get X coordinate + 8\r
d9fc2fe1 1495\r
ac891449 1496 if (sy <= max_lines && sy + (height<<3) >= first_line) // sprite onscreen (y)?\r
cc68a136 1497 {\r
25be5c52 1498 int entry, y, w, sx_min, onscr_x, maybe_op = 0;\r
d515a352 1499 // omit look-ahead line if sprite parsing limit reached\r
1500 int last_line = (limit && u >= 2*limit ? max_lines-1 : max_lines);\r
cc68a136 1501\r
25be5c52 1502 sx_min = 8-(width<<3);\r
1503 onscr_x = sx_min < sx && sx < max_width;\r
1504 if (sh && (code2 & 0x6000) == 0x6000)\r
1505 maybe_op = SPRL_MAY_HAVE_OP;\r
cc68a136 1506\r
ae9a76c9 1507 entry = (((pd - HighPreSpr) / 2) & 0x7f) | ((code2>>8)&0x80);\r
ac891449 1508 y = (sy >= first_line) ? sy : first_line;\r
d515a352 1509 for (; y < sy + (height<<3) && y <= last_line; y++)\r
d9fc2fe1 1510 {\r
25be5c52 1511 unsigned char *p = &HighLnSpr[y][0];\r
ae9a76c9 1512 int cnt = p[0] & 0x7f;\r
8e4ab3c6 1513 if (p[1] & SPRL_MASKED) continue; // masked?\r
25be5c52 1514\r
d515a352 1515 if (p[3] >= max_line_sprites) continue; // sprite limit?\r
1516 p[3] ++;\r
1517\r
25be5c52 1518 w = width;\r
1519 if (p[2] + width > max_line_sprites*2) { // tile limit?\r
1520 if (y+1 < 240) HighLnSpr[y+1][1] |= SPRL_TILE_OVFL;\r
1521 if (p[2] >= max_line_sprites*2) continue;\r
1522 w = max_line_sprites*2 - p[2];\r
d9fc2fe1 1523 }\r
25be5c52 1524 p[2] += w;\r
25be5c52 1525\r
1526 if (sx == -0x78) {\r
1527 if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL))\r
8e4ab3c6 1528 p[1] |= SPRL_MASKED; // masked, no more sprites for this line\r
25be5c52 1529 if (!(p[1] & SPRL_HAVE_X) && cnt == 0)\r
1530 p[1] |= SPRL_HAVE_MASK0; // 1st sprite is masking\r
1531 } else\r
1532 p[1] |= SPRL_HAVE_X;\r
1533\r
1534 if (!onscr_x) continue; // offscreen x\r
1535\r
d515a352 1536 // sprite is (partly) visible, store info for renderer\r
25be5c52 1537 p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO;\r
1538 p[1] |= maybe_op; // there might be op sprites on this line\r
1539 if (cnt > 0 && (code2 & 0x8000) && !(p[4+cnt-1]&0x80))\r
1540 p[1] |= SPRL_LO_ABOVE_HI;\r
d515a352 1541\r
1542 p[4+cnt] = entry;\r
1543 p[5+cnt] = w; // width clipped by tile limit for sprite renderer\r
ae9a76c9 1544 p[0] = (cnt + 1) | HighPreSprBank;\r
cc68a136 1545 }\r
25be5c52 1546 }\r
6d7acf9e 1547\r
ae9a76c9 1548 *pd++ = (width<<28)|(height<<24)|(link<<16)|((u16)sy);\r
1549 *pd++ = (sx<<16)|((u16)code2);\r
6d7acf9e 1550\r
25be5c52 1551 // Find next sprite\r
1552 link=(code>>16)&0x7f;\r
1553 if (!link) break; // End of sprites\r
1554 }\r
1555 *pd = 0;\r
d9fc2fe1 1556\r
d515a352 1557 // fetching sprite pixels isn't done while display is disabled during HBLANK\r
1558 if (limit) {\r
1559 int w = 0;\r
1560 unsigned char *sprited = &HighLnSpr[max_lines-1][0]; // current render line\r
1561\r
ae9a76c9 1562 for (u = 0; u < (sprited[0] & 0x7f); u++) {\r
1563 s32 *sp = HighPreSpr + (sprited[4+u] & 0x7f) * 2 + HighPreSprBank*2;\r
d515a352 1564 int sw = sp[0] >> 28;\r
1565 if (w + sw > limit) {\r
ae9a76c9 1566 sprited[0] = u | HighPreSprBank;\r
d515a352 1567 sprited[4+u] = limit-w;\r
1568 break;\r
1569 }\r
1570 w += sw;\r
1571 }\r
1572 }\r
1573\r
d9fc2fe1 1574#if 0\r
ac891449 1575 for (u = first_line; u <= max_lines; u++)\r
25be5c52 1576 {\r
1577 int y;\r
ae9a76c9 1578 printf("c%03i b%d: f %x c %2i/%2i w %2i: ", u, !!HighPreSprBank, HighLnSpr[u][1],\r
1579 HighLnSpr[u][0] & 0x7f, HighLnSpr[u][3], HighLnSpr[u][2]);\r
1580 for (y = 0; y < (HighLnSpr[u][0] & 0x7f); y++) {\r
1581 s32 *sp = HighPreSpr + (HighLnSpr[u][y+4]&0x7f) * 2 + HighPreSprBank*2;\r
47548249 1582 printf(" %i(%x/%x)", HighLnSpr[u][y+4],sp[0],sp[1]);\r
1583 }\r
25be5c52 1584 printf("\n");\r
cc68a136 1585 }\r
25be5c52 1586#endif\r
ae9a76c9 1587\r
1588 HighPreSprBank ^= 0x80;\r
cc68a136 1589}\r
1590\r
283fec1b 1591#ifndef _ASM_DRAW_C\r
ea38612f 1592static void DrawAllSprites(unsigned char *sprited, int prio, int sh,\r
1593 struct PicoEState *est)\r
cc68a136 1594{\r
d9fc2fe1 1595 unsigned char *p;\r
672b29e6 1596 int cnt, w;\r
cc68a136 1597\r
ae9a76c9 1598 cnt = sprited[0] & 0x7f;\r
ee4f03ae 1599 if (cnt == 0) return;\r
cc68a136 1600\r
5f0d224e 1601 p = &sprited[4];\r
1602 if ((sprited[1] & (SPRL_TILE_OVFL|SPRL_HAVE_MASK0)) == (SPRL_TILE_OVFL|SPRL_HAVE_MASK0))\r
1603 return; // masking effective due to tile overflow\r
cc68a136 1604\r
ee4f03ae 1605 // Go through sprites backwards:\r
5f0d224e 1606 w = p[cnt]; // possibly clipped width of last sprite\r
1607 for (cnt--; cnt >= 0; cnt--, w = 0)\r
ee4f03ae 1608 {\r
ae9a76c9 1609 s32 *sp = est->HighPreSpr + (p[cnt]&0x7f) * 2;\r
97a7f774 1610 if ((p[cnt] >> 7) != prio) continue;\r
5f0d224e 1611 DrawSprite(sp, sh, w);\r
cc68a136 1612 }\r
cc68a136 1613}\r
1614\r
1615\r
1616// --------------------------------------------\r
1617\r
0c9c8e47 1618void BackFill(int bgc, int sh, struct PicoEState *est)\r
cc68a136 1619{\r
0c9c8e47 1620 u32 back = bgc;\r
cc68a136 1621\r
1622 // Start with a blank scanline (background colour):\r
6bfa97ff 1623 back|=sh<<7; // shadow\r
cc68a136 1624 back|=back<<8;\r
1625 back|=back<<16;\r
1626\r
99bdfd31 1627 memset32((int *)(est->HighCol+8), back, 320/4);\r
cc68a136 1628}\r
1629#endif\r
1630\r
1631// --------------------------------------------\r
1632\r
724db457 1633static u16 *BgcDMAbase;\r
1634static u32 BgcDMAsrc, BgcDMAmask;\r
1635static int BgcDMAlen, BgcDMAoffs;\r
1636\r
1637#ifndef _ASM_DRAW_C\r
1638static\r
1639#endif\r
1640// handle DMA to background color\r
db1ee7a2 1641void BgcDMA(struct PicoEState *est)\r
724db457 1642{\r
db1ee7a2 1643 u16 *pd=est->DrawLineDest;\r
1644 int len = (est->Pico->video.reg[12]&1) ? 320 : 256;\r
1645 // TODO for now handles the line as all background.\r
724db457 1646 int xl = (len == 320 ? 38 : 33); // DMA slots during HSYNC\r
1647 int upscale = (est->rendstatus & PDRAW_SOFTSCALE) && len < 320;\r
db1ee7a2 1648 u16 *q = upscale ? DefOutBuff : pd;\r
1649 int i, l = len;\r
1650 u16 t;\r
724db457 1651\r
db1ee7a2 1652 if ((est->rendstatus & PDRAW_BORDER_32) && !upscale)\r
1653 q += (320-len) / 2;\r
724db457 1654\r
db1ee7a2 1655 BgcDMAlen -= ((l-BgcDMAoffs)>>1)+xl;\r
1656 if (BgcDMAlen <= 0) {\r
1657 // partial line\r
1658 l += 2*BgcDMAlen;\r
1659 est->rendstatus &= ~PDRAW_BGC_DMA;\r
1660 }\r
724db457 1661\r
db1ee7a2 1662 for (i = BgcDMAoffs; i < l; i += 2) {\r
1663 // TODO use ps to overwrite only real bg pixels\r
1664 t = BgcDMAbase[BgcDMAsrc++ & BgcDMAmask];\r
1665 q[i] = q[i+1] = PXCONV(t);\r
1666 }\r
1667 BgcDMAsrc += xl; // HSYNC DMA\r
1668 BgcDMAoffs = 0;\r
724db457 1669\r
f1dbe764 1670 t = PXCONV(PicoMem.cram[Pico.video.reg[7] & 0x3f]);\r
db1ee7a2 1671 while (i < len) q[i++] = t; // fill partial line with BG\r
724db457 1672\r
db1ee7a2 1673 if (upscale) {\r
1674 switch (PicoIn.filter) {\r
1675 case 3: h_upscale_bl4_4_5(pd, 320, q, 256, len, f_nop); break;\r
1676 case 2: h_upscale_bl2_4_5(pd, 320, q, 256, len, f_nop); break;\r
1677 case 1: h_upscale_snn_4_5(pd, 320, q, 256, len, f_nop); break;\r
1678 default: h_upscale_nn_4_5(pd, 320, q, 256, len, f_nop); break;\r
724db457 1679 }\r
724db457 1680 }\r
724db457 1681}\r
1682\r
1683// --------------------------------------------\r
1684\r
1685static void PicoDoHighPal555_8bit(int sh, int line, struct PicoEState *est)\r
b1a047c9 1686{\r
1687 unsigned int *spal, *dpal;\r
1688 unsigned int cnt = (sh ? 1 : est->SonicPalCount+1);\r
1689 unsigned int t, i;\r
1690\r
1691 // reset dirty only if there are no outstanding changes\r
17c1401b 1692 if (est->Pico->m.dirtyPal == 2)\r
1693 est->Pico->m.dirtyPal = 0;\r
b1a047c9 1694\r
1695 // In Sonic render mode palettes were backuped in SonicPal\r
1696 spal = (void *)est->SonicPal;\r
1697 dpal = (void *)est->HighPal;\r
1698\r
1699 // additional palettes stored after in-frame changes\r
1700 for (i = 0; i < cnt * 0x40 / 2; i++) {\r
1701 t = spal[i];\r
b1a047c9 1702 // treat it like it was 4-bit per channel, since in s/h mode it somewhat is that.\r
1703 // otherwise intensity difference between this and s/h will be wrong\r
16b11d91 1704 t = PXCONV(t);\r
1705 t |= (t >> 4) & PXMASKL;\r
08bbe7f8 1706 dpal[i] = t;\r
b1a047c9 1707 }\r
1708\r
1709 // norm: xxx0, sh: 0xxx, hi: 0xxx + 7\r
1710 if (sh)\r
1711 {\r
1712 // shadowed pixels\r
08bbe7f8 1713 for (i = 0; i < 0x40 / 2; i++) {\r
1714 dpal[0xc0/2 + i] = dpal[i];\r
16b11d91 1715 dpal[0x80/2 + i] = (dpal[i] >> 1) & PXMASKH;\r
08bbe7f8 1716 }\r
b1a047c9 1717 // hilighted pixels\r
1718 for (i = 0; i < 0x40 / 2; i++) {\r
16b11d91 1719 t = ((dpal[i] >> 1) & PXMASKH) + PXMASKH;\r
1720 t |= (t >> 4) & PXMASKL;\r
6bfa97ff 1721 dpal[0x40/2 + i] = t;\r
b1a047c9 1722 }\r
1723 }\r
1724}\r
1725\r
b2305d08 1726#ifndef _ASM_DRAW_C\r
ea38612f 1727void PicoDoHighPal555(int sh, int line, struct PicoEState *est)\r
e55f0cbb 1728{\r
19954be1 1729 unsigned int *spal, *dpal;\r
a39d8ba5 1730 unsigned int t, i;\r
e55f0cbb 1731\r
17c1401b 1732 est->Pico->m.dirtyPal = 0;\r
e55f0cbb 1733\r
88fd63ad 1734 spal = (void *)PicoMem.cram;\r
98a27142 1735 dpal = (void *)est->HighPal;\r
19954be1 1736\r
a39d8ba5 1737 for (i = 0; i < 0x40 / 2; i++) {\r
1738 t = spal[i];\r
a39d8ba5 1739 // treat it like it was 4-bit per channel, since in s/h mode it somewhat is that.\r
1740 // otherwise intensity difference between this and s/h will be wrong\r
16b11d91 1741 t = PXCONV(t);\r
1742 t |= (t >> 4) & PXMASKL;\r
6bfa97ff 1743 dpal[i] = dpal[0xc0/2 + i] = t;\r
e55f0cbb 1744 }\r
1745\r
a39d8ba5 1746 // norm: xxx0, sh: 0xxx, hi: 0xxx + 7\r
e55f0cbb 1747 if (sh)\r
1748 {\r
1749 // shadowed pixels\r
a39d8ba5 1750 for (i = 0; i < 0x40 / 2; i++)\r
16b11d91 1751 dpal[0x80/2 + i] = (dpal[i] >> 1) & PXMASKH;\r
e55f0cbb 1752 // hilighted pixels\r
a39d8ba5 1753 for (i = 0; i < 0x40 / 2; i++) {\r
16b11d91 1754 t = ((dpal[i] >> 1) & PXMASKH) + PXMASKH;\r
1755 t |= (t >> 4) & PXMASKL;\r
6bfa97ff 1756 dpal[0x40/2 + i] = t;\r
e55f0cbb 1757 }\r
1758 }\r
1759}\r
1760\r
99bdfd31 1761void FinalizeLine555(int sh, int line, struct PicoEState *est)\r
cc68a136 1762{\r
99bdfd31 1763 unsigned short *pd=est->DrawLineDest;\r
1764 unsigned char *ps=est->HighCol+8;\r
98a27142 1765 unsigned short *pal=est->HighPal;\r
e55f0cbb 1766 int len;\r
cc68a136 1767\r
aa97a0a0 1768 if (DrawLineDestIncrement == 0)\r
1769 return;\r
1770\r
f1dbe764 1771 if (est->rendstatus & PDRAW_BGC_DMA)\r
1772 return BgcDMA(est);\r
1773\r
b1a047c9 1774 PicoDrawUpdateHighPal();\r
cc68a136 1775\r
96948bdf 1776 len = 256;\r
17c1401b 1777 if (!(PicoIn.AHW & PAHW_8BIT) && (est->Pico->video.reg[12]&1))\r
96948bdf 1778 len = 320;\r
17c1401b 1779 else if ((PicoIn.AHW & PAHW_GG) && (est->Pico->m.hardware & PMS_HW_LCD))\r
0c9c8e47 1780 len = 160;\r
17c1401b 1781 else if ((PicoIn.AHW & PAHW_SMS) && (est->Pico->video.reg[0] & 0x20))\r
96948bdf 1782 len -= 8, ps += 8;\r
466fa079 1783\r
17c1401b 1784 if ((est->rendstatus & PDRAW_SOFTSCALE) && len < 320) {\r
96948bdf 1785 if (len >= 240 && len <= 256) {\r
1786 pd += (256-len)>>1;\r
466fa079 1787 switch (PicoIn.filter) {\r
1788 case 3: h_upscale_bl4_4_5(pd, 320, ps, 256, len, f_pal); break;\r
1789 case 2: h_upscale_bl2_4_5(pd, 320, ps, 256, len, f_pal); break;\r
1790 case 1: h_upscale_snn_4_5(pd, 320, ps, 256, len, f_pal); break;\r
1791 default: h_upscale_nn_4_5(pd, 320, ps, 256, len, f_pal); break;\r
1792 }\r
52e4a905 1793 if (est->rendstatus & PDRAW_32X_SCALE) { // 32X needs scaled CLUT data\r
1794 unsigned char *psc = ps - 256, *pdc = psc;\r
1795 rh_upscale_nn_4_5(pdc, 320, psc, 256, 256, f_nop);\r
1796 }\r
1797 } else if (len == 160)\r
466fa079 1798 switch (PicoIn.filter) {\r
1799 case 3:\r
1800 case 2: h_upscale_bl2_1_2(pd, 320, ps, 160, len, f_pal); break;\r
1801 default: h_upscale_nn_1_2(pd, 320, ps, 160, len, f_pal); break;\r
1802 }\r
d5d17782 1803 } else {\r
17c1401b 1804 if ((est->rendstatus & PDRAW_BORDER_32) && len < 320)\r
466fa079 1805 pd += (320-len) / 2;\r
d5d17782 1806#if 1\r
1807 h_copy(pd, 320, ps, 320, len, f_pal);\r
70357ce5 1808#else\r
70357ce5 1809 extern void amips_clut(unsigned short *dst, unsigned char *src, unsigned short *pal, int count);\r
e5fa9817 1810 extern void amips_clut_6bit(unsigned short *dst, unsigned char *src, unsigned short *pal, int count);\r
ee3c39ef 1811 if (!sh)\r
e5fa9817 1812 amips_clut_6bit(pd, ps, pal, len);\r
1813 else amips_clut(pd, ps, pal, len);\r
70357ce5 1814#endif\r
e5fa9817 1815 }\r
cc68a136 1816}\r
1817#endif\r
1818\r
466fa079 1819void FinalizeLine8bit(int sh, int line, struct PicoEState *est)\r
cc68a136 1820{\r
99bdfd31 1821 unsigned char *pd = est->DrawLineDest;\r
466fa079 1822 unsigned char *ps = est->HighCol+8;\r
b1a047c9 1823 int len;\r
1824 static int dirty_line;\r
cc68a136 1825\r
646be42e 1826 // a hack for mid-frame palette changes\r
17c1401b 1827 if (est->Pico->m.dirtyPal == 1)\r
602133e1 1828 {\r
0c9c8e47 1829 // store a maximum of 3 additional palettes in SonicPal\r
1830 if (est->SonicPalCount < 3 &&\r
1831 (!(est->rendstatus & PDRAW_SONIC_MODE) || (line - dirty_line >= 4))) {\r
646be42e 1832 est->SonicPalCount ++;\r
b1a047c9 1833 dirty_line = line;\r
1834 est->rendstatus |= PDRAW_SONIC_MODE;\r
cc68a136 1835 }\r
b1a047c9 1836 blockcpy(est->SonicPal+est->SonicPalCount*0x40, PicoMem.cram, 0x40*2);\r
17c1401b 1837 est->Pico->m.dirtyPal = 2;\r
cc68a136 1838 }\r
1839\r
96948bdf 1840 len = 256;\r
17c1401b 1841 if (!(PicoIn.AHW & PAHW_8BIT) && (est->Pico->video.reg[12]&1))\r
96948bdf 1842 len = 320;\r
17c1401b 1843 else if ((PicoIn.AHW & PAHW_GG) && (est->Pico->m.hardware & PMS_HW_LCD))\r
0c9c8e47 1844 len = 160;\r
17c1401b 1845 else if ((PicoIn.AHW & PAHW_SMS) && (est->Pico->video.reg[0] & 0x20))\r
96948bdf 1846 len -= 8, ps += 8;\r
466fa079 1847\r
1848 if (DrawLineDestIncrement == 0)\r
1849 pd = est->HighCol+8;\r
cc68a136 1850\r
17c1401b 1851 if ((est->rendstatus & PDRAW_SOFTSCALE) && len < 320) {\r
d5d17782 1852 unsigned char pal = 0;\r
1853\r
1854 if (!sh && (est->rendstatus & PDRAW_SONIC_MODE))\r
1855 pal = est->SonicPalCount*0x40;\r
d5d17782 1856 // Smoothing can't be used with CLUT, hence it's always Nearest Neighbour.\r
96948bdf 1857 if (len >= 240)\r
466fa079 1858 // use reverse version since src and dest ptr may be the same.\r
1859 rh_upscale_nn_4_5(pd, 320, ps, 256, len, f_or);\r
1860 else\r
1861 rh_upscale_nn_1_2(pd, 320, ps, 256, len, f_or);\r
cc68a136 1862 } else {\r
17c1401b 1863 if ((est->rendstatus & PDRAW_BORDER_32) && len < 320)\r
466fa079 1864 pd += (320-len) / 2;\r
1865 if (!sh && (est->rendstatus & PDRAW_SONIC_MODE))\r
d5d17782 1866 // select active backup palette\r
466fa079 1867 blockcpy_or(pd, ps, len, est->SonicPalCount*0x40);\r
1868 else if (pd != ps)\r
1869 blockcpy(pd, ps, len);\r
cc68a136 1870 }\r
1871}\r
1872\r
ea38612f 1873static void (*FinalizeLine)(int sh, int line, struct PicoEState *est);\r
cc68a136 1874\r
b6d7ac70 1875// --------------------------------------------\r
1876\r
0fc0e241 1877static int DrawDisplay(int sh)\r
cc68a136 1878{\r
ea38612f 1879 struct PicoEState *est=&Pico.est;\r
1880 unsigned char *sprited = &HighLnSpr[est->DrawScanline][0];\r
17c1401b 1881 struct PicoVideo *pvid=&est->Pico->video;\r
e0bcb7a9 1882 int win=0, edge=0, hvwind=0, lflags;\r
1883 int maxw, maxcells;\r
cc68a136 1884\r
47677a2a 1885 est->rendstatus &= ~(PDRAW_SHHI_DONE|PDRAW_PLANE_HI_PRIO|PDRAW_WND_DIFF_PRIO);\r
ae9a76c9 1886 est->HighPreSpr = HighPreSpr + (sprited[0]&0x80)*2;\r
740da8c6 1887\r
fbc65db7 1888 if (pvid->reg[12]&1) {\r
cc68a136 1889 maxw = 328; maxcells = 40;\r
1890 } else {\r
1891 maxw = 264; maxcells = 32;\r
1892 }\r
1893\r
1894 // Find out if the window is on this line:\r
1895 win=pvid->reg[0x12];\r
1896 edge=(win&0x1f)<<3;\r
1897\r
ea38612f 1898 if (win&0x80) { if (est->DrawScanline>=edge) hvwind=1; }\r
1899 else { if (est->DrawScanline< edge) hvwind=1; }\r
cc68a136 1900\r
fbc65db7 1901 if (!hvwind) // we might have a vertical window here\r
1902 {\r
cc68a136 1903 win=pvid->reg[0x11];\r
1904 edge=win&0x1f;\r
7a7c6476 1905 if (win&0x80) {\r
1906 if (!edge) hvwind=1;\r
cc68a136 1907 else if(edge < (maxcells>>1)) hvwind=2;\r
1908 } else {\r
7a7c6476 1909 if (!edge);\r
cc68a136 1910 else if(edge < (maxcells>>1)) hvwind=2;\r
1911 else hvwind=1;\r
1912 }\r
1913 }\r
1914\r
fbc65db7 1915 /* - layer B low - */\r
e0bcb7a9 1916 if (!(pvid->debug_p & PVD_KILL_B)) {\r
6bfa97ff 1917 lflags = LF_PLANE_B | (sh<<1);\r
e0bcb7a9 1918 DrawLayer(lflags, HighCacheB, 0, maxcells, est);\r
1919 }\r
fbc65db7 1920 /* - layer A low - */\r
6bfa97ff 1921 lflags = LF_PLANE_A | (sh<<1);\r
e0bcb7a9 1922 if (pvid->debug_p & PVD_KILL_A)\r
1923 ;\r
fbc65db7 1924 else if (hvwind == 1)\r
ea38612f 1925 DrawWindow(0, maxcells>>1, 0, sh, est);\r
7a7c6476 1926 else if (hvwind == 2) {\r
e0bcb7a9 1927 DrawLayer(lflags, HighCacheA, (win&0x80) ? 0 : edge<<1, (win&0x80) ? edge<<1 : maxcells, est);\r
1928 DrawWindow( (win&0x80) ? edge : 0, (win&0x80) ? maxcells>>1 : edge, 0, sh, est);\r
1929 }\r
1930 else\r
1931 DrawLayer(lflags, HighCacheA, 0, maxcells, est);\r
fbc65db7 1932 /* - sprites low - */\r
e0bcb7a9 1933 if (pvid->debug_p & PVD_KILL_S_LO)\r
1934 ;\r
6d8782a1 1935 else if (est->rendstatus & PDRAW_INTERLACE)\r
fbc65db7 1936 DrawAllSpritesInterlace(0, sh);\r
1937 else if (sprited[1] & SPRL_HAVE_LO)\r
ea38612f 1938 DrawAllSprites(sprited, 0, sh, est);\r
fbc65db7 1939\r
1940 /* - layer B hi - */\r
e0bcb7a9 1941 if (!(pvid->debug_p & PVD_KILL_B) && HighCacheB[0])\r
ea38612f 1942 DrawTilesFromCache(HighCacheB, sh, maxw, est);\r
fbc65db7 1943 /* - layer A hi - */\r
e0bcb7a9 1944 if (pvid->debug_p & PVD_KILL_A)\r
1945 ;\r
fbc65db7 1946 else if (hvwind == 1)\r
ea38612f 1947 DrawWindow(0, maxcells>>1, 1, sh, est);\r
7a7c6476 1948 else if (hvwind == 2) {\r
ea38612f 1949 if (HighCacheA[0])\r
1950 DrawTilesFromCache(HighCacheA, sh, (win&0x80) ? edge<<4 : maxw, est);\r
1951 DrawWindow((win&0x80) ? edge : 0, (win&0x80) ? maxcells>>1 : edge, 1, sh, est);\r
cc68a136 1952 } else\r
ea38612f 1953 if (HighCacheA[0])\r
1954 DrawTilesFromCache(HighCacheA, sh, maxw, est);\r
fbc65db7 1955 /* - sprites hi - */\r
e0bcb7a9 1956 if (pvid->debug_p & PVD_KILL_S_HI)\r
1957 ;\r
6d8782a1 1958 else if (est->rendstatus & PDRAW_INTERLACE)\r
fbc65db7 1959 DrawAllSpritesInterlace(1, sh);\r
f8af9634 1960 // have sprites without layer pri bit ontop of sprites with that bit\r
17c1401b 1961 else if ((sprited[1] & SPRL_LO_ABOVE_HI) && (*est->PicoOpt & POPT_ACC_SPRITES))\r
fbc65db7 1962 DrawSpritesHiAS(sprited, sh);\r
1963 else if (sh && (sprited[1] & SPRL_MAY_HAVE_OP))\r
ea38612f 1964 DrawSpritesSHi(sprited, est);\r
fbc65db7 1965 else if (sprited[1] & SPRL_HAVE_HI)\r
ea38612f 1966 DrawAllSprites(sprited, 1, 0, est);\r
cc68a136 1967\r
6dd553c7 1968#ifdef FORCE\r
1969 if (pvid->debug_p & PVD_FORCE_B) {\r
6bfa97ff 1970 lflags = LF_PLANE_B | (sh<<1);\r
6dd553c7 1971 DrawLayerForced(lflags, 0, maxcells, est);\r
1972 } else if (pvid->debug_p & PVD_FORCE_A) {\r
6bfa97ff 1973 lflags = LF_PLANE_A | (sh<<1);\r
6dd553c7 1974 DrawLayerForced(lflags, 0, maxcells, est);\r
1975 } else if (pvid->debug_p & PVD_FORCE_S)\r
2d5b6a66 1976 DrawSpritesForced(sprited);\r
6dd553c7 1977#endif\r
e0bcb7a9 1978\r
740da8c6 1979#if 0\r
1980 {\r
1981 int *c, a, b;\r
47677a2a 1982 for (a = 0, c = HighCacheA; *c; c+=2, a++);\r
1983 for (b = 0, c = HighCacheB; *c; c+=2, b++);\r
ea38612f 1984 printf("%i:%03i: a=%i, b=%i\n", Pico.m.frame_count,\r
0c9c8e47 1985 est->DrawScanline, a, b);\r
740da8c6 1986 }\r
1987#endif\r
1988\r
cc68a136 1989 return 0;\r
1990}\r
1991\r
f8af9634 1992// MUST be called every frame\r
eff55556 1993PICO_INTERNAL void PicoFrameStart(void)\r
cc68a136 1994{\r
0c9c8e47 1995 struct PicoEState *est = &Pico.est;\r
d5d17782 1996 int loffs = 8, lines = 224, coffs = 0, columns = 320;\r
0c9c8e47 1997 int sprep = est->rendstatus & PDRAW_DIRTY_SPRITES;\r
1998 int skipped = est->rendstatus & PDRAW_SKIP_FRAME;\r
f9ed9446 1999 int sync = est->rendstatus & (PDRAW_SYNC_NEEDED | PDRAW_SYNC_NEXT);\r
ae87bffa 2000\r
cc68a136 2001 // prepare to do this frame\r
0c9c8e47 2002 est->rendstatus = 0;\r
17c1401b 2003\r
5864c421 2004 if (PicoIn.AHW & PAHW_32X) // H32 upscaling, before mixing in 32X layer\r
2005 est->rendstatus = (*est->PicoOpt & POPT_ALT_RENDERER) ?\r
2006 PDRAW_BORDER_32 : PDRAW_32X_SCALE|PDRAW_SOFTSCALE;\r
17c1401b 2007 else if (!(PicoIn.opt & POPT_DIS_32C_BORDER))\r
2008 est->rendstatus |= PDRAW_BORDER_32;\r
2009\r
5864c421 2010 if ((PicoIn.opt & POPT_EN_SOFTSCALE) && !(*est->PicoOpt & POPT_ALT_RENDERER))\r
2011 est->rendstatus |= PDRAW_SOFTSCALE;\r
2012\r
17c1401b 2013 if ((est->Pico->video.reg[12] & 6) == 6)\r
0c9c8e47 2014 est->rendstatus |= PDRAW_INTERLACE; // interlace mode\r
17c1401b 2015 if (!(est->Pico->video.reg[12] & 1)) {\r
0c9c8e47 2016 est->rendstatus |= PDRAW_32_COLS;\r
5864c421 2017 if (!(est->rendstatus & PDRAW_SOFTSCALE)) {\r
d5d17782 2018 columns = 256;\r
2019 coffs = 32;\r
2020 }\r
2021 }\r
17c1401b 2022 if (est->Pico->video.reg[1] & 8) {\r
0c9c8e47 2023 est->rendstatus |= PDRAW_30_ROWS;\r
ae87bffa 2024 lines = 240;\r
d5d17782 2025 loffs = 0;\r
5a681086 2026 }\r
17c1401b 2027 if (!(est->rendstatus & PDRAW_BORDER_32))\r
d5d17782 2028 coffs = 0;\r
e5fa9817 2029\r
0c9c8e47 2030 if (est->rendstatus != rendstatus_old || lines != rendlines) {\r
ae87bffa 2031 rendlines = lines;\r
c180662e 2032 // mode_change() might reset rendstatus_old by calling SetOutFormat\r
a5aae2c3 2033 int rendstatus = est->rendstatus;\r
d5d17782 2034 emu_video_mode_change(loffs, lines, coffs, columns);\r
a5aae2c3 2035 rendstatus_old = rendstatus;\r
f9ed9446 2036 // mode_change() might clear buffers, redraw needed\r
2037 est->rendstatus |= PDRAW_SYNC_NEEDED;\r
19954be1 2038 }\r
f9ed9446 2039\r
2040 if (sync | skipped)\r
2041 est->rendstatus |= PDRAW_SYNC_NEEDED;\r
3031ff34 2042 if (PicoIn.skipFrame) // preserve this until something is rendered at last\r
0c9c8e47 2043 est->rendstatus |= PDRAW_SKIP_FRAME;\r
3031ff34 2044 if (sprep | skipped)\r
0c9c8e47 2045 est->rendstatus |= PDRAW_PARSE_SPRITES;\r
19954be1 2046\r
0c9c8e47 2047 est->HighCol = HighColBase + loffs * HighColIncrement;\r
2048 est->DrawLineDest = (char *)DrawLineDestBase + loffs * DrawLineDestIncrement;\r
2049 est->DrawScanline = 0;\r
b7d64dbd 2050 skip_next_line = 0;\r
2051\r
b1a047c9 2052 if (FinalizeLine == FinalizeLine8bit) {\r
2053 // make a backup of the current palette in case Sonic mode is detected later\r
17c1401b 2054 est->Pico->m.dirtyPal = (est->Pico->m.dirtyPal || est->SonicPalCount ? 2 : 0);\r
0c9c8e47 2055 blockcpy(est->SonicPal, PicoMem.cram, 0x40*2);\r
b1a047c9 2056 }\r
0c9c8e47 2057 est->SonicPalCount = 0;\r
cc68a136 2058}\r
2059\r
974fdb5b 2060static void DrawBlankedLine(int line, int offs, int sh, int bgc)\r
87b0845f 2061{\r
0c9c8e47 2062 struct PicoEState *est = &Pico.est;\r
466fa079 2063 int skip = skip_next_line;\r
2064\r
2065 if (PicoScanBegin != NULL && skip == 0)\r
2066 skip = PicoScanBegin(line + offs);\r
2067\r
2068 if (skip) {\r
2069 skip_next_line = skip - 1;\r
2070 return;\r
2071 }\r
87b0845f 2072\r
0c9c8e47 2073 BackFill(bgc, sh, est);\r
87b0845f 2074\r
2075 if (FinalizeLine != NULL)\r
0c9c8e47 2076 FinalizeLine(sh, line, est);\r
87b0845f 2077\r
2078 if (PicoScanEnd != NULL)\r
466fa079 2079 skip_next_line = PicoScanEnd(line + offs);\r
b2670137 2080\r
0c9c8e47 2081 est->HighCol += HighColIncrement;\r
2082 est->DrawLineDest = (char *)est->DrawLineDest + DrawLineDestIncrement;\r
87b0845f 2083}\r
2084\r
97232a47 2085static void PicoLine(int line, int offs, int sh, int bgc, int off, int on)\r
cc68a136 2086{\r
0c9c8e47 2087 struct PicoEState *est = &Pico.est;\r
466fa079 2088 int skip = skip_next_line;\r
cc68a136 2089\r
0c9c8e47 2090 est->DrawScanline = line;\r
466fa079 2091 if (PicoScanBegin != NULL && skip == 0)\r
ae87bffa 2092 skip = PicoScanBegin(line + offs);\r
2093\r
2094 if (skip) {\r
2095 skip_next_line = skip - 1;\r
2096 return;\r
2097 }\r
602133e1 2098\r
17c1401b 2099 if (est->Pico->video.debug_p & (PVD_FORCE_A | PVD_FORCE_B | PVD_FORCE_S))\r
e0bcb7a9 2100 bgc = 0x3f;\r
2101\r
cc68a136 2102 // Draw screen:\r
0c9c8e47 2103 BackFill(bgc, sh, est);\r
17c1401b 2104 if (est->Pico->video.reg[1]&0x40) {\r
2105 int width = (est->Pico->video.reg[12]&1) ? 320 : 256;\r
0fc0e241 2106 DrawDisplay(sh);\r
97232a47 2107 // partial line blanking (display on or off inside the line)\r
2108 if (unlikely(off|on)) {\r
2109 if (off > 0)\r
2110 memset(est->HighCol+8 + off, bgc, width-off);\r
2111 if (on > 0)\r
2112 memset(est->HighCol+8, bgc, on);\r
2113 }\r
2114 }\r
cc68a136 2115\r
ea8c405f 2116 if (FinalizeLine != NULL)\r
0c9c8e47 2117 FinalizeLine(sh, line, est);\r
cc68a136 2118\r
602133e1 2119 if (PicoScanEnd != NULL)\r
87b0845f 2120 skip_next_line = PicoScanEnd(line + offs);\r
5a681086 2121\r
0c9c8e47 2122 est->HighCol += HighColIncrement;\r
2123 est->DrawLineDest = (char *)est->DrawLineDest + DrawLineDestIncrement;\r
cc68a136 2124}\r
2125\r
97232a47 2126void PicoDrawSync(int to, int off, int on)\r
b6d7ac70 2127{\r
b061bc16 2128 struct PicoEState *est = &Pico.est;\r
b38c0ea6 2129 int line, offs;\r
17c1401b 2130 int sh = (est->Pico->video.reg[0xC] & 8) >> 3; // shadow/hilight?\r
2131 int bgc = est->Pico->video.reg[7] & 0x3f;\r
87b0845f 2132\r
f6c49d38 2133 pprof_start(draw);\r
2134\r
b38c0ea6 2135 offs = (240-rendlines) >> 1;\r
2136 if (to >= rendlines)\r
2137 to = rendlines-1;\r
2138\r
0c9c8e47 2139 if (est->DrawScanline <= to &&\r
2140 (est->rendstatus & (PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))\r
97232a47 2141 ParseSprites(to + 1, on);\r
f9ed9446 2142 else if (!(est->rendstatus & PDRAW_SYNC_NEEDED)) {\r
2143 // nothing has changed in VDP/VRAM and buffer is the same -> no sync needed\r
2144 int count = to+1 - est->DrawScanline;\r
2145 est->HighCol += count*HighColIncrement;\r
2146 est->DrawLineDest = (char *)est->DrawLineDest + count*DrawLineDestIncrement;\r
2147 est->DrawScanline = to+1;\r
2148 return;\r
2149 }\r
87b0845f 2150\r
b061bc16 2151 for (line = est->DrawScanline; line < to; line++)\r
97232a47 2152 PicoLine(line, offs, sh, bgc, 0, 0);\r
b6d7ac70 2153\r
b6d7ac70 2154 // last line\r
87b0845f 2155 if (line <= to)\r
b6d7ac70 2156 {\r
17c1401b 2157 int width2 = (est->Pico->video.reg[12]&1) ? 160 : 128;\r
97232a47 2158\r
97232a47 2159 if (unlikely(on|off) && (off >= width2 ||\r
2160 // hack for timing inaccuracy, if on/off near borders\r
2161 (off && off <= 24) || (on < width2 && on >= width2-24)))\r
2162 DrawBlankedLine(line, offs, sh, bgc);\r
2163 else {\r
2164 if (on > width2) on = 0; // on, before start of line?\r
2165 PicoLine(line, offs, sh, bgc, 2*off, 2*on);\r
2166 }\r
87b0845f 2167 line++;\r
b6d7ac70 2168 }\r
b061bc16 2169 est->DrawScanline = line;\r
f6c49d38 2170\r
2171 pprof_end(draw);\r
b6d7ac70 2172}\r
cc68a136 2173\r
0c9c8e47 2174void PicoDrawRefreshSprites(void)\r
ae9a76c9 2175{\r
0c9c8e47 2176 struct PicoEState *est = &Pico.est;\r
2177 unsigned char *sprited = &HighLnSpr[est->DrawScanline][0];\r
ae9a76c9 2178 int i;\r
2179\r
0c9c8e47 2180 if (est->DrawScanline == 0 || est->DrawScanline >= rendlines) return;\r
ae9a76c9 2181\r
2182 // compute sprite row. The VDP does this by subtracting the sprite y pos from\r
2183 // the current line and treating the lower 5 bits as the row number. Y pos\r
2184 // is reread from SAT cache, which may have changed by now (Overdrive 2).\r
2185 for (i = 0; i < (sprited[0] & 0x7f); i++) {\r
2186 int num = sprited[4+i] & 0x7f;\r
2187 s32 *sp = HighPreSpr + 2*num + (sprited[0] & 0x80)*2;\r
2188 int link = (sp[0]>>16) & 0x7f;\r
2189 int sy = (CPU_LE2(VdpSATCache[2*link]) & 0x1ff) - 0x80;\r
2190 if (sy != (s16)sp[0]) {\r
2191 // Y info in SAT cache has really changed\r
0c9c8e47 2192 sy = est->DrawScanline - ((est->DrawScanline - sy) & 0x1f);\r
ae9a76c9 2193 sp[0] = (sp[0] & 0xffff0000) | (u16)sy;\r
2194 }\r
2195 }\r
2196}\r
2197\r
724db457 2198void PicoDrawBgcDMA(u16 *base, u32 source, u32 mask, int dlen, int sl)\r
2199{\r
2200 struct PicoEState *est = &Pico.est;\r
2201 int len = (est->Pico->video.reg[12]&1) ? 320 : 256;\r
2202 int xl = (est->Pico->video.reg[12]&1) ? 38 : 33; // DMA slots during HSYNC\r
2203\r
2204 BgcDMAbase = base;\r
2205 BgcDMAsrc = source;\r
2206 BgcDMAmask = mask;\r
2207 BgcDMAlen = dlen;\r
2208 BgcDMAoffs = 0;\r
2209\r
2210 // handle slot offset in 1st line\r
c1812e1a 2211 if (sl-12 > 0)\r
724db457 2212 BgcDMAoffs = 2*(sl-12);\r
2213 else if (sl < 0) { // DMA starts before active display\r
2214 BgcDMAsrc += 2*-sl;\r
2215 BgcDMAlen -= 2*-sl;\r
2216 }\r
2217\r
2218 // skip 1st line if it had been drawn already\r
2219 if (Pico.est.DrawScanline > Pico.m.scanline) {\r
2220 len -= BgcDMAoffs;\r
2221 BgcDMAsrc += (len>>1)+xl;\r
2222 BgcDMAlen -= (len>>1)+xl;\r
2223 BgcDMAoffs = 0;\r
2224 }\r
db1ee7a2 2225 if (BgcDMAlen > 0)\r
2226 est->rendstatus |= PDRAW_BGC_DMA;\r
724db457 2227}\r
2228\r
5a681086 2229// also works for fast renderer\r
2230void PicoDrawUpdateHighPal(void)\r
2231{\r
98a27142 2232 struct PicoEState *est = &Pico.est;\r
17c1401b 2233 if (est->Pico->m.dirtyPal) {\r
2234 int sh = (est->Pico->video.reg[0xC] & 8) >> 3; // shadow/hilight?\r
2235 if ((*est->PicoOpt & POPT_ALT_RENDERER) | (est->rendstatus & PDRAW_SONIC_MODE))\r
b1a047c9 2236 sh = 0; // no s/h support\r
2237\r
7980d477 2238 if (PicoIn.AHW & PAHW_SMS)\r
0df7401c 2239 PicoDoHighPal555SMS();\r
7980d477 2240 else if (FinalizeLine == FinalizeLine8bit)\r
b1a047c9 2241 PicoDoHighPal555_8bit(sh, 0, est);\r
2242 else\r
2243 PicoDoHighPal555(sh, 0, est);\r
5a681086 2244\r
b1a047c9 2245 // cover for sprite priority bits if not in s/h or sonic mode\r
2246 if (!sh && !(est->rendstatus & PDRAW_SONIC_MODE)) {\r
2247 blockcpy(est->HighPal+0x40, est->HighPal, 0x40*2);\r
2248 blockcpy(est->HighPal+0x80, est->HighPal, 0x80*2);\r
2249 }\r
0c9c8e47 2250 est->HighPal[0xe0] = 0x0000; // black and white, reserved for OSD\r
2251 est->HighPal[0xf0] = 0xffff;\r
5a681086 2252 }\r
2253}\r
2254\r
41946d70 2255void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode)\r
cc68a136 2256{\r
7980d477 2257 PicoDrawSetInternalBuf(NULL, 0);\r
aa97a0a0 2258 PicoDrawSetOutBufMD(NULL, 0);\r
f55fb314 2259 PicoDraw2SetOutBuf(NULL, 0);\r
ea8c405f 2260 switch (which)\r
2261 {\r
5a681086 2262 case PDF_8BIT:\r
2263 FinalizeLine = FinalizeLine8bit;\r
2264 break;\r
2265\r
2266 case PDF_RGB555:\r
93f9619e 2267 if ((PicoIn.AHW & PAHW_32X) && use_32x_line_mode)\r
5a681086 2268 FinalizeLine = FinalizeLine32xRGB555;\r
2269 else\r
2270 FinalizeLine = FinalizeLine555;\r
2271 break;\r
2272\r
2273 default:\r
2274 FinalizeLine = NULL;\r
2275 break;\r
ea8c405f 2276 }\r
b1a047c9 2277 if (PicoIn.AHW & PAHW_32X)\r
2278 PicoDrawSetOutFormat32x(which, use_32x_line_mode);\r
0df7401c 2279 PicoDrawSetOutputSMS(which);\r
205bc456 2280 rendstatus_old = -1;\r
207e5ba0 2281 Pico.m.dirtyPal = 1;\r
5a681086 2282}\r
2283\r
b1a047c9 2284void PicoDrawSetOutBufMD(void *dest, int increment)\r
2285{\r
cdc6aac4 2286 if (FinalizeLine == FinalizeLine8bit && increment >= 328) {\r
aa97a0a0 2287 // kludge for no-copy mode, using ALT_RENDERER layout\r
35247900 2288 PicoDrawSetInternalBuf(dest, increment);\r
aa97a0a0 2289 } else if (FinalizeLine == NULL) {\r
0df7401c 2290 PicoDrawSetInternalBuf(dest, increment); // needed for SMS\r
f55fb314 2291 PicoDraw2SetOutBuf(dest, increment);\r
aa97a0a0 2292 } else if (dest != NULL) {\r
f9ed9446 2293 if (dest != DrawLineDestBase)\r
2294 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;\r
b1a047c9 2295 DrawLineDestBase = dest;\r
2296 DrawLineDestIncrement = increment;\r
61d76999 2297 Pico.est.DrawLineDest = (char *)DrawLineDestBase + Pico.est.DrawScanline * increment;\r
aa97a0a0 2298 } else {\r
b1a047c9 2299 DrawLineDestBase = DefOutBuff;\r
2300 DrawLineDestIncrement = 0;\r
2301 Pico.est.DrawLineDest = DefOutBuff;\r
2302 }\r
2303}\r
2304\r
e51e5983 2305// note: may be called on the middle of frame\r
5a681086 2306void PicoDrawSetOutBuf(void *dest, int increment)\r
2307{\r
b1a047c9 2308 if (PicoIn.AHW & PAHW_32X)\r
2309 PicoDrawSetOutBuf32X(dest, increment);\r
2310 else\r
2311 PicoDrawSetOutBufMD(dest, increment);\r
5a681086 2312}\r
2313\r
2314void PicoDrawSetInternalBuf(void *dest, int increment)\r
2315{\r
2316 if (dest != NULL) {\r
f9ed9446 2317 if (dest != HighColBase)\r
2318 Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;\r
5a681086 2319 HighColBase = dest;\r
2320 HighColIncrement = increment;\r
99bdfd31 2321 Pico.est.HighCol = HighColBase + Pico.est.DrawScanline * increment;\r
5a681086 2322 }\r
2323 else {\r
2324 HighColBase = DefHighCol;\r
2325 HighColIncrement = 0;\r
b1a047c9 2326 Pico.est.HighCol = DefHighCol;\r
5a681086 2327 }\r
cc68a136 2328}\r
ea8c405f 2329\r
f4750ee0 2330void PicoDrawSetCallbacks(int (*begin)(unsigned int num), int (*end)(unsigned int num))\r
2331{\r
2332 PicoScanBegin = NULL;\r
2333 PicoScanEnd = NULL;\r
2334 PicoScan32xBegin = NULL;\r
2335 PicoScan32xEnd = NULL;\r
2336\r
93f9619e 2337 if ((PicoIn.AHW & PAHW_32X) && FinalizeLine != FinalizeLine32xRGB555) {\r
f4750ee0 2338 PicoScan32xBegin = begin;\r
2339 PicoScan32xEnd = end;\r
2340 }\r
2341 else {\r
2342 PicoScanBegin = begin;\r
2343 PicoScanEnd = end;\r
2344 }\r
2345}\r
ea38612f 2346\r
99bdfd31 2347void PicoDrawInit(void)\r
2348{\r
2349 Pico.est.DrawLineDest = DefOutBuff;\r
2350 Pico.est.HighCol = HighColBase;\r
99bdfd31 2351 rendstatus_old = -1;\r
2352}\r
2353\r
2354// vim:ts=2:sw=2:expandtab\r