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