split memories away from Pico
[picodrive.git] / pico / draw.c
1 /*\r
2  * line renderer\r
3  * (c) Copyright Dave, 2004\r
4  * (C) notaz, 2006-2010\r
5  *\r
6  * This work is licensed under the terms of MAME license.\r
7  * See COPYING file in the top-level directory.\r
8  */\r
9 /*\r
10  * The renderer has 4 modes now:\r
11  * - normal\r
12  * - shadow/hilight (s/h)\r
13  * - "sonic mode" for midline palette changes (8bit mode only)\r
14  * - accurate sprites (AS) [+ s/h]\r
15  *\r
16  * AS and s/h both use upper bits for both priority and shadow/hilight flags.\r
17  * "sonic mode" is autodetected, shadow/hilight is enabled by emulated game.\r
18  * AS is enabled by user and takes priority over "sonic mode".\r
19  *\r
20  * since renderer always draws line in 8bit mode, there are 2 spare bits:\r
21  * b \ mode: s/h             as        sonic\r
22  * 00        normal          -         pal index\r
23  * 01        shadow          -         pal index\r
24  * 10        hilight+op spr  spr       pal index\r
25  * 11        shadow +op spr  -         pal index\r
26  *\r
27  * not handled properly:\r
28  * - hilight op on shadow tile\r
29  * - AS + s/h (s/h sprite flag interferes with and cleared by AS code)\r
30  */\r
31 \r
32 #include "pico_int.h"\r
33 \r
34 int (*PicoScanBegin)(unsigned int num) = NULL;\r
35 int (*PicoScanEnd)  (unsigned int num) = NULL;\r
36 \r
37 static unsigned char DefHighCol[8+320+8];\r
38 static unsigned char *HighColBase = DefHighCol;\r
39 static int HighColIncrement;\r
40 \r
41 static unsigned int DefOutBuff[320*2/2];\r
42 void *DrawLineDestBase = DefOutBuff;\r
43 int DrawLineDestIncrement;\r
44 \r
45 static int  HighCacheA[41+1];   // caches for high layers\r
46 static int  HighCacheB[41+1];\r
47 static int  HighPreSpr[80*2+1]; // slightly preprocessed sprites\r
48 \r
49 #define LF_PLANE_1 (1 << 0)\r
50 #define LF_SH      (1 << 1) // must be = 2\r
51 #define LF_FORCE   (1 << 2)\r
52 \r
53 #define SPRL_HAVE_HI     0x80 // have hi priority sprites\r
54 #define SPRL_HAVE_LO     0x40 // *lo*\r
55 #define SPRL_MAY_HAVE_OP 0x20 // may have operator sprites on the line\r
56 #define SPRL_LO_ABOVE_HI 0x10 // low priority sprites may be on top of hi\r
57 unsigned char HighLnSpr[240][3 + MAX_LINE_SPRITES]; // sprite_count, ^flags, tile_count, [spritep]...\r
58 \r
59 int rendstatus_old;\r
60 int rendlines;\r
61 \r
62 static int skip_next_line=0;\r
63 \r
64 struct TileStrip\r
65 {\r
66   int nametab; // Position in VRAM of name table (for this tile line)\r
67   int line;    // Line number in pixels 0x000-0x3ff within the virtual tilemap\r
68   int hscroll; // Horizontal scroll value in pixels for the line\r
69   int xmask;   // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap\r
70   int *hc;     // cache for high tile codes and their positions\r
71   int cells;   // cells (tiles) to draw (32 col mode doesn't need to update whole 320)\r
72 };\r
73 \r
74 // stuff available in asm:\r
75 #ifdef _ASM_DRAW_C\r
76 void DrawWindow(int tstart, int tend, int prio, int sh,\r
77                 struct PicoEState *est);\r
78 void DrawAllSprites(unsigned char *sprited, int prio, int sh,\r
79                     struct PicoEState *est);\r
80 void DrawTilesFromCache(int *hc, int sh, int rlim,\r
81                     struct PicoEState *est);\r
82 void DrawSpritesSHi(unsigned char *sprited, struct PicoEState *est);\r
83 void DrawLayer(int plane_sh, int *hcache, int cellskip, int maxcells,\r
84                struct PicoEState *est);\r
85 void *blockcpy(void *dst, const void *src, size_t n);\r
86 void blockcpy_or(void *dst, void *src, size_t n, int pat);\r
87 #else\r
88 // utility\r
89 void blockcpy_or(void *dst, void *src, size_t n, int pat)\r
90 {\r
91   unsigned char *pd = dst, *ps = src;\r
92   for (; n; n--)\r
93     *pd++ = (unsigned char) (*ps++ | pat);\r
94 }\r
95 #define blockcpy memcpy\r
96 #endif\r
97 \r
98 \r
99 #define TileNormMaker(funcname,pix_func)                     \\r
100 static void funcname(int sx, unsigned int pack, int pal)     \\r
101 {                                                            \\r
102   unsigned char *pd = Pico.est.HighCol + sx;                 \\r
103   unsigned int t;                                            \\r
104                                                              \\r
105   t = (pack&0x0000f000)>>12; pix_func(0);                    \\r
106   t = (pack&0x00000f00)>> 8; pix_func(1);                    \\r
107   t = (pack&0x000000f0)>> 4; pix_func(2);                    \\r
108   t = (pack&0x0000000f)    ; pix_func(3);                    \\r
109   t = (pack&0xf0000000)>>28; pix_func(4);                    \\r
110   t = (pack&0x0f000000)>>24; pix_func(5);                    \\r
111   t = (pack&0x00f00000)>>20; pix_func(6);                    \\r
112   t = (pack&0x000f0000)>>16; pix_func(7);                    \\r
113 }\r
114 \r
115 #define TileFlipMaker(funcname,pix_func)                     \\r
116 static void funcname(int sx, unsigned int pack, int pal)     \\r
117 {                                                            \\r
118   unsigned char *pd = Pico.est.HighCol + sx;                 \\r
119   unsigned int t;                                            \\r
120                                                              \\r
121   t = (pack&0x000f0000)>>16; pix_func(0);                    \\r
122   t = (pack&0x00f00000)>>20; pix_func(1);                    \\r
123   t = (pack&0x0f000000)>>24; pix_func(2);                    \\r
124   t = (pack&0xf0000000)>>28; pix_func(3);                    \\r
125   t = (pack&0x0000000f)    ; pix_func(4);                    \\r
126   t = (pack&0x000000f0)>> 4; pix_func(5);                    \\r
127   t = (pack&0x00000f00)>> 8; pix_func(6);                    \\r
128   t = (pack&0x0000f000)>>12; pix_func(7);                    \\r
129 }\r
130 \r
131 \r
132 #define pix_just_write(x) \\r
133   if (t) pd[x]=pal|t\r
134 \r
135 TileNormMaker(TileNorm,pix_just_write)\r
136 TileFlipMaker(TileFlip,pix_just_write)\r
137 \r
138 #ifndef _ASM_DRAW_C\r
139 \r
140 // draw a sprite pixel, process operator colors\r
141 #define pix_sh(x) \\r
142   if (!t); \\r
143   else if (t>=0xe) pd[x]=(pd[x]&0x3f)|(t<<6); /* c0 shadow, 80 hilight */ \\r
144   else pd[x]=pal|t\r
145 \r
146 TileNormMaker(TileNormSH, pix_sh)\r
147 TileFlipMaker(TileFlipSH, pix_sh)\r
148 \r
149 // draw a sprite pixel, mark operator colors\r
150 #define pix_sh_markop(x) \\r
151   if (!t); \\r
152   else if (t>=0xe) pd[x]|=0x80; \\r
153   else pd[x]=pal|t\r
154 \r
155 TileNormMaker(TileNormSH_markop, pix_sh_markop)\r
156 TileFlipMaker(TileFlipSH_markop, pix_sh_markop)\r
157 \r
158 // process operator pixels only, apply only on low pri tiles and other op pixels\r
159 #define pix_sh_onlyop(x) \\r
160   if (t>=0xe && (pd[x]&0xc0)) \\r
161     pd[x]=(pd[x]&0x3f)|(t<<6); /* c0 shadow, 80 hilight */ \\r
162 \r
163 TileNormMaker(TileNormSH_onlyop_lp, pix_sh_onlyop)\r
164 TileFlipMaker(TileFlipSH_onlyop_lp, pix_sh_onlyop)\r
165 \r
166 #endif\r
167 \r
168 // draw a sprite pixel (AS)\r
169 #define pix_as(x) \\r
170   if (t && !(pd[x]&0x80)) pd[x]=pal|t\r
171 \r
172 TileNormMaker(TileNormAS, pix_as)\r
173 TileFlipMaker(TileFlipAS, pix_as)\r
174 \r
175 // draw a sprite pixel, skip operator colors (AS)\r
176 #define pix_sh_as_noop(x) \\r
177   if (t && t < 0xe && !(pd[x]&0x80)) pd[x]=pal|t\r
178 \r
179 TileNormMaker(TileNormAS_noop, pix_sh_as_noop)\r
180 TileFlipMaker(TileFlipAS_noop, pix_sh_as_noop)\r
181 \r
182 // mark pixel as sprite pixel (AS)\r
183 #define pix_sh_as_onlymark(x) \\r
184   if (t) pd[x]|=0x80\r
185 \r
186 TileNormMaker(TileNormAS_onlymark, pix_sh_as_onlymark)\r
187 TileFlipMaker(TileFlipAS_onlymark, pix_sh_as_onlymark)\r
188 \r
189 // mark pixel as sprite pixel (AS)\r
190 #define pix_and(x) \\r
191   pd[x] = (pd[x] & 0xc0) | (pd[x] & (pal | t))\r
192 \r
193 TileNormMaker(TileNorm_and, pix_and)\r
194 TileFlipMaker(TileFlip_and, pix_and)\r
195 \r
196 // --------------------------------------------\r
197 \r
198 #ifndef _ASM_DRAW_C\r
199 static void DrawStrip(struct TileStrip *ts, int lflags, int cellskip)\r
200 {\r
201   int tilex,dx,ty,code=0,addr=0,cells;\r
202   int oldcode=-1,blank=-1; // The tile we know is blank\r
203   int pal=0,sh;\r
204 \r
205   // Draw tiles across screen:\r
206   sh = (lflags & LF_SH) << 5; // 0x40\r
207   tilex=((-ts->hscroll)>>3)+cellskip;\r
208   ty=(ts->line&7)<<1; // Y-Offset into tile\r
209   dx=((ts->hscroll-1)&7)+1;\r
210   cells = ts->cells - cellskip;\r
211   if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
212   dx+=cellskip<<3;\r
213 \r
214   for (; cells > 0; dx+=8, tilex++, cells--)\r
215   {\r
216     unsigned int pack;\r
217 \r
218     code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
219     if (code == blank)\r
220       continue;\r
221     if ((code >> 15) | (lflags & LF_FORCE)) { // high priority tile\r
222       int cval = code | (dx<<16) | (ty<<25);\r
223       if(code&0x1000) cval^=7<<26;\r
224       *ts->hc++ = cval; // cache it\r
225       continue;\r
226     }\r
227 \r
228     if (code!=oldcode) {\r
229       oldcode = code;\r
230       // Get tile address/2:\r
231       addr=(code&0x7ff)<<4;\r
232       addr+=ty;\r
233       if (code&0x1000) addr^=0xe; // Y-flip\r
234 \r
235       pal=((code>>9)&0x30)|sh;\r
236     }\r
237 \r
238     pack = *(unsigned int *)(PicoMem.vram + addr);\r
239     if (!pack) {\r
240       blank = code;\r
241       continue;\r
242     }\r
243 \r
244     if (code & 0x0800) TileFlip(dx, pack, pal);\r
245     else               TileNorm(dx, pack, pal);\r
246   }\r
247 \r
248   // terminate the cache list\r
249   *ts->hc = 0;\r
250   // if oldcode wasn't changed, it means all layer is hi priority\r
251   if (oldcode == -1) Pico.est.rendstatus |= PDRAW_PLANE_HI_PRIO;\r
252 }\r
253 \r
254 // this is messy\r
255 static void DrawStripVSRam(struct TileStrip *ts, int plane_sh, int cellskip)\r
256 {\r
257   int tilex,dx,code=0,addr=0,cell=0;\r
258   int oldcode=-1,blank=-1; // The tile we know is blank\r
259   int pal=0,scan=Pico.est.DrawScanline;\r
260 \r
261   // Draw tiles across screen:\r
262   tilex=(-ts->hscroll)>>3;\r
263   dx=((ts->hscroll-1)&7)+1;\r
264   if(dx != 8) cell--; // have hscroll, start with negative cell\r
265   cell+=cellskip;\r
266   tilex+=cellskip;\r
267   dx+=cellskip<<3;\r
268 \r
269   for (; cell < ts->cells; dx+=8,tilex++,cell++)\r
270   {\r
271     int nametabadd, ty;\r
272     unsigned int pack;\r
273 \r
274     //if((cell&1)==0)\r
275     {\r
276       int line,vscroll;\r
277       vscroll=PicoMem.vsram[(plane_sh&1)+(cell&~1)];\r
278 \r
279       // Find the line in the name table\r
280       line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
281       nametabadd=(line>>3)<<(ts->line>>24);    // .. and shift[width]\r
282       ty=(line&7)<<1; // Y-Offset into tile\r
283     }\r
284 \r
285     code=PicoMem.vram[ts->nametab+nametabadd+(tilex&ts->xmask)];\r
286     if (code==blank) continue;\r
287     if (code>>15) { // high priority tile\r
288       int cval = code | (dx<<16) | (ty<<25);\r
289       if(code&0x1000) cval^=7<<26;\r
290       *ts->hc++ = cval; // cache it\r
291       continue;\r
292     }\r
293 \r
294     if (code!=oldcode) {\r
295       oldcode = code;\r
296       // Get tile address/2:\r
297       addr=(code&0x7ff)<<4;\r
298       if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
299 \r
300       pal=((code>>9)&0x30)|((plane_sh<<5)&0x40);\r
301     }\r
302 \r
303     pack = *(unsigned int *)(PicoMem.vram + addr);\r
304     if (!pack) {\r
305       blank = code;\r
306       continue;\r
307     }\r
308 \r
309     if (code & 0x0800) TileFlip(dx, pack, pal);\r
310     else               TileNorm(dx, pack, pal);\r
311   }\r
312 \r
313   // terminate the cache list\r
314   *ts->hc = 0;\r
315   if (oldcode == -1) Pico.est.rendstatus |= PDRAW_PLANE_HI_PRIO;\r
316 }\r
317 #endif\r
318 \r
319 #ifndef _ASM_DRAW_C\r
320 static\r
321 #endif\r
322 void DrawStripInterlace(struct TileStrip *ts)\r
323 {\r
324   int tilex=0,dx=0,ty=0,code=0,addr=0,cells;\r
325   int oldcode=-1,blank=-1; // The tile we know is blank\r
326   int pal=0;\r
327 \r
328   // Draw tiles across screen:\r
329   tilex=(-ts->hscroll)>>3;\r
330   ty=(ts->line&15)<<1; // Y-Offset into tile\r
331   dx=((ts->hscroll-1)&7)+1;\r
332   cells = ts->cells;\r
333   if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
334 \r
335   for (; cells; dx+=8,tilex++,cells--)\r
336   {\r
337     unsigned int pack;\r
338 \r
339     code = PicoMem.vram[ts->nametab + (tilex & ts->xmask)];\r
340     if (code==blank) continue;\r
341     if (code>>15) { // high priority tile\r
342       int cval = (code&0xfc00) | (dx<<16) | (ty<<25);\r
343       cval|=(code&0x3ff)<<1;\r
344       if(code&0x1000) cval^=0xf<<26;\r
345       *ts->hc++ = cval; // cache it\r
346       continue;\r
347     }\r
348 \r
349     if (code!=oldcode) {\r
350       oldcode = code;\r
351       // Get tile address/2:\r
352       addr=(code&0x7ff)<<5;\r
353       if (code&0x1000) addr+=30-ty; else addr+=ty; // Y-flip\r
354 \r
355 //      pal=Pico.cram+((code>>9)&0x30);\r
356       pal=((code>>9)&0x30);\r
357     }\r
358 \r
359     pack = *(unsigned int *)(PicoMem.vram + addr);\r
360     if (!pack) {\r
361       blank = code;\r
362       continue;\r
363     }\r
364 \r
365     if (code & 0x0800) TileFlip(dx, pack, pal);\r
366     else               TileNorm(dx, pack, pal);\r
367   }\r
368 \r
369   // terminate the cache list\r
370   *ts->hc = 0;\r
371 }\r
372 \r
373 // --------------------------------------------\r
374 \r
375 #ifndef _ASM_DRAW_C\r
376 static void DrawLayer(int plane_sh, int *hcache, int cellskip, int maxcells,\r
377   struct PicoEState *est)\r
378 {\r
379   struct PicoVideo *pvid=&Pico.video;\r
380   const char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps (2 is invalid)\r
381   struct TileStrip ts;\r
382   int width, height, ymask;\r
383   int vscroll, htab;\r
384 \r
385   ts.hc=hcache;\r
386   ts.cells=maxcells;\r
387 \r
388   // Work out the TileStrip to draw\r
389 \r
390   // Work out the name table size: 32 64 or 128 tiles (0-3)\r
391   width=pvid->reg[16];\r
392   height=(width>>4)&3; width&=3;\r
393 \r
394   ts.xmask=(1<<shift[width])-1; // X Mask in tiles (0x1f-0x7f)\r
395   ymask=(height<<8)|0xff;       // Y Mask in pixels\r
396   switch (width) {\r
397     case 1: ymask &= 0x1ff; break;\r
398     case 2: ymask =  0x007; break;\r
399     case 3: ymask =  0x0ff; break;\r
400   }\r
401 \r
402   // Find name table:\r
403   if (plane_sh&1) ts.nametab=(pvid->reg[4]&0x07)<<12; // B\r
404   else            ts.nametab=(pvid->reg[2]&0x38)<< 9; // A\r
405 \r
406   htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
407   if ( pvid->reg[11]&2)     htab+=est->DrawScanline<<1; // Offset by line\r
408   if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile\r
409   htab+=plane_sh&1; // A or B\r
410 \r
411   // Get horizontal scroll value, will be masked later\r
412   ts.hscroll = PicoMem.vram[htab & 0x7fff];\r
413 \r
414   if((pvid->reg[12]&6) == 6) {\r
415     // interlace mode 2\r
416     vscroll = PicoMem.vsram[plane_sh & 1]; // Get vertical scroll value\r
417 \r
418     // Find the line in the name table\r
419     ts.line=(vscroll+(est->DrawScanline<<1))&((ymask<<1)|1);\r
420     ts.nametab+=(ts.line>>4)<<shift[width];\r
421 \r
422     DrawStripInterlace(&ts);\r
423   } else if( pvid->reg[11]&4) {\r
424     // shit, we have 2-cell column based vscroll\r
425     // luckily this doesn't happen too often\r
426     ts.line=ymask|(shift[width]<<24); // save some stuff instead of line\r
427     DrawStripVSRam(&ts, plane_sh, cellskip);\r
428   } else {\r
429     vscroll = PicoMem.vsram[plane_sh & 1]; // Get vertical scroll value\r
430 \r
431     // Find the line in the name table\r
432     ts.line=(vscroll+est->DrawScanline)&ymask;\r
433     ts.nametab+=(ts.line>>3)<<shift[width];\r
434 \r
435     DrawStrip(&ts, plane_sh, cellskip);\r
436   }\r
437 }\r
438 \r
439 \r
440 // --------------------------------------------\r
441 \r
442 // tstart & tend are tile pair numbers\r
443 static void DrawWindow(int tstart, int tend, int prio, int sh,\r
444                        struct PicoEState *est)\r
445 {\r
446   struct PicoVideo *pvid=&Pico.video;\r
447   int tilex,ty,nametab,code=0;\r
448   int blank=-1; // The tile we know is blank\r
449 \r
450   // Find name table line:\r
451   if (pvid->reg[12]&1)\r
452   {\r
453     nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode\r
454     nametab+=(est->DrawScanline>>3)<<6;\r
455   }\r
456   else\r
457   {\r
458     nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode\r
459     nametab+=(est->DrawScanline>>3)<<5;\r
460   }\r
461 \r
462   tilex=tstart<<1;\r
463 \r
464   if (!(est->rendstatus & PDRAW_WND_DIFF_PRIO)) {\r
465     // check the first tile code\r
466     code = PicoMem.vram[nametab + tilex];\r
467     // if the whole window uses same priority (what is often the case), we may be able to skip this field\r
468     if ((code>>15) != prio) return;\r
469   }\r
470 \r
471   tend<<=1;\r
472   ty=(est->DrawScanline&7)<<1; // Y-Offset into tile\r
473 \r
474   // Draw tiles across screen:\r
475   if (!sh)\r
476   {\r
477     for (; tilex < tend; tilex++)\r
478     {\r
479       unsigned int pack;\r
480       int dx, addr;\r
481       int pal;\r
482 \r
483       code = PicoMem.vram[nametab + tilex];\r
484       if (code==blank) continue;\r
485       if ((code>>15) != prio) {\r
486         est->rendstatus |= PDRAW_WND_DIFF_PRIO;\r
487         continue;\r
488       }\r
489 \r
490       // Get tile address/2:\r
491       addr=(code&0x7ff)<<4;\r
492       if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
493 \r
494       pack = *(unsigned int *)(PicoMem.vram + addr);\r
495       if (!pack) {\r
496         blank = code;\r
497         continue;\r
498       }\r
499 \r
500       pal = ((code >> 9) & 0x30);\r
501       dx = 8 + (tilex << 3);\r
502 \r
503       if (code & 0x0800) TileFlip(dx, pack, pal);\r
504       else               TileNorm(dx, pack, pal);\r
505     }\r
506   }\r
507   else\r
508   {\r
509     for (; tilex < tend; tilex++)\r
510     {\r
511       unsigned int pack;\r
512       int dx, addr;\r
513       int pal;\r
514 \r
515       code = PicoMem.vram[nametab + tilex];\r
516       if(code==blank) continue;\r
517       if((code>>15) != prio) {\r
518         est->rendstatus |= PDRAW_WND_DIFF_PRIO;\r
519         continue;\r
520       }\r
521 \r
522       pal=((code>>9)&0x30);\r
523 \r
524       if (prio) {\r
525         int *zb = (int *)(est->HighCol+8+(tilex<<3));\r
526         *zb++ &= 0xbfbfbfbf;\r
527         *zb   &= 0xbfbfbfbf;\r
528       } else {\r
529         pal |= 0x40;\r
530       }\r
531 \r
532       // Get tile address/2:\r
533       addr=(code&0x7ff)<<4;\r
534       if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
535 \r
536       pack = *(unsigned int *)(PicoMem.vram + addr);\r
537       if (!pack) {\r
538         blank = code;\r
539         continue;\r
540       }\r
541 \r
542       dx = 8 + (tilex << 3);\r
543 \r
544       if (code & 0x0800) TileFlip(dx, pack, pal);\r
545       else               TileNorm(dx, pack, pal);\r
546     }\r
547   }\r
548 }\r
549 \r
550 // --------------------------------------------\r
551 \r
552 static void DrawTilesFromCacheShPrep(void)\r
553 {\r
554   // as some layer has covered whole line with hi priority tiles,\r
555   // we can process whole line and then act as if sh/hi mode was off,\r
556   // but leave lo pri op sprite markers alone\r
557   int c = 320/4, *zb = (int *)(Pico.est.HighCol+8);\r
558   Pico.est.rendstatus |= PDRAW_SHHI_DONE;\r
559   while (c--)\r
560   {\r
561     *zb++ &= 0xbfbfbfbf;\r
562   }\r
563 }\r
564 \r
565 static void DrawTilesFromCache(int *hc, int sh, int rlim, struct PicoEState *est)\r
566 {\r
567   int code, addr, dx;\r
568   unsigned int pack;\r
569   int pal;\r
570 \r
571   // *ts->hc++ = code | (dx<<16) | (ty<<25); // cache it\r
572 \r
573   if (sh && (est->rendstatus & (PDRAW_SHHI_DONE|PDRAW_PLANE_HI_PRIO)))\r
574   {\r
575     if (!(est->rendstatus & PDRAW_SHHI_DONE))\r
576       DrawTilesFromCacheShPrep();\r
577     sh = 0;\r
578   }\r
579 \r
580   if (!sh)\r
581   {\r
582     short blank=-1; // The tile we know is blank\r
583     while ((code=*hc++)) {\r
584       if (!(code & 0x8000) || (short)code == blank)\r
585         continue;\r
586       // Get tile address/2:\r
587       addr = (code & 0x7ff) << 4;\r
588       addr += code >> 25; // y offset into tile\r
589 \r
590       pack = *(unsigned int *)(PicoMem.vram + addr);\r
591       if (!pack) {\r
592         blank = (short)code;\r
593         continue;\r
594       }\r
595 \r
596       dx = (code >> 16) & 0x1ff;\r
597       pal = ((code >> 9) & 0x30);\r
598       if (rlim-dx < 0)\r
599         goto last_cut_tile;\r
600 \r
601       if (code & 0x0800) TileFlip(dx, pack, pal);\r
602       else               TileNorm(dx, pack, pal);\r
603     }\r
604   }\r
605   else\r
606   {\r
607     while ((code=*hc++)) {\r
608       unsigned char *zb;\r
609 \r
610       // Get tile address/2:\r
611       addr=(code&0x7ff)<<4;\r
612       addr+=(unsigned int)code>>25; // y offset into tile\r
613       dx=(code>>16)&0x1ff;\r
614       zb = est->HighCol+dx;\r
615       *zb++ &= 0xbf; *zb++ &= 0xbf; *zb++ &= 0xbf; *zb++ &= 0xbf;\r
616       *zb++ &= 0xbf; *zb++ &= 0xbf; *zb++ &= 0xbf; *zb++ &= 0xbf;\r
617 \r
618       pack = *(unsigned int *)(PicoMem.vram + addr);\r
619       if (!pack)\r
620         continue;\r
621 \r
622       pal = ((code >> 9) & 0x30);\r
623       if (rlim - dx < 0)\r
624         goto last_cut_tile;\r
625 \r
626       if (code & 0x0800) TileFlip(dx, pack, pal);\r
627       else               TileNorm(dx, pack, pal);\r
628     }\r
629   }\r
630   return;\r
631 \r
632 last_cut_tile:\r
633   // for vertical window cutoff\r
634   {\r
635     unsigned char *pd = est->HighCol + dx;\r
636     unsigned int t;\r
637 \r
638     if (code&0x0800)\r
639     {\r
640       switch (rlim-dx+8)\r
641       {\r
642         case 7: t=pack&0x00000f00; if (t) pd[6]=(unsigned char)(pal|(t>> 8)); // "break" is left out intentionally\r
643         case 6: t=pack&0x000000f0; if (t) pd[5]=(unsigned char)(pal|(t>> 4));\r
644         case 5: t=pack&0x0000000f; if (t) pd[4]=(unsigned char)(pal|(t    ));\r
645         case 4: t=pack&0xf0000000; if (t) pd[3]=(unsigned char)(pal|(t>>28));\r
646         case 3: t=pack&0x0f000000; if (t) pd[2]=(unsigned char)(pal|(t>>24));\r
647         case 2: t=pack&0x00f00000; if (t) pd[1]=(unsigned char)(pal|(t>>20));\r
648         case 1: t=pack&0x000f0000; if (t) pd[0]=(unsigned char)(pal|(t>>16));\r
649         default: break;\r
650       }\r
651     }\r
652     else\r
653     {\r
654       switch (rlim-dx+8)\r
655       {\r
656         case 7: t=pack&0x00f00000; if (t) pd[6]=(unsigned char)(pal|(t>>20));\r
657         case 6: t=pack&0x0f000000; if (t) pd[5]=(unsigned char)(pal|(t>>24));\r
658         case 5: t=pack&0xf0000000; if (t) pd[4]=(unsigned char)(pal|(t>>28));\r
659         case 4: t=pack&0x0000000f; if (t) pd[3]=(unsigned char)(pal|(t    ));\r
660         case 3: t=pack&0x000000f0; if (t) pd[2]=(unsigned char)(pal|(t>> 4));\r
661         case 2: t=pack&0x00000f00; if (t) pd[1]=(unsigned char)(pal|(t>> 8));\r
662         case 1: t=pack&0x0000f000; if (t) pd[0]=(unsigned char)(pal|(t>>12));\r
663         default: break;\r
664       }\r
665     }\r
666   }\r
667 }\r
668 \r
669 // --------------------------------------------\r
670 \r
671 // Index + 0  :    hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size\r
672 // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
673 \r
674 static void DrawSprite(int *sprite, int sh)\r
675 {\r
676   int width=0,height=0;\r
677   int row=0,code=0;\r
678   int pal;\r
679   int tile=0,delta=0;\r
680   int sx, sy;\r
681   void (*fTileFunc)(int sx, unsigned int pack, int pal);\r
682 \r
683   // parse the sprite data\r
684   sy=sprite[0];\r
685   code=sprite[1];\r
686   sx=code>>16; // X\r
687   width=sy>>28;\r
688   height=(sy>>24)&7; // Width and height in tiles\r
689   sy=(sy<<16)>>16; // Y\r
690 \r
691   row=Pico.est.DrawScanline-sy; // Row of the sprite we are on\r
692 \r
693   if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
694 \r
695   tile=code + (row>>3); // Tile number increases going down\r
696   delta=height; // Delta to increase tile by going right\r
697   if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
698 \r
699   tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
700   delta<<=4; // Delta of address\r
701 \r
702   pal=(code>>9)&0x30;\r
703   pal|=sh<<6;\r
704 \r
705   if (sh && (code&0x6000) == 0x6000) {\r
706     if(code&0x0800) fTileFunc=TileFlipSH_markop;\r
707     else            fTileFunc=TileNormSH_markop;\r
708   } else {\r
709     if(code&0x0800) fTileFunc=TileFlip;\r
710     else            fTileFunc=TileNorm;\r
711   }\r
712 \r
713   for (; width; width--,sx+=8,tile+=delta)\r
714   {\r
715     unsigned int pack;\r
716 \r
717     if(sx<=0)   continue;\r
718     if(sx>=328) break; // Offscreen\r
719 \r
720     pack = *(unsigned int *)(PicoMem.vram + (tile & 0x7fff));\r
721     fTileFunc(sx, pack, pal);\r
722   }\r
723 }\r
724 #endif\r
725 \r
726 static NOINLINE void DrawTilesFromCacheForced(const int *hc)\r
727 {\r
728   int code, addr, dx;\r
729   unsigned int pack;\r
730   int pal;\r
731 \r
732   // *ts->hc++ = code | (dx<<16) | (ty<<25);\r
733   while ((code = *hc++)) {\r
734     // Get tile address/2:\r
735     addr = (code & 0x7ff) << 4;\r
736     addr += (code >> 25) & 0x0e; // y offset into tile\r
737 \r
738     dx = (code >> 16) & 0x1ff;\r
739     pal = ((code >> 9) & 0x30);\r
740     pack = *(unsigned int *)(PicoMem.vram + addr);\r
741 \r
742     if (code & 0x0800) TileFlip_and(dx, pack, pal);\r
743     else               TileNorm_and(dx, pack, pal);\r
744   }\r
745 }\r
746 \r
747 static void DrawSpriteInterlace(unsigned int *sprite)\r
748 {\r
749   int width=0,height=0;\r
750   int row=0,code=0;\r
751   int pal;\r
752   int tile=0,delta=0;\r
753   int sx, sy;\r
754 \r
755   // parse the sprite data\r
756   sy=sprite[0];\r
757   height=sy>>24;\r
758   sy=(sy&0x3ff)-0x100; // Y\r
759   width=(height>>2)&3; height&=3;\r
760   width++; height++; // Width and height in tiles\r
761 \r
762   row=(Pico.est.DrawScanline<<1)-sy; // Row of the sprite we are on\r
763 \r
764   code=sprite[1];\r
765   sx=((code>>16)&0x1ff)-0x78; // X\r
766 \r
767   if (code&0x1000) row^=(16<<height)-1; // Flip Y\r
768 \r
769   tile=code&0x3ff; // Tile number\r
770   tile+=row>>4; // Tile number increases going down\r
771   delta=height; // Delta to increase tile by going right\r
772   if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
773 \r
774   tile<<=5; tile+=(row&15)<<1; // Tile address\r
775 \r
776   delta<<=5; // Delta of address\r
777   pal=((code>>9)&0x30); // Get palette pointer\r
778 \r
779   for (; width; width--,sx+=8,tile+=delta)\r
780   {\r
781     unsigned int pack;\r
782 \r
783     if(sx<=0)   continue;\r
784     if(sx>=328) break; // Offscreen\r
785 \r
786     pack = *(unsigned int *)(PicoMem.vram + (tile & 0x7fff));\r
787     if (code & 0x0800) TileFlip(sx, pack, pal);\r
788     else               TileNorm(sx, pack, pal);\r
789   }\r
790 }\r
791 \r
792 \r
793 static NOINLINE void DrawAllSpritesInterlace(int pri, int sh)\r
794 {\r
795   struct PicoVideo *pvid=&Pico.video;\r
796   int i,u,table,link=0,sline=Pico.est.DrawScanline<<1;\r
797   unsigned int *sprites[80]; // Sprite index\r
798 \r
799   table=pvid->reg[5]&0x7f;\r
800   if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
801   table<<=8; // Get sprite table address/2\r
802 \r
803   for (i=u=0; u < 80 && i < 21; u++)\r
804   {\r
805     unsigned int *sprite;\r
806     int code, sx, sy, height;\r
807 \r
808     sprite=(unsigned int *)(PicoMem.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
809 \r
810     // get sprite info\r
811     code = sprite[0];\r
812     sx = sprite[1];\r
813     if(((sx>>15)&1) != pri) goto nextsprite; // wrong priority sprite\r
814 \r
815     // check if it is on this line\r
816     sy = (code&0x3ff)-0x100;\r
817     height = (((code>>24)&3)+1)<<4;\r
818     if(sline < sy || sline >= sy+height) goto nextsprite; // no\r
819 \r
820     // check if sprite is not hidden offscreen\r
821     sx = (sx>>16)&0x1ff;\r
822     sx -= 0x78; // Get X coordinate + 8\r
823     if(sx <= -8*3 || sx >= 328) goto nextsprite;\r
824 \r
825     // sprite is good, save it's pointer\r
826     sprites[i++]=sprite;\r
827 \r
828     nextsprite:\r
829     // Find next sprite\r
830     link=(code>>16)&0x7f;\r
831     if(!link) break; // End of sprites\r
832   }\r
833 \r
834   // Go through sprites backwards:\r
835   for (i-- ;i>=0; i--)\r
836     DrawSpriteInterlace(sprites[i]);\r
837 }\r
838 \r
839 \r
840 #ifndef _ASM_DRAW_C\r
841 /*\r
842  * s/h drawing: lo_layers|40, lo_sprites|40 && mark_op,\r
843  *        hi_layers&=~40, hi_sprites\r
844  *\r
845  * Index + 0  :    hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size\r
846  * Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
847  */\r
848 static void DrawSpritesSHi(unsigned char *sprited, const struct PicoEState *est)\r
849 {\r
850   void (*fTileFunc)(int sx, unsigned int pack, int pal);\r
851   unsigned char *p;\r
852   int cnt;\r
853 \r
854   cnt = sprited[0] & 0x7f;\r
855   if (cnt == 0) return;\r
856 \r
857   p = &sprited[3];\r
858 \r
859   // Go through sprites backwards:\r
860   for (cnt--; cnt >= 0; cnt--)\r
861   {\r
862     int *sprite, code, pal, tile, sx, sy;\r
863     int offs, delta, width, height, row;\r
864 \r
865     offs = (p[cnt] & 0x7f) * 2;\r
866     sprite = est->HighPreSpr + offs;\r
867     code = sprite[1];\r
868     pal = (code>>9)&0x30;\r
869 \r
870     if (pal == 0x30)\r
871     {\r
872       if (code & 0x8000) // hi priority\r
873       {\r
874         if (code&0x800) fTileFunc=TileFlipSH;\r
875         else            fTileFunc=TileNormSH;\r
876       } else {\r
877         if (code&0x800) fTileFunc=TileFlipSH_onlyop_lp;\r
878         else            fTileFunc=TileNormSH_onlyop_lp;\r
879       }\r
880     } else {\r
881       if (!(code & 0x8000)) continue; // non-operator low sprite, already drawn\r
882       if (code&0x800) fTileFunc=TileFlip;\r
883       else            fTileFunc=TileNorm;\r
884     }\r
885 \r
886     // parse remaining sprite data\r
887     sy=sprite[0];\r
888     sx=code>>16; // X\r
889     width=sy>>28;\r
890     height=(sy>>24)&7; // Width and height in tiles\r
891     sy=(sy<<16)>>16; // Y\r
892 \r
893     row=est->DrawScanline-sy; // Row of the sprite we are on\r
894 \r
895     if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
896 \r
897     tile=code + (row>>3); // Tile number increases going down\r
898     delta=height; // Delta to increase tile by going right\r
899     if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
900 \r
901     tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
902     delta<<=4; // Delta of address\r
903 \r
904     for (; width; width--,sx+=8,tile+=delta)\r
905     {\r
906       unsigned int pack;\r
907 \r
908       if(sx<=0)   continue;\r
909       if(sx>=328) break; // Offscreen\r
910 \r
911       pack = *(unsigned int *)(PicoMem.vram + (tile & 0x7fff));\r
912       fTileFunc(sx, pack, pal);\r
913     }\r
914   }\r
915 }\r
916 #endif // !_ASM_DRAW_C\r
917 \r
918 static void DrawSpritesHiAS(unsigned char *sprited, int sh)\r
919 {\r
920   void (*fTileFunc)(int sx, unsigned int pack, int pal);\r
921   unsigned char *p;\r
922   int entry, cnt, sh_cnt = 0;\r
923 \r
924   cnt = sprited[0] & 0x7f;\r
925   if (cnt == 0) return;\r
926 \r
927   Pico.est.rendstatus |= PDRAW_SPR_LO_ON_HI;\r
928 \r
929   p = &sprited[3];\r
930 \r
931   // Go through sprites:\r
932   for (entry = 0; entry < cnt; entry++)\r
933   {\r
934     int *sprite, code, pal, tile, sx, sy;\r
935     int offs, delta, width, height, row;\r
936 \r
937     offs = (p[entry] & 0x7f) * 2;\r
938     sprite = HighPreSpr + offs;\r
939     code = sprite[1];\r
940     pal = (code>>9)&0x30;\r
941 \r
942     if (code & 0x8000) // hi priority\r
943     {\r
944       if (sh && pal == 0x30)\r
945       {\r
946         if (code&0x800) fTileFunc=TileFlipAS_noop;\r
947         else            fTileFunc=TileNormAS_noop;\r
948       } else {\r
949         if (code&0x800) fTileFunc=TileFlipAS;\r
950         else            fTileFunc=TileNormAS;\r
951       }\r
952     } else {\r
953       if (code&0x800) fTileFunc=TileFlipAS_onlymark;\r
954       else            fTileFunc=TileNormAS_onlymark;\r
955     }\r
956     if (sh && pal == 0x30)\r
957       p[sh_cnt++] = offs / 2; // re-save for sh/hi pass\r
958 \r
959     // parse remaining sprite data\r
960     sy=sprite[0];\r
961     sx=code>>16; // X\r
962     width=sy>>28;\r
963     height=(sy>>24)&7; // Width and height in tiles\r
964     sy=(sy<<16)>>16; // Y\r
965 \r
966     row=Pico.est.DrawScanline-sy; // Row of the sprite we are on\r
967 \r
968     if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
969 \r
970     tile=code + (row>>3); // Tile number increases going down\r
971     delta=height; // Delta to increase tile by going right\r
972     if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
973 \r
974     tile &= 0x7ff; tile<<=4; tile+=(row&7)<<1; // Tile address\r
975     delta<<=4; // Delta of address\r
976 \r
977     pal |= 0x80;\r
978     for (; width; width--,sx+=8,tile+=delta)\r
979     {\r
980       unsigned int pack;\r
981 \r
982       if(sx<=0)   continue;\r
983       if(sx>=328) break; // Offscreen\r
984 \r
985       pack = *(unsigned int *)(PicoMem.vram + (tile & 0x7fff));\r
986       fTileFunc(sx, pack, pal);\r
987     }\r
988   }\r
989 \r
990   if (!sh || !(sprited[1]&SPRL_MAY_HAVE_OP)) return;\r
991 \r
992   /* nasty 1: remove 'sprite' flags */\r
993   {\r
994     int c = 320/4/4, *zb = (int *)(Pico.est.HighCol+8);\r
995     while (c--)\r
996     {\r
997       *zb++ &= 0x7f7f7f7f; *zb++ &= 0x7f7f7f7f;\r
998       *zb++ &= 0x7f7f7f7f; *zb++ &= 0x7f7f7f7f;\r
999     }\r
1000   }\r
1001 \r
1002   /* nasty 2: sh operator pass */\r
1003   sprited[0] = sh_cnt;\r
1004   DrawSpritesSHi(sprited, &Pico.est);\r
1005 }\r
1006 \r
1007 \r
1008 // Index + 0  :    ----hhvv -lllllll -------y yyyyyyyy\r
1009 // Index + 4  :    -------x xxxxxxxx pccvhnnn nnnnnnnn\r
1010 // v\r
1011 // Index + 0  :    hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size\r
1012 // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
1013 \r
1014 static NOINLINE void PrepareSprites(int full)\r
1015 {\r
1016   const struct PicoVideo *pvid=&Pico.video;\r
1017   const struct PicoEState *est=&Pico.est;\r
1018   int u,link=0,sh;\r
1019   int table=0;\r
1020   int *pd = HighPreSpr;\r
1021   int max_lines = 224, max_sprites = 80, max_width = 328;\r
1022   int max_line_sprites = 20; // 20 sprites, 40 tiles\r
1023 \r
1024   if (!(Pico.video.reg[12]&1))\r
1025     max_sprites = 64, max_line_sprites = 16, max_width = 264;\r
1026   if (PicoOpt & POPT_DIS_SPRITE_LIM)\r
1027     max_line_sprites = MAX_LINE_SPRITES;\r
1028 \r
1029   if (pvid->reg[1]&8) max_lines = 240;\r
1030   sh = Pico.video.reg[0xC]&8; // shadow/hilight?\r
1031 \r
1032   table=pvid->reg[5]&0x7f;\r
1033   if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
1034   table<<=8; // Get sprite table address/2\r
1035 \r
1036   if (!full)\r
1037   {\r
1038     int pack;\r
1039     // updates: tilecode, sx\r
1040     for (u=0; u < max_sprites && (pack = *pd); u++, pd+=2)\r
1041     {\r
1042       unsigned int *sprite;\r
1043       int code2, sx, sy, height;\r
1044 \r
1045       sprite=(unsigned int *)(PicoMem.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
1046 \r
1047       // parse sprite info\r
1048       code2 = sprite[1];\r
1049       sx = (code2>>16)&0x1ff;\r
1050       sx -= 0x78; // Get X coordinate + 8\r
1051       sy = (pack << 16) >> 16;\r
1052       height = (pack >> 24) & 0xf;\r
1053 \r
1054       if (sy < max_lines &&\r
1055           sy + (height<<3) > est->DrawScanline && // sprite onscreen (y)?\r
1056           (sx > -24 || sx < max_width))                   // onscreen x\r
1057       {\r
1058         int y = (sy >= est->DrawScanline) ? sy : est->DrawScanline;\r
1059         int entry = ((pd - HighPreSpr) / 2) | ((code2>>8)&0x80);\r
1060         for (; y < sy + (height<<3) && y < max_lines; y++)\r
1061         {\r
1062           int i, cnt;\r
1063           cnt = HighLnSpr[y][0] & 0x7f;\r
1064           if (cnt >= max_line_sprites) continue;              // sprite limit?\r
1065 \r
1066           for (i = 0; i < cnt; i++)\r
1067             if (((HighLnSpr[y][3+i] ^ entry) & 0x7f) == 0) goto found;\r
1068 \r
1069           // this sprite was previously missing\r
1070           HighLnSpr[y][3+cnt] = entry;\r
1071           HighLnSpr[y][0] = cnt + 1;\r
1072 found:;\r
1073           if (entry & 0x80)\r
1074                HighLnSpr[y][1] |= SPRL_HAVE_HI;\r
1075           else HighLnSpr[y][1] |= SPRL_HAVE_LO;\r
1076         }\r
1077       }\r
1078 \r
1079       code2 &= ~0xfe000000;\r
1080       code2 -=  0x00780000; // Get X coordinate + 8 in upper 16 bits\r
1081       pd[1] = code2;\r
1082 \r
1083       // Find next sprite\r
1084       link=(sprite[0]>>16)&0x7f;\r
1085       if (!link) break; // End of sprites\r
1086     }\r
1087   }\r
1088   else\r
1089   {\r
1090     for (u = 0; u < max_lines; u++)\r
1091       *((int *)&HighLnSpr[u][0]) = 0;\r
1092 \r
1093     for (u = 0; u < max_sprites; u++)\r
1094     {\r
1095       unsigned int *sprite;\r
1096       int code, code2, sx, sy, hv, height, width;\r
1097 \r
1098       sprite=(unsigned int *)(PicoMem.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
1099 \r
1100       // parse sprite info\r
1101       code = sprite[0];\r
1102       sy = (code&0x1ff)-0x80;\r
1103       hv = (code>>24)&0xf;\r
1104       height = (hv&3)+1;\r
1105 \r
1106       width  = (hv>>2)+1;\r
1107       code2 = sprite[1];\r
1108       sx = (code2>>16)&0x1ff;\r
1109       sx -= 0x78; // Get X coordinate + 8\r
1110 \r
1111       if (sy < max_lines && sy + (height<<3) > est->DrawScanline) // sprite onscreen (y)?\r
1112       {\r
1113         int entry, y, sx_min, onscr_x, maybe_op = 0;\r
1114 \r
1115         sx_min = 8-(width<<3);\r
1116         onscr_x = sx_min < sx && sx < max_width;\r
1117         if (sh && (code2 & 0x6000) == 0x6000)\r
1118           maybe_op = SPRL_MAY_HAVE_OP;\r
1119 \r
1120         entry = ((pd - HighPreSpr) / 2) | ((code2>>8)&0x80);\r
1121         y = (sy >= est->DrawScanline) ? sy : est->DrawScanline;\r
1122         for (; y < sy + (height<<3) && y < max_lines; y++)\r
1123         {\r
1124           unsigned char *p = &HighLnSpr[y][0];\r
1125           int cnt = p[0];\r
1126           if (cnt >= max_line_sprites) continue;              // sprite limit?\r
1127 \r
1128           if (p[2] >= max_line_sprites*2) {        // tile limit?\r
1129             p[0] |= 0x80;\r
1130             continue;\r
1131           }\r
1132           p[2] += width;\r
1133 \r
1134           if (sx == -0x78) {\r
1135             if (cnt > 0)\r
1136               p[0] |= 0x80; // masked, no more sprites for this line\r
1137             continue;\r
1138           }\r
1139           // must keep the first sprite even if it's offscreen, for masking\r
1140           if (cnt > 0 && !onscr_x) continue; // offscreen x\r
1141 \r
1142           p[3+cnt] = entry;\r
1143           p[0] = cnt + 1;\r
1144           p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO;\r
1145           p[1] |= maybe_op; // there might be op sprites on this line\r
1146           if (cnt > 0 && (code2 & 0x8000) && !(p[3+cnt-1]&0x80))\r
1147             p[1] |= SPRL_LO_ABOVE_HI;\r
1148         }\r
1149       }\r
1150 \r
1151       *pd++ = (width<<28)|(height<<24)|(hv<<16)|((unsigned short)sy);\r
1152       *pd++ = (sx<<16)|((unsigned short)code2);\r
1153 \r
1154       // Find next sprite\r
1155       link=(code>>16)&0x7f;\r
1156       if (!link) break; // End of sprites\r
1157     }\r
1158     *pd = 0;\r
1159 \r
1160 #if 0\r
1161     for (u = 0; u < max_lines; u++)\r
1162     {\r
1163       int y;\r
1164       printf("c%03i: %2i, %2i: ", u, HighLnSpr[u][0] & 0x7f, HighLnSpr[u][2]);\r
1165       for (y = 0; y < HighLnSpr[u][0] & 0x7f; y++)\r
1166         printf(" %i", HighLnSpr[u][y+3]);\r
1167       printf("\n");\r
1168     }\r
1169 #endif\r
1170   }\r
1171 }\r
1172 \r
1173 #ifndef _ASM_DRAW_C\r
1174 static void DrawAllSprites(unsigned char *sprited, int prio, int sh,\r
1175                            struct PicoEState *est)\r
1176 {\r
1177   unsigned char *p;\r
1178   int cnt;\r
1179 \r
1180   cnt = sprited[0] & 0x7f;\r
1181   if (cnt == 0) return;\r
1182 \r
1183   p = &sprited[3];\r
1184 \r
1185   // Go through sprites backwards:\r
1186   for (cnt--; cnt >= 0; cnt--)\r
1187   {\r
1188     int offs;\r
1189     if ((p[cnt] >> 7) != prio) continue;\r
1190     offs = (p[cnt]&0x7f) * 2;\r
1191     DrawSprite(HighPreSpr + offs, sh);\r
1192   }\r
1193 }\r
1194 \r
1195 \r
1196 // --------------------------------------------\r
1197 \r
1198 void BackFill(int reg7, int sh, struct PicoEState *est)\r
1199 {\r
1200   unsigned int back;\r
1201 \r
1202   // Start with a blank scanline (background colour):\r
1203   back=reg7&0x3f;\r
1204   back|=sh<<6;\r
1205   back|=back<<8;\r
1206   back|=back<<16;\r
1207 \r
1208   memset32((int *)(est->HighCol+8), back, 320/4);\r
1209 }\r
1210 #endif\r
1211 \r
1212 // --------------------------------------------\r
1213 \r
1214 #ifndef _ASM_DRAW_C\r
1215 void PicoDoHighPal555(int sh, int line, struct PicoEState *est)\r
1216 {\r
1217   unsigned int *spal, *dpal;\r
1218   unsigned int t, i;\r
1219 \r
1220   Pico.m.dirtyPal = 0;\r
1221 \r
1222   spal = (void *)PicoMem.cram;\r
1223   dpal = (void *)est->HighPal;\r
1224 \r
1225   for (i = 0; i < 0x40 / 2; i++) {\r
1226     t = spal[i];\r
1227 #ifdef USE_BGR555\r
1228     t = ((t & 0x000e000e)<< 1) | ((t & 0x00e000e0)<<3) | ((t & 0x0e000e00)<<4);\r
1229 #else\r
1230     t = ((t & 0x000e000e)<<12) | ((t & 0x00e000e0)<<3) | ((t & 0x0e000e00)>>7);\r
1231 #endif\r
1232     // treat it like it was 4-bit per channel, since in s/h mode it somewhat is that.\r
1233     // otherwise intensity difference between this and s/h will be wrong\r
1234     t |= (t >> 4) & 0x08610861; // 0x18e318e3\r
1235     dpal[i] = t;\r
1236   }\r
1237 \r
1238   // norm: xxx0, sh: 0xxx, hi: 0xxx + 7\r
1239   if (sh)\r
1240   {\r
1241     // shadowed pixels\r
1242     for (i = 0; i < 0x40 / 2; i++)\r
1243       dpal[0x40/2 | i] = dpal[0xc0/2 | i] = (dpal[i] >> 1) & 0x738e738e;\r
1244     // hilighted pixels\r
1245     for (i = 0; i < 0x40 / 2; i++) {\r
1246       t = ((dpal[i] >> 1) & 0x738e738e) + 0x738e738e; // 0x7bef7bef;\r
1247       t |= (t >> 4) & 0x08610861;\r
1248       dpal[0x80/2 | i] = t;\r
1249     }\r
1250   }\r
1251 }\r
1252 \r
1253 void FinalizeLine555(int sh, int line, struct PicoEState *est)\r
1254 {\r
1255   unsigned short *pd=est->DrawLineDest;\r
1256   unsigned char  *ps=est->HighCol+8;\r
1257   unsigned short *pal=est->HighPal;\r
1258   int len;\r
1259 \r
1260   if (Pico.m.dirtyPal)\r
1261     PicoDoHighPal555(sh, line, est);\r
1262 \r
1263   if (Pico.video.reg[12]&1) {\r
1264     len = 320;\r
1265   } else {\r
1266     if (!(PicoOpt&POPT_DIS_32C_BORDER)) pd+=32;\r
1267     len = 256;\r
1268   }\r
1269 \r
1270   {\r
1271 #ifndef PSP\r
1272     int i, mask=0xff;\r
1273     if (!sh && (est->rendstatus & PDRAW_SPR_LO_ON_HI))\r
1274       mask=0x3f; // accurate sprites, upper bits are priority stuff\r
1275 \r
1276     for (i = 0; i < len; i++)\r
1277       pd[i] = pal[ps[i] & mask];\r
1278 #else\r
1279     extern void amips_clut(unsigned short *dst, unsigned char *src, unsigned short *pal, int count);\r
1280     extern void amips_clut_6bit(unsigned short *dst, unsigned char *src, unsigned short *pal, int count);\r
1281     if (!sh && (est->rendstatus & PDRAW_SPR_LO_ON_HI))\r
1282          amips_clut_6bit(pd, ps, pal, len);\r
1283     else amips_clut(pd, ps, pal, len);\r
1284 #endif\r
1285   }\r
1286 }\r
1287 #endif\r
1288 \r
1289 static void FinalizeLine8bit(int sh, int line, struct PicoEState *est)\r
1290 {\r
1291   unsigned char *pd = est->DrawLineDest;\r
1292   int len, rs = est->rendstatus;\r
1293   static int dirty_count;\r
1294 \r
1295   if (!sh && Pico.m.dirtyPal == 1)\r
1296   {\r
1297     // a hack for mid-frame palette changes\r
1298     if (!(rs & PDRAW_SONIC_MODE))\r
1299          dirty_count = 1;\r
1300     else dirty_count++;\r
1301     rs |= PDRAW_SONIC_MODE;\r
1302     est->rendstatus = rs;\r
1303     if (dirty_count == 3) {\r
1304       blockcpy(est->HighPal, PicoMem.cram, 0x40*2);\r
1305     } else if (dirty_count == 11) {\r
1306       blockcpy(est->HighPal+0x40, PicoMem.cram, 0x40*2);\r
1307     }\r
1308   }\r
1309 \r
1310   if (Pico.video.reg[12]&1) {\r
1311     len = 320;\r
1312   } else {\r
1313     if (!(PicoOpt & POPT_DIS_32C_BORDER))\r
1314       pd += 32;\r
1315     len = 256;\r
1316   }\r
1317 \r
1318   if (!sh && (rs & PDRAW_SONIC_MODE)) {\r
1319     if (dirty_count >= 11) {\r
1320       blockcpy_or(pd, est->HighCol+8, len, 0x80);\r
1321     } else {\r
1322       blockcpy_or(pd, est->HighCol+8, len, 0x40);\r
1323     }\r
1324   } else {\r
1325     blockcpy(pd, est->HighCol+8, len);\r
1326   }\r
1327 }\r
1328 \r
1329 static void (*FinalizeLine)(int sh, int line, struct PicoEState *est);\r
1330 \r
1331 // --------------------------------------------\r
1332 \r
1333 static int DrawDisplay(int sh)\r
1334 {\r
1335   struct PicoEState *est=&Pico.est;\r
1336   unsigned char *sprited = &HighLnSpr[est->DrawScanline][0];\r
1337   struct PicoVideo *pvid=&Pico.video;\r
1338   int win=0, edge=0, hvwind=0, lflags;\r
1339   int maxw, maxcells;\r
1340 \r
1341   if (est->rendstatus & (PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES)) {\r
1342     // elprintf(EL_STATUS, "PrepareSprites(%i)", (est->rendstatus>>4)&1);\r
1343     PrepareSprites(est->rendstatus & PDRAW_DIRTY_SPRITES);\r
1344     est->rendstatus &= ~(PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES);\r
1345   }\r
1346 \r
1347   est->rendstatus &= ~(PDRAW_SHHI_DONE|PDRAW_PLANE_HI_PRIO);\r
1348 \r
1349   if (pvid->reg[12]&1) {\r
1350     maxw = 328; maxcells = 40;\r
1351   } else {\r
1352     maxw = 264; maxcells = 32;\r
1353   }\r
1354 \r
1355   // Find out if the window is on this line:\r
1356   win=pvid->reg[0x12];\r
1357   edge=(win&0x1f)<<3;\r
1358 \r
1359   if (win&0x80) { if (est->DrawScanline>=edge) hvwind=1; }\r
1360   else          { if (est->DrawScanline< edge) hvwind=1; }\r
1361 \r
1362   if (!hvwind) // we might have a vertical window here\r
1363   {\r
1364     win=pvid->reg[0x11];\r
1365     edge=win&0x1f;\r
1366     if (win&0x80) {\r
1367       if (!edge) hvwind=1;\r
1368       else if(edge < (maxcells>>1)) hvwind=2;\r
1369     } else {\r
1370       if (!edge);\r
1371       else if(edge < (maxcells>>1)) hvwind=2;\r
1372       else hvwind=1;\r
1373     }\r
1374   }\r
1375 \r
1376   /* - layer B low - */\r
1377   if (!(pvid->debug_p & PVD_KILL_B)) {\r
1378     lflags = LF_PLANE_1 | (sh << 1);\r
1379     if (pvid->debug_p & PVD_FORCE_B)\r
1380       lflags |= LF_FORCE;\r
1381     DrawLayer(lflags, HighCacheB, 0, maxcells, est);\r
1382   }\r
1383   /* - layer A low - */\r
1384   lflags = 0 | (sh << 1);\r
1385   if (pvid->debug_p & PVD_FORCE_A)\r
1386     lflags |= LF_FORCE;\r
1387   if (pvid->debug_p & PVD_KILL_A)\r
1388     ;\r
1389   else if (hvwind == 1)\r
1390     DrawWindow(0, maxcells>>1, 0, sh, est);\r
1391   else if (hvwind == 2) {\r
1392     DrawLayer(lflags, HighCacheA, (win&0x80) ?    0 : edge<<1, (win&0x80) ?     edge<<1 : maxcells, est);\r
1393     DrawWindow(                   (win&0x80) ? edge :       0, (win&0x80) ? maxcells>>1 : edge, 0, sh, est);\r
1394   }\r
1395   else\r
1396     DrawLayer(lflags, HighCacheA, 0, maxcells, est);\r
1397   /* - sprites low - */\r
1398   if (pvid->debug_p & PVD_KILL_S_LO)\r
1399     ;\r
1400   else if (est->rendstatus & PDRAW_INTERLACE)\r
1401     DrawAllSpritesInterlace(0, sh);\r
1402   else if (sprited[1] & SPRL_HAVE_LO)\r
1403     DrawAllSprites(sprited, 0, sh, est);\r
1404 \r
1405   /* - layer B hi - */\r
1406   if (!(pvid->debug_p & PVD_KILL_B) && HighCacheB[0])\r
1407     DrawTilesFromCache(HighCacheB, sh, maxw, est);\r
1408   /* - layer A hi - */\r
1409   if (pvid->debug_p & PVD_KILL_A)\r
1410     ;\r
1411   else if (hvwind == 1)\r
1412     DrawWindow(0, maxcells>>1, 1, sh, est);\r
1413   else if (hvwind == 2) {\r
1414     if (HighCacheA[0])\r
1415       DrawTilesFromCache(HighCacheA, sh, (win&0x80) ? edge<<4 : maxw, est);\r
1416     DrawWindow((win&0x80) ? edge : 0, (win&0x80) ? maxcells>>1 : edge, 1, sh, est);\r
1417   } else\r
1418     if (HighCacheA[0])\r
1419       DrawTilesFromCache(HighCacheA, sh, maxw, est);\r
1420   /* - sprites hi - */\r
1421   if (pvid->debug_p & PVD_KILL_S_HI)\r
1422     ;\r
1423   else if (est->rendstatus & PDRAW_INTERLACE)\r
1424     DrawAllSpritesInterlace(1, sh);\r
1425   // have sprites without layer pri bit ontop of sprites with that bit\r
1426   else if ((sprited[1] & 0xd0) == 0xd0 && (PicoOpt & POPT_ACC_SPRITES))\r
1427     DrawSpritesHiAS(sprited, sh);\r
1428   else if (sh && (sprited[1] & SPRL_MAY_HAVE_OP))\r
1429     DrawSpritesSHi(sprited, est);\r
1430   else if (sprited[1] & SPRL_HAVE_HI)\r
1431     DrawAllSprites(sprited, 1, 0, est);\r
1432 \r
1433   if (pvid->debug_p & PVD_FORCE_B)\r
1434     DrawTilesFromCacheForced(HighCacheB);\r
1435   else if (pvid->debug_p & PVD_FORCE_A)\r
1436     DrawTilesFromCacheForced(HighCacheA);\r
1437 \r
1438 #if 0\r
1439   {\r
1440     int *c, a, b;\r
1441     for (a = 0, c = HighCacheA; *c; c++, a++);\r
1442     for (b = 0, c = HighCacheB; *c; c++, b++);\r
1443     printf("%i:%03i: a=%i, b=%i\n", Pico.m.frame_count,\r
1444            Pico.est.DrawScanline, a, b);\r
1445   }\r
1446 #endif\r
1447 \r
1448   return 0;\r
1449 }\r
1450 \r
1451 // MUST be called every frame\r
1452 PICO_INTERNAL void PicoFrameStart(void)\r
1453 {\r
1454   int offs = 8, lines = 224;\r
1455 \r
1456   // prepare to do this frame\r
1457   Pico.est.rendstatus = 0;\r
1458   if ((Pico.video.reg[12] & 6) == 6)\r
1459     Pico.est.rendstatus |= PDRAW_INTERLACE; // interlace mode\r
1460   if (!(Pico.video.reg[12] & 1))\r
1461     Pico.est.rendstatus |= PDRAW_32_COLS;\r
1462   if (Pico.video.reg[1] & 8) {\r
1463     offs = 0;\r
1464     lines = 240;\r
1465   }\r
1466 \r
1467   if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {\r
1468     rendlines = lines;\r
1469     // mode_change() might reset rendstatus_old by calling SetColorFormat\r
1470     emu_video_mode_change((lines == 240) ? 0 : 8,\r
1471       lines, (Pico.video.reg[12] & 1) ? 0 : 1);\r
1472     rendstatus_old = Pico.est.rendstatus;\r
1473   }\r
1474 \r
1475   Pico.est.HighCol = HighColBase + offs * HighColIncrement;\r
1476   Pico.est.DrawLineDest = (char *)DrawLineDestBase + offs * DrawLineDestIncrement;\r
1477   Pico.est.DrawScanline = 0;\r
1478   skip_next_line = 0;\r
1479 \r
1480   if (PicoOpt & POPT_ALT_RENDERER)\r
1481     return;\r
1482 \r
1483   if (Pico.m.dirtyPal)\r
1484     Pico.m.dirtyPal = 2; // reset dirty if needed\r
1485   PrepareSprites(1);\r
1486 }\r
1487 \r
1488 static void DrawBlankedLine(int line, int offs, int sh, int bgc)\r
1489 {\r
1490   if (PicoScanBegin != NULL)\r
1491     PicoScanBegin(line + offs);\r
1492 \r
1493   BackFill(bgc, sh, &Pico.est);\r
1494 \r
1495   if (FinalizeLine != NULL)\r
1496     FinalizeLine(sh, line, &Pico.est);\r
1497 \r
1498   if (PicoScanEnd != NULL)\r
1499     PicoScanEnd(line + offs);\r
1500 \r
1501   Pico.est.HighCol += HighColIncrement;\r
1502   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;\r
1503 }\r
1504 \r
1505 static void PicoLine(int line, int offs, int sh, int bgc)\r
1506 {\r
1507   int skip = 0;\r
1508 \r
1509   if (skip_next_line > 0) {\r
1510     skip_next_line--;\r
1511     return;\r
1512   }\r
1513 \r
1514   Pico.est.DrawScanline = line;\r
1515   if (PicoScanBegin != NULL)\r
1516     skip = PicoScanBegin(line + offs);\r
1517 \r
1518   if (skip) {\r
1519     skip_next_line = skip - 1;\r
1520     return;\r
1521   }\r
1522 \r
1523   if (Pico.video.debug_p & (PVD_FORCE_A | PVD_FORCE_B))\r
1524     bgc = 0x3f;\r
1525 \r
1526   // Draw screen:\r
1527   BackFill(bgc, sh, &Pico.est);\r
1528   if (Pico.video.reg[1]&0x40)\r
1529     DrawDisplay(sh);\r
1530 \r
1531   if (FinalizeLine != NULL)\r
1532     FinalizeLine(sh, line, &Pico.est);\r
1533 \r
1534   if (PicoScanEnd != NULL)\r
1535     skip_next_line = PicoScanEnd(line + offs);\r
1536 \r
1537   Pico.est.HighCol += HighColIncrement;\r
1538   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;\r
1539 }\r
1540 \r
1541 void PicoDrawSync(int to, int blank_last_line)\r
1542 {\r
1543   int line, offs = 0;\r
1544   int sh = (Pico.video.reg[0xC] & 8) >> 3; // shadow/hilight?\r
1545   int bgc = Pico.video.reg[7];\r
1546 \r
1547   pprof_start(draw);\r
1548 \r
1549   if (rendlines != 240) {\r
1550     offs = 8;\r
1551     if (to > 223)\r
1552       to = 223;\r
1553   }\r
1554 \r
1555   for (line = Pico.est.DrawScanline; line < to; line++)\r
1556     PicoLine(line, offs, sh, bgc);\r
1557 \r
1558   // last line\r
1559   if (line <= to)\r
1560   {\r
1561     if (blank_last_line)\r
1562          DrawBlankedLine(line, offs, sh, bgc);\r
1563     else PicoLine(line, offs, sh, bgc);\r
1564     line++;\r
1565   }\r
1566   Pico.est.DrawScanline = line;\r
1567 \r
1568   pprof_end(draw);\r
1569 }\r
1570 \r
1571 // also works for fast renderer\r
1572 void PicoDrawUpdateHighPal(void)\r
1573 {\r
1574   struct PicoEState *est = &Pico.est;\r
1575   int sh = (Pico.video.reg[0xC] & 8) >> 3; // shadow/hilight?\r
1576   if (PicoOpt & POPT_ALT_RENDERER)\r
1577     sh = 0; // no s/h support\r
1578 \r
1579   PicoDoHighPal555(sh, 0, &Pico.est);\r
1580   if (est->rendstatus & PDRAW_SONIC_MODE) {\r
1581     // FIXME?\r
1582     memcpy(est->HighPal + 0x40, est->HighPal, 0x40*2);\r
1583     memcpy(est->HighPal + 0x80, est->HighPal, 0x40*2);\r
1584   }\r
1585 }\r
1586 \r
1587 void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode)\r
1588 {\r
1589   switch (which)\r
1590   {\r
1591     case PDF_8BIT:\r
1592       FinalizeLine = FinalizeLine8bit;\r
1593       break;\r
1594 \r
1595     case PDF_RGB555:\r
1596       if ((PicoAHW & PAHW_32X) && use_32x_line_mode)\r
1597         FinalizeLine = FinalizeLine32xRGB555;\r
1598       else\r
1599         FinalizeLine = FinalizeLine555;\r
1600       break;\r
1601 \r
1602     default:\r
1603       FinalizeLine = NULL;\r
1604       break;\r
1605   }\r
1606   PicoDrawSetOutFormat32x(which, use_32x_line_mode);\r
1607   PicoDrawSetOutputMode4(which);\r
1608   rendstatus_old = -1;\r
1609 }\r
1610 \r
1611 // note: may be called on the middle of frame\r
1612 void PicoDrawSetOutBuf(void *dest, int increment)\r
1613 {\r
1614   DrawLineDestBase = dest;\r
1615   DrawLineDestIncrement = increment;\r
1616   Pico.est.DrawLineDest = DrawLineDestBase + Pico.est.DrawScanline * increment;\r
1617 }\r
1618 \r
1619 void PicoDrawSetInternalBuf(void *dest, int increment)\r
1620 {\r
1621   if (dest != NULL) {\r
1622     HighColBase = dest;\r
1623     HighColIncrement = increment;\r
1624     Pico.est.HighCol = HighColBase + Pico.est.DrawScanline * increment;\r
1625   }\r
1626   else {\r
1627     HighColBase = DefHighCol;\r
1628     HighColIncrement = 0;\r
1629   }\r
1630 }\r
1631 \r
1632 void PicoDrawSetCallbacks(int (*begin)(unsigned int num), int (*end)(unsigned int num))\r
1633 {\r
1634   PicoScanBegin = NULL;\r
1635   PicoScanEnd = NULL;\r
1636   PicoScan32xBegin = NULL;\r
1637   PicoScan32xEnd = NULL;\r
1638 \r
1639   if ((PicoAHW & PAHW_32X) && FinalizeLine != FinalizeLine32xRGB555) {\r
1640     PicoScan32xBegin = begin;\r
1641     PicoScan32xEnd = end;\r
1642   }\r
1643   else {\r
1644     PicoScanBegin = begin;\r
1645     PicoScanEnd = end;\r
1646   }\r
1647 }\r
1648 \r
1649 void PicoDrawInit(void)\r
1650 {\r
1651   Pico.est.DrawLineDest = DefOutBuff;\r
1652   Pico.est.HighCol = HighColBase;\r
1653   Pico.est.HighPreSpr = HighPreSpr;\r
1654   rendstatus_old = -1;\r
1655 }\r
1656 \r
1657 // vim:ts=2:sw=2:expandtab\r