sram handling refactored
[picodrive.git] / Pico / Draw.c
1 // This is part of Pico Library\r
2 \r
3 // (c) Copyright 2004 Dave, All rights reserved.\r
4 // (c) Copyright 2006 notaz, All rights reserved.\r
5 // Free for non-commercial use.\r
6 \r
7 // For commercial use, separate licencing terms must be obtained.\r
8 \r
9 \r
10 #include "PicoInt.h"\r
11 #ifndef __GNUC__\r
12 #pragma warning (disable:4706) // Disable assignment within conditional\r
13 #endif\r
14 \r
15 int (*PicoScan)(unsigned int num, void *data)=NULL;\r
16 \r
17 unsigned short DefOutBuff[320*2];\r
18 unsigned char  HighCol[8+320+8];\r
19 static int  HighCacheA[41+1];   // caches for high layers\r
20 static int  HighCacheB[41+1];\r
21 static int  HighCacheS[80+1];   // and sprites\r
22 static int  HighPreSpr[80*2+1]; // slightly preprocessed sprites\r
23 char HighSprZ[320+8+8]; // Z-buffer for accurate sprites and shadow/hilight mode\r
24                         // (if bit 7 == 0, sh caused by tile; if bit 6 == 0 pixel must be shadowed, else hilighted, if bit5 == 1)\r
25 // lsb->msb: moved sprites, not all window tiles use same priority, accurate sprites (copied from PicoOpt), interlace\r
26 //           dirty sprites, sonic mode, have layer with all hi prio tiles (mk3), layer sh/hi already processed\r
27 int rendstatus;\r
28 void *DrawLineDest=DefOutBuff; // pointer to dest buffer where to draw this line to\r
29 int Scanline=0; // Scanline\r
30 \r
31 static int SpriteBlocks;\r
32 //unsigned short ppt[] = { 0x0f11, 0x0ff1, 0x01f1, 0x011f, 0x01ff, 0x0f1f, 0x0f0e, 0x0e7c };\r
33 \r
34 struct TileStrip\r
35 {\r
36   int nametab; // Position in VRAM of name table (for this tile line)\r
37   int line;    // Line number in pixels 0x000-0x3ff within the virtual tilemap\r
38   int hscroll; // Horizontal scroll value in pixels for the line\r
39   int xmask;   // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap\r
40   int *hc;     // cache for high tile codes and their positions\r
41   int cells;   // cells (tiles) to draw (32 col mode doesn't need to update whole 320)\r
42 };\r
43 \r
44 // stuff available in asm:\r
45 #ifdef _ASM_DRAW_C\r
46 void DrawWindow(int tstart, int tend, int prio, int sh);\r
47 void BackFill(int reg7, int sh);\r
48 void DrawSprite(int *sprite, int **hc, int sh);\r
49 void DrawTilesFromCache(int *hc, int sh);\r
50 void DrawSpritesFromCache(int *hc, int sh);\r
51 void DrawLayer(int plane, int *hcache, int maxcells, int sh);\r
52 void FinalizeLineBGR444(int sh);\r
53 void FinalizeLineRGB555(int sh);\r
54 void blockcpy_or(void *dst, void *src, size_t n, int pat);\r
55 #else\r
56 // utility\r
57 void blockcpy_or(void *dst, void *src, size_t n, int pat)\r
58 {\r
59   unsigned char *pd = dst, *ps = src;\r
60   for (; n; n--)\r
61     *pd++ = (unsigned char) (*ps++ | pat);\r
62 }\r
63 #endif\r
64 \r
65 \r
66 static int TileNorm(int sx,int addr,int pal)\r
67 {\r
68   unsigned char *pd = HighCol+sx;\r
69   unsigned int pack=0; unsigned int t=0;\r
70 \r
71   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
72   if (pack)\r
73   {\r
74     t=pack&0x0000f000; if (t) pd[0]=(unsigned char)(pal|(t>>12));\r
75     t=pack&0x00000f00; if (t) pd[1]=(unsigned char)(pal|(t>> 8));\r
76     t=pack&0x000000f0; if (t) pd[2]=(unsigned char)(pal|(t>> 4));\r
77     t=pack&0x0000000f; if (t) pd[3]=(unsigned char)(pal|(t    ));\r
78     t=pack&0xf0000000; if (t) pd[4]=(unsigned char)(pal|(t>>28));\r
79     t=pack&0x0f000000; if (t) pd[5]=(unsigned char)(pal|(t>>24));\r
80     t=pack&0x00f00000; if (t) pd[6]=(unsigned char)(pal|(t>>20));\r
81     t=pack&0x000f0000; if (t) pd[7]=(unsigned char)(pal|(t>>16));\r
82     return 0;\r
83   }\r
84 \r
85   return 1; // Tile blank\r
86 }\r
87 \r
88 static int TileFlip(int sx,int addr,int pal)\r
89 {\r
90   unsigned char *pd = HighCol+sx;\r
91   unsigned int pack=0; unsigned int t=0;\r
92 \r
93   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
94   if (pack)\r
95   {\r
96     t=pack&0x000f0000; if (t) pd[0]=(unsigned char)(pal|(t>>16));\r
97     t=pack&0x00f00000; if (t) pd[1]=(unsigned char)(pal|(t>>20));\r
98     t=pack&0x0f000000; if (t) pd[2]=(unsigned char)(pal|(t>>24));\r
99     t=pack&0xf0000000; if (t) pd[3]=(unsigned char)(pal|(t>>28));\r
100     t=pack&0x0000000f; if (t) pd[4]=(unsigned char)(pal|(t    ));\r
101     t=pack&0x000000f0; if (t) pd[5]=(unsigned char)(pal|(t>> 4));\r
102     t=pack&0x00000f00; if (t) pd[6]=(unsigned char)(pal|(t>> 8));\r
103     t=pack&0x0000f000; if (t) pd[7]=(unsigned char)(pal|(t>>12));\r
104     return 0;\r
105   }\r
106   return 1; // Tile blank\r
107 }\r
108 \r
109 \r
110 // tile renderers for hacky operator sprite support\r
111 #define sh_pix(x) \\r
112   if(!t); \\r
113   else if(t==0xe) pd[x]=(unsigned char)((pd[x]&0x3f)|0x80); /* hilight */ \\r
114   else if(t==0xf) pd[x]=(unsigned char)((pd[x]&0x3f)|0xc0); /* shadow  */ \\r
115   else pd[x]=(unsigned char)(pal|t)\r
116 \r
117 #ifndef _ASM_DRAW_C\r
118 static int TileNormSH(int sx,int addr,int pal)\r
119 {\r
120   unsigned int pack=0; unsigned int t=0;\r
121   unsigned char *pd = HighCol+sx;\r
122 \r
123   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
124   if (pack)\r
125   {\r
126     t=(pack&0x0000f000)>>12; sh_pix(0);\r
127     t=(pack&0x00000f00)>> 8; sh_pix(1);\r
128     t=(pack&0x000000f0)>> 4; sh_pix(2);\r
129     t=(pack&0x0000000f)    ; sh_pix(3);\r
130     t=(pack&0xf0000000)>>28; sh_pix(4);\r
131     t=(pack&0x0f000000)>>24; sh_pix(5);\r
132     t=(pack&0x00f00000)>>20; sh_pix(6);\r
133     t=(pack&0x000f0000)>>16; sh_pix(7);\r
134     return 0;\r
135   }\r
136 \r
137   return 1; // Tile blank\r
138 }\r
139 \r
140 static int TileFlipSH(int sx,int addr,int pal)\r
141 {\r
142   unsigned int pack=0; unsigned int t=0;\r
143   unsigned char *pd = HighCol+sx;\r
144 \r
145   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
146   if (pack)\r
147   {\r
148     t=(pack&0x000f0000)>>16; sh_pix(0);\r
149     t=(pack&0x00f00000)>>20; sh_pix(1);\r
150     t=(pack&0x0f000000)>>24; sh_pix(2);\r
151     t=(pack&0xf0000000)>>28; sh_pix(3);\r
152     t=(pack&0x0000000f)    ; sh_pix(4);\r
153     t=(pack&0x000000f0)>> 4; sh_pix(5);\r
154     t=(pack&0x00000f00)>> 8; sh_pix(6);\r
155     t=(pack&0x0000f000)>>12; sh_pix(7);\r
156     return 0;\r
157   }\r
158   return 1; // Tile blank\r
159 }\r
160 #endif\r
161 \r
162 static int TileNormZ(int sx,int addr,int pal,int zval)\r
163 {\r
164   unsigned int pack=0; unsigned int t=0;\r
165   unsigned char *pd = HighCol+sx;\r
166   char *zb = HighSprZ+sx;\r
167   int collision = 0, zb_s;\r
168 \r
169   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
170   if (pack)\r
171   {\r
172     t=pack&0x0000f000; if(t) { zb_s=zb[0]; if(zb_s) collision=1; if(zval>zb_s) { pd[0]=(unsigned char)(pal|(t>>12)); zb[0]=(char)zval; } }\r
173     t=pack&0x00000f00; if(t) { zb_s=zb[1]; if(zb_s) collision=1; if(zval>zb_s) { pd[1]=(unsigned char)(pal|(t>> 8)); zb[1]=(char)zval; } }\r
174     t=pack&0x000000f0; if(t) { zb_s=zb[2]; if(zb_s) collision=1; if(zval>zb_s) { pd[2]=(unsigned char)(pal|(t>> 4)); zb[2]=(char)zval; } }\r
175     t=pack&0x0000000f; if(t) { zb_s=zb[3]; if(zb_s) collision=1; if(zval>zb_s) { pd[3]=(unsigned char)(pal|(t    )); zb[3]=(char)zval; } }\r
176     t=pack&0xf0000000; if(t) { zb_s=zb[4]; if(zb_s) collision=1; if(zval>zb_s) { pd[4]=(unsigned char)(pal|(t>>28)); zb[4]=(char)zval; } }\r
177     t=pack&0x0f000000; if(t) { zb_s=zb[5]; if(zb_s) collision=1; if(zval>zb_s) { pd[5]=(unsigned char)(pal|(t>>24)); zb[5]=(char)zval; } }\r
178     t=pack&0x00f00000; if(t) { zb_s=zb[6]; if(zb_s) collision=1; if(zval>zb_s) { pd[6]=(unsigned char)(pal|(t>>20)); zb[6]=(char)zval; } }\r
179     t=pack&0x000f0000; if(t) { zb_s=zb[7]; if(zb_s) collision=1; if(zval>zb_s) { pd[7]=(unsigned char)(pal|(t>>16)); zb[7]=(char)zval; } }\r
180     if(collision) Pico.video.status|=0x20;\r
181     return 0;\r
182   }\r
183 \r
184   return 1; // Tile blank\r
185 }\r
186 \r
187 static int TileFlipZ(int sx,int addr,int pal,int zval)\r
188 {\r
189   unsigned int pack=0; unsigned int t=0;\r
190   unsigned char *pd = HighCol+sx;\r
191   char *zb = HighSprZ+sx;\r
192   int collision = 0, zb_s;\r
193 \r
194   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
195   if (pack)\r
196   {\r
197     t=pack&0x000f0000; if(t) { zb_s=zb[0]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[0]=(unsigned char)(pal|(t>>16)); zb[0]=(char)zval; } }\r
198     t=pack&0x00f00000; if(t) { zb_s=zb[1]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[1]=(unsigned char)(pal|(t>>20)); zb[1]=(char)zval; } }\r
199     t=pack&0x0f000000; if(t) { zb_s=zb[2]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[2]=(unsigned char)(pal|(t>>24)); zb[2]=(char)zval; } }\r
200     t=pack&0xf0000000; if(t) { zb_s=zb[3]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[3]=(unsigned char)(pal|(t>>28)); zb[3]=(char)zval; } }\r
201     t=pack&0x0000000f; if(t) { zb_s=zb[4]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[4]=(unsigned char)(pal|(t    )); zb[4]=(char)zval; } }\r
202     t=pack&0x000000f0; if(t) { zb_s=zb[5]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[5]=(unsigned char)(pal|(t>> 4)); zb[5]=(char)zval; } }\r
203     t=pack&0x00000f00; if(t) { zb_s=zb[6]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[6]=(unsigned char)(pal|(t>> 8)); zb[6]=(char)zval; } }\r
204     t=pack&0x0000f000; if(t) { zb_s=zb[7]&0x1f; if(zb_s) collision=1; if(zval>zb_s) { pd[7]=(unsigned char)(pal|(t>>12)); zb[7]=(char)zval; } }\r
205     if(collision) Pico.video.status|=0x20;\r
206     return 0;\r
207   }\r
208   return 1; // Tile blank\r
209 }\r
210 \r
211 \r
212 #define sh_pixZ(x) \\r
213   if(t) { \\r
214     if(zb[x]) collision=1; \\r
215     if(zval>zb[x]) { \\r
216       if     (t==0xe) { pd[x]=(unsigned char)((pd[x]&0x3f)|0x80); /* hilight */ } \\r
217       else if(t==0xf) { pd[x]=(unsigned char)((pd[x]&0x3f)|0xc0); /* shadow  */ } \\r
218       else            { zb[x]=(char)zval; pd[x]=(unsigned char)(pal|t); } \\r
219     } \\r
220   }\r
221 \r
222 static int TileNormZSH(int sx,int addr,int pal,int zval)\r
223 {\r
224   unsigned int pack=0; unsigned int t=0;\r
225   unsigned char *pd = HighCol+sx;\r
226   char *zb = HighSprZ+sx;\r
227   int collision = 0;\r
228 \r
229   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
230   if (pack)\r
231   {\r
232     t=(pack&0x0000f000)>>12; sh_pixZ(0);\r
233     t=(pack&0x00000f00)>> 8; sh_pixZ(1);\r
234     t=(pack&0x000000f0)>> 4; sh_pixZ(2);\r
235     t=(pack&0x0000000f)    ; sh_pixZ(3);\r
236     t=(pack&0xf0000000)>>28; sh_pixZ(4);\r
237     t=(pack&0x0f000000)>>24; sh_pixZ(5);\r
238     t=(pack&0x00f00000)>>20; sh_pixZ(6);\r
239     t=(pack&0x000f0000)>>16; sh_pixZ(7);\r
240     if(collision) Pico.video.status|=0x20;\r
241     return 0;\r
242   }\r
243 \r
244   return 1; // Tile blank\r
245 }\r
246 \r
247 static int TileFlipZSH(int sx,int addr,int pal,int zval)\r
248 {\r
249   unsigned int pack=0; unsigned int t=0;\r
250   unsigned char *pd = HighCol+sx;\r
251   char *zb = HighSprZ+sx;\r
252   int collision = 0;\r
253 \r
254   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
255   if (pack)\r
256   {\r
257     t=(pack&0x000f0000)>>16; sh_pixZ(0);\r
258     t=(pack&0x00f00000)>>20; sh_pixZ(1);\r
259     t=(pack&0x0f000000)>>24; sh_pixZ(2);\r
260     t=(pack&0xf0000000)>>28; sh_pixZ(3);\r
261     t=(pack&0x0000000f)    ; sh_pixZ(4);\r
262     t=(pack&0x000000f0)>> 4; sh_pixZ(5);\r
263     t=(pack&0x00000f00)>> 8; sh_pixZ(6);\r
264     t=(pack&0x0000f000)>>12; sh_pixZ(7);\r
265     if(collision) Pico.video.status|=0x20;\r
266     return 0;\r
267   }\r
268   return 1; // Tile blank\r
269 }\r
270 \r
271 // --------------------------------------------\r
272 \r
273 #ifndef _ASM_DRAW_C\r
274 static void DrawStrip(struct TileStrip *ts, int sh)\r
275 {\r
276   int tilex=0,dx=0,ty=0,code=0,addr=0,cells;\r
277   int oldcode=-1,blank=-1; // The tile we know is blank\r
278   int pal=0;\r
279 \r
280   // Draw tiles across screen:\r
281   tilex=(-ts->hscroll)>>3;\r
282   ty=(ts->line&7)<<1; // Y-Offset into tile\r
283   dx=((ts->hscroll-1)&7)+1;\r
284   cells = ts->cells;\r
285   if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
286 \r
287   for (; cells; dx+=8,tilex++,cells--)\r
288   {\r
289     int zero=0;\r
290 \r
291     code=Pico.vram[ts->nametab+(tilex&ts->xmask)];\r
292     if (code==blank) continue;\r
293     if (code>>15) { // high priority tile\r
294       int cval = code | (dx<<16) | (ty<<25);\r
295       if(code&0x1000) cval^=7<<26;\r
296       *ts->hc++ = cval; // cache it\r
297       continue;\r
298     }\r
299 \r
300     if (code!=oldcode) {\r
301       oldcode = code;\r
302       // Get tile address/2:\r
303       addr=(code&0x7ff)<<4;\r
304       addr+=ty;\r
305       if (code&0x1000) addr^=0xe; // Y-flip\r
306 \r
307 //      pal=Pico.cram+((code>>9)&0x30);\r
308       pal=((code>>9)&0x30)|(sh<<6);\r
309     }\r
310 \r
311     if (code&0x0800) zero=TileFlip(dx,addr,pal);\r
312     else             zero=TileNorm(dx,addr,pal);\r
313 \r
314     if (zero) blank=code; // We know this tile is blank now\r
315   }\r
316 \r
317   // terminate the cache list\r
318   *ts->hc = 0;\r
319   // if oldcode wasn't changed, it means all layer is hi priority\r
320   if (oldcode == -1) rendstatus|=0x40;\r
321 }\r
322 \r
323 // this is messy\r
324 void DrawStripVSRam(struct TileStrip *ts, int plane)\r
325 {\r
326   int tilex=0,dx=0,ty=0,code=0,addr=0,cell=0,nametabadd=0;\r
327   int oldcode=-1,blank=-1; // The tile we know is blank\r
328   int pal=0,scan=Scanline;\r
329 \r
330   // Draw tiles across screen:\r
331   tilex=(-ts->hscroll)>>3;\r
332   dx=((ts->hscroll-1)&7)+1;\r
333   if(dx != 8) {\r
334     int vscroll, line;\r
335     cell--; // have hscroll, start with negative cell\r
336     // also calculate intial VS stuff\r
337     vscroll=Pico.vsram[plane];\r
338 \r
339     // Find the line in the name table\r
340     line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
341     nametabadd=(line>>3)<<(ts->line>>24);    // .. and shift[width]\r
342     ty=(line&7)<<1; // Y-Offset into tile\r
343   }\r
344 \r
345   for (; cell < ts->cells; dx+=8,tilex++,cell++)\r
346   {\r
347     int zero=0;\r
348 \r
349     if((cell&1)==0) {\r
350       int line,vscroll;\r
351       vscroll=Pico.vsram[plane+(cell&~1)];\r
352 \r
353       // Find the line in the name table\r
354       line=(vscroll+scan)&ts->line&0xffff; // ts->line is really ymask ..\r
355       nametabadd=(line>>3)<<(ts->line>>24);    // .. and shift[width]\r
356       ty=(line&7)<<1; // Y-Offset into tile\r
357     }\r
358 \r
359     code=Pico.vram[ts->nametab+nametabadd+(tilex&ts->xmask)];\r
360     if (code==blank) continue;\r
361     if (code>>15) { // high priority tile\r
362       int cval = code | (dx<<16) | (ty<<25);\r
363       if(code&0x1000) cval^=7<<26;\r
364       *ts->hc++ = cval; // cache it\r
365       continue;\r
366     }\r
367 \r
368     if (code!=oldcode) {\r
369       oldcode = code;\r
370       // Get tile address/2:\r
371       addr=(code&0x7ff)<<4;\r
372       if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
373 \r
374 //      pal=Pico.cram+((code>>9)&0x30);\r
375       pal=((code>>9)&0x30);\r
376     }\r
377 \r
378     if (code&0x0800) zero=TileFlip(dx,addr,pal);\r
379     else             zero=TileNorm(dx,addr,pal);\r
380 \r
381     if (zero) blank=code; // We know this tile is blank now\r
382   }\r
383 \r
384   // terminate the cache list\r
385   *ts->hc = 0;\r
386   if (oldcode == -1) rendstatus|=0x40;\r
387 }\r
388 #endif\r
389 \r
390 #ifndef _ASM_DRAW_C\r
391 static\r
392 #endif\r
393 void DrawStripInterlace(struct TileStrip *ts)\r
394 {\r
395   int tilex=0,dx=0,ty=0,code=0,addr=0,cells;\r
396   int oldcode=-1,blank=-1; // The tile we know is blank\r
397   int pal=0;\r
398 \r
399   // Draw tiles across screen:\r
400   tilex=(-ts->hscroll)>>3;\r
401   ty=(ts->line&15)<<1; // Y-Offset into tile\r
402   dx=((ts->hscroll-1)&7)+1;\r
403   cells = ts->cells;\r
404   if(dx != 8) cells++; // have hscroll, need to draw 1 cell more\r
405 \r
406   for (; cells; dx+=8,tilex++,cells--)\r
407   {\r
408     int zero=0;\r
409 \r
410     code=Pico.vram[ts->nametab+(tilex&ts->xmask)];\r
411     if (code==blank) continue;\r
412     if (code>>15) { // high priority tile\r
413       int cval = (code&0xfc00) | (dx<<16) | (ty<<25);\r
414       cval|=(code&0x3ff)<<1;\r
415       if(code&0x1000) cval^=0xf<<26;\r
416       *ts->hc++ = cval; // cache it\r
417       continue;\r
418     }\r
419 \r
420     if (code!=oldcode) {\r
421       oldcode = code;\r
422       // Get tile address/2:\r
423       addr=(code&0x7ff)<<5;\r
424       if (code&0x1000) addr+=30-ty; else addr+=ty; // Y-flip\r
425 \r
426 //      pal=Pico.cram+((code>>9)&0x30);\r
427       pal=((code>>9)&0x30);\r
428     }\r
429 \r
430     if (code&0x0800) zero=TileFlip(dx,addr,pal);\r
431     else             zero=TileNorm(dx,addr,pal);\r
432 \r
433     if (zero) blank=code; // We know this tile is blank now\r
434   }\r
435 \r
436   // terminate the cache list\r
437   *ts->hc = 0;\r
438 }\r
439 \r
440 // --------------------------------------------\r
441 \r
442 #ifndef _ASM_DRAW_C\r
443 static void DrawLayer(int plane, int *hcache, int maxcells, int sh)\r
444 {\r
445   struct PicoVideo *pvid=&Pico.video;\r
446   const char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps (2 is invalid)\r
447   struct TileStrip ts;\r
448   int width, height, ymask;\r
449   int vscroll, htab;\r
450 \r
451   ts.hc=hcache;\r
452   ts.cells=maxcells;\r
453 \r
454   // Work out the TileStrip to draw\r
455 \r
456   // Work out the name table size: 32 64 or 128 tiles (0-3)\r
457   width=pvid->reg[16];\r
458   height=(width>>4)&3; width&=3;\r
459 \r
460   ts.xmask=(1<<shift[width])-1; // X Mask in tiles (0x1f-0x7f)\r
461   ymask=(height<<8)|0xff;       // Y Mask in pixels\r
462   if(width == 1)   ymask&=0x1ff;\r
463   else if(width>1) ymask =0x0ff;\r
464 \r
465   // Find name table:\r
466   if (plane==0) ts.nametab=(pvid->reg[2]&0x38)<< 9; // A\r
467   else          ts.nametab=(pvid->reg[4]&0x07)<<12; // B\r
468 \r
469   htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
470   if ( pvid->reg[11]&2)     htab+=Scanline<<1; // Offset by line\r
471   if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile\r
472   htab+=plane; // A or B\r
473 \r
474   // Get horizontal scroll value, will be masked later\r
475   ts.hscroll=Pico.vram[htab&0x7fff];\r
476 \r
477   if((pvid->reg[12]&6) == 6) {\r
478     // interlace mode 2\r
479     vscroll=Pico.vsram[plane]; // Get vertical scroll value\r
480 \r
481     // Find the line in the name table\r
482     ts.line=(vscroll+(Scanline<<1))&((ymask<<1)|1);\r
483     ts.nametab+=(ts.line>>4)<<shift[width];\r
484 \r
485     DrawStripInterlace(&ts);\r
486   } else if( pvid->reg[11]&4) {\r
487     // shit, we have 2-cell column based vscroll\r
488     // luckily this doesn't happen too often\r
489     ts.line=ymask|(shift[width]<<24); // save some stuff instead of line\r
490     DrawStripVSRam(&ts, plane);\r
491   } else {\r
492     vscroll=Pico.vsram[plane]; // Get vertical scroll value\r
493 \r
494     // Find the line in the name table\r
495     ts.line=(vscroll+Scanline)&ymask;\r
496     ts.nametab+=(ts.line>>3)<<shift[width];\r
497 \r
498     DrawStrip(&ts, sh);\r
499   }\r
500 }\r
501 \r
502 \r
503 // --------------------------------------------\r
504 \r
505 // tstart & tend are tile pair numbers\r
506 static void DrawWindow(int tstart, int tend, int prio, int sh) // int *hcache\r
507 {\r
508   struct PicoVideo *pvid=&Pico.video;\r
509   int tilex=0,ty=0,nametab,code=0;\r
510   int blank=-1; // The tile we know is blank\r
511 \r
512   // Find name table line:\r
513   if (pvid->reg[12]&1)\r
514   {\r
515     nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode\r
516     nametab+=(Scanline>>3)<<6;\r
517   }\r
518   else\r
519   {\r
520     nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode\r
521     nametab+=(Scanline>>3)<<5;\r
522   }\r
523 \r
524   tilex=tstart<<1;\r
525   tend<<=1;\r
526 \r
527   ty=(Scanline&7)<<1; // Y-Offset into tile\r
528 \r
529   if(!(rendstatus&2)) {\r
530     // check the first tile code\r
531     code=Pico.vram[nametab+tilex];\r
532     // if the whole window uses same priority (what is often the case), we may be able to skip this field\r
533     if((code>>15) != prio) return;\r
534   }\r
535 \r
536   // Draw tiles across screen:\r
537   for (; tilex < tend; tilex++)\r
538   {\r
539     int addr=0,zero=0;\r
540     int pal;\r
541 \r
542     code=Pico.vram[nametab+tilex];\r
543     if(code==blank) continue;\r
544     if((code>>15) != prio) {\r
545       rendstatus|=2;\r
546       continue;\r
547     }\r
548 \r
549     pal=((code>>9)&0x30);\r
550 \r
551     if(sh) {\r
552       int tmp, *zb = (int *)(HighCol+8+(tilex<<3));\r
553       if(prio) {\r
554         tmp = *zb;\r
555         if(!(tmp&0x00000080)) tmp&=~0x000000c0; if(!(tmp&0x00008000)) tmp&=~0x0000c000;\r
556         if(!(tmp&0x00800000)) tmp&=~0x00c00000; if(!(tmp&0x80000000)) tmp&=~0xc0000000;\r
557         *zb++=tmp; tmp = *zb;\r
558         if(!(tmp&0x00000080)) tmp&=~0x000000c0; if(!(tmp&0x00008000)) tmp&=~0x0000c000;\r
559         if(!(tmp&0x00800000)) tmp&=~0x00c00000; if(!(tmp&0x80000000)) tmp&=~0xc0000000;\r
560         *zb++=tmp;\r
561       } else {\r
562         pal |= 0x40;\r
563       }\r
564     }\r
565 \r
566     // Get tile address/2:\r
567     addr=(code&0x7ff)<<4;\r
568     if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
569 \r
570     if (code&0x0800) zero=TileFlip(8+(tilex<<3),addr,pal);\r
571     else             zero=TileNorm(8+(tilex<<3),addr,pal);\r
572 \r
573     if (zero) blank=code; // We know this tile is blank now\r
574   }\r
575 \r
576   // terminate the cache list\r
577   //*hcache = 0;\r
578 }\r
579 \r
580 // --------------------------------------------\r
581 \r
582 static void DrawTilesFromCache(int *hc, int sh)\r
583 {\r
584   int code, addr, dx;\r
585   int pal;\r
586 \r
587   // *ts->hc++ = code | (dx<<16) | (ty<<25); // cache it\r
588 \r
589   if (sh && (rendstatus&0xc0))\r
590   {\r
591     if (!(rendstatus&0x80))\r
592     {\r
593       // as some layer has covered whole line with hi priority tiles,\r
594       // we can process whole line and then act as if sh/hi mode was off.\r
595       rendstatus|=0x80;\r
596       int c = 320/4, *zb = (int *)(HighCol+8);\r
597       while (c--)\r
598       {\r
599         int tmp = *zb;\r
600         if (!(tmp & 0x80808080)) *zb=tmp&0x3f3f3f3f;\r
601         else {\r
602           if(!(tmp&0x00000080)) tmp&=~0x000000c0; if(!(tmp&0x00008000)) tmp&=~0x0000c000;\r
603           if(!(tmp&0x00800000)) tmp&=~0x00c00000; if(!(tmp&0x80000000)) tmp&=~0xc0000000;\r
604           *zb=tmp;\r
605         }\r
606         zb++;\r
607       }\r
608     }\r
609     sh = 0;\r
610   }\r
611 \r
612   if (sh)\r
613   {\r
614     while((code=*hc++)) {\r
615       unsigned char *zb;\r
616       // Get tile address/2:\r
617       addr=(code&0x7ff)<<4;\r
618       addr+=(unsigned int)code>>25; // y offset into tile\r
619       dx=(code>>16)&0x1ff;\r
620       zb = HighCol+dx;\r
621       if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++;\r
622       if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++;\r
623       if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++;\r
624       if(!(*zb&0x80)) *zb&=0x3f; zb++; if(!(*zb&0x80)) *zb&=0x3f; zb++;\r
625 \r
626       pal=((code>>9)&0x30);\r
627 \r
628       if (code&0x0800) TileFlip(dx,addr,pal);\r
629       else             TileNorm(dx,addr,pal);\r
630     }\r
631   }\r
632   else\r
633   {\r
634     short blank=-1; // The tile we know is blank\r
635     while((code=*hc++)) {\r
636       int zero;\r
637       if((short)code == blank) continue;\r
638       // Get tile address/2:\r
639       addr=(code&0x7ff)<<4;\r
640       addr+=(unsigned int)code>>25; // y offset into tile\r
641       dx=(code>>16)&0x1ff;\r
642 \r
643       pal=((code>>9)&0x30);\r
644 \r
645       if (code&0x0800) zero=TileFlip(dx,addr,pal);\r
646       else             zero=TileNorm(dx,addr,pal);\r
647 \r
648       if(zero) blank=(short)code;\r
649     }\r
650   }\r
651 }\r
652 \r
653 // --------------------------------------------\r
654 \r
655 // Index + 0  :    hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size\r
656 // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
657 \r
658 static void DrawSprite(int *sprite, int **hc, int sh)\r
659 {\r
660   int width=0,height=0;\r
661   int row=0,code=0;\r
662   int pal;\r
663   int tile=0,delta=0;\r
664   int sx, sy;\r
665   int (*fTileFunc)(int sx,int addr,int pal);\r
666 \r
667   // parse the sprite data\r
668   sy=sprite[0];\r
669   code=sprite[1];\r
670   sx=code>>16; // X\r
671   width=sy>>28;\r
672   height=(sy>>24)&7; // Width and height in tiles\r
673   sy=(sy<<16)>>16; // Y\r
674 \r
675   row=Scanline-sy; // Row of the sprite we are on\r
676 \r
677   if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
678 \r
679   tile=code&0x7ff; // Tile number\r
680   tile+=row>>3; // Tile number increases going down\r
681   delta=height; // Delta to increase tile by going right\r
682   if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
683 \r
684   tile<<=4; tile+=(row&7)<<1; // Tile address\r
685 \r
686   if(code&0x8000) { // high priority - cache it\r
687     *(*hc)++ = (tile<<16)|((code&0x0800)<<5)|((sx<<6)&0x0000ffc0)|((code>>9)&0x30)|((sprite[0]>>16)&0xf);\r
688   } else {\r
689     delta<<=4; // Delta of address\r
690     pal=((code>>9)&0x30)|(sh<<6);\r
691 \r
692     if(sh && (code&0x6000) == 0x6000) {\r
693       if(code&0x0800) fTileFunc=TileFlipSH;\r
694       else            fTileFunc=TileNormSH;\r
695     } else {\r
696       if(code&0x0800) fTileFunc=TileFlip;\r
697       else            fTileFunc=TileNorm;\r
698     }\r
699 \r
700     for (; width; width--,sx+=8,tile+=delta)\r
701     {\r
702       if(sx<=0)   continue;\r
703       if(sx>=328) break; // Offscreen\r
704 \r
705       tile&=0x7fff; // Clip tile address\r
706       fTileFunc(sx,tile,pal);\r
707     }\r
708   }\r
709 }\r
710 #endif\r
711 \r
712 \r
713 // Index + 0  :    hhhhvvvv s---hhvv yyyyyyyy yyyyyyyy // s: skip flag, h: horiz. size\r
714 // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
715 \r
716 static void DrawSpriteZ(int pack, int pack2, int shpri, int sprio)\r
717 {\r
718   int width=0,height=0;\r
719   int row=0;\r
720   int pal;\r
721   int tile=0,delta=0;\r
722   int sx, sy;\r
723   int (*fTileFunc)(int sx,int addr,int pal,int zval);\r
724 \r
725   // parse the sprite data\r
726   sx=pack2>>16; // X\r
727   sy=(pack <<16)>>16; // Y\r
728   width=pack>>28;\r
729   height=(pack>>24)&7; // Width and height in tiles\r
730 \r
731   row=Scanline-sy; // Row of the sprite we are on\r
732 \r
733   if (pack2&0x1000) row=(height<<3)-1-row; // Flip Y\r
734 \r
735   tile=pack2&0x7ff; // Tile number\r
736   tile+=row>>3; // Tile number increases going down\r
737   delta=height; // Delta to increase tile by going right\r
738   if (pack2&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
739 \r
740   tile<<=4; tile+=(row&7)<<1; // Tile address\r
741   delta<<=4; // Delta of address\r
742   pal=((pack2>>9)&0x30);\r
743   if((shpri&1)&&!(shpri&2)) pal|=0x40;\r
744 \r
745   shpri&=1;\r
746   if((pack2&0x6000) != 0x6000) shpri = 0;\r
747   shpri |= (pack2&0x0800)>>10;\r
748   switch(shpri) {\r
749     default:\r
750     case 0: fTileFunc=TileNormZ;   break;\r
751     case 1: fTileFunc=TileNormZSH; break;\r
752     case 2: fTileFunc=TileFlipZ;   break;\r
753     case 3: fTileFunc=TileFlipZSH; break;\r
754   }\r
755 \r
756   for (; width; width--,sx+=8,tile+=delta)\r
757   {\r
758     if(sx<=0)   continue;\r
759     if(sx>=328) break; // Offscreen\r
760 \r
761     tile&=0x7fff; // Clip tile address\r
762     fTileFunc(sx,tile,pal,sprio);\r
763   }\r
764 }\r
765 \r
766 static void DrawSpriteInterlace(unsigned int *sprite)\r
767 {\r
768   int width=0,height=0;\r
769   int row=0,code=0;\r
770   int pal;\r
771   int tile=0,delta=0;\r
772   int sx, sy;\r
773 \r
774   // parse the sprite data\r
775   sy=sprite[0];\r
776   height=sy>>24;\r
777   sy=(sy&0x3ff)-0x100; // Y\r
778   width=(height>>2)&3; height&=3;\r
779   width++; height++; // Width and height in tiles\r
780 \r
781   row=(Scanline<<1)-sy; // Row of the sprite we are on\r
782 \r
783   code=sprite[1];\r
784   sx=((code>>16)&0x1ff)-0x78; // X\r
785 \r
786   if (code&0x1000) row^=(16<<height)-1; // Flip Y\r
787 \r
788   tile=code&0x3ff; // Tile number\r
789   tile+=row>>4; // Tile number increases going down\r
790   delta=height; // Delta to increase tile by going right\r
791   if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
792 \r
793   tile<<=5; tile+=(row&15)<<1; // Tile address\r
794 \r
795   delta<<=5; // Delta of address\r
796   pal=((code>>9)&0x30); // Get palette pointer\r
797 \r
798   for (; width; width--,sx+=8,tile+=delta)\r
799   {\r
800     if(sx<=0)   continue;\r
801     if(sx>=328) break; // Offscreen\r
802 \r
803     tile&=0x7fff; // Clip tile address\r
804     if (code&0x0800) TileFlip(sx,tile,pal);\r
805     else             TileNorm(sx,tile,pal);\r
806   }\r
807 }\r
808 \r
809 \r
810 static void DrawAllSpritesInterlace(int pri, int maxwidth)\r
811 {\r
812   struct PicoVideo *pvid=&Pico.video;\r
813   int i,u,table,link=0,sline=Scanline<<1;\r
814   unsigned int *sprites[80]; // Sprite index\r
815 \r
816   table=pvid->reg[5]&0x7f;\r
817   if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
818   table<<=8; // Get sprite table address/2\r
819 \r
820   for (i=u=0; u < 80 && i < 21; u++)\r
821   {\r
822     unsigned int *sprite;\r
823     int code, sx, sy, height;\r
824 \r
825     sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
826 \r
827     // get sprite info\r
828     code = sprite[0];\r
829     sx = sprite[1];\r
830     if(((sx>>15)&1) != pri) goto nextsprite; // wrong priority sprite\r
831 \r
832     // check if it is on this line\r
833     sy = (code&0x3ff)-0x100;\r
834     height = (((code>>24)&3)+1)<<4;\r
835     if(sline < sy || sline >= sy+height) goto nextsprite; // no\r
836 \r
837     // check if sprite is not hidden offscreen\r
838     sx = (sx>>16)&0x1ff;\r
839     sx -= 0x78; // Get X coordinate + 8\r
840     if(sx <= -8*3 || sx >= maxwidth) goto nextsprite;\r
841 \r
842     // sprite is good, save it's pointer\r
843     sprites[i++]=sprite;\r
844 \r
845     nextsprite:\r
846     // Find next sprite\r
847     link=(code>>16)&0x7f;\r
848     if(!link) break; // End of sprites\r
849   }\r
850 \r
851   // Go through sprites backwards:\r
852   for (i-- ;i>=0; i--)\r
853     DrawSpriteInterlace(sprites[i]);\r
854 }\r
855 \r
856 \r
857 #ifndef _ASM_DRAW_C\r
858 static void DrawSpritesFromCache(int *hc, int sh)\r
859 {\r
860   int code, tile, sx, delta, width;\r
861   int pal;\r
862   int (*fTileFunc)(int sx,int addr,int pal);\r
863 \r
864   // *(*hc)++ = (tile<<16)|((code&0x0800)<<5)|((sx<<6)&0x0000ffc0)|((code>>9)&0x30)|((sprite[0]>>24)&0xf);\r
865 \r
866   while((code=*hc++)) {\r
867     pal=(code&0x30);\r
868     delta=code&0xf;\r
869     width=delta>>2; delta&=3;\r
870     width++; delta++; // Width and height in tiles\r
871     if (code&0x10000) delta=-delta; // Flip X\r
872     delta<<=4;\r
873     tile=((unsigned int)code>>17)<<1;\r
874     sx=(code<<16)>>22; // sx can be negative (start offscreen), so sign extend\r
875 \r
876     if(sh && pal == 0x30) { //\r
877       if(code&0x10000) fTileFunc=TileFlipSH;\r
878       else             fTileFunc=TileNormSH;\r
879     } else {\r
880       if(code&0x10000) fTileFunc=TileFlip;\r
881       else             fTileFunc=TileNorm;\r
882     }\r
883 \r
884     for (; width; width--,sx+=8,tile+=delta)\r
885     {\r
886       if(sx<=0)   continue;\r
887       if(sx>=328) break; // Offscreen\r
888 \r
889       tile&=0x7fff; // Clip tile address\r
890       fTileFunc(sx,tile,pal);\r
891     }\r
892   }\r
893 }\r
894 #endif\r
895 \r
896 \r
897 // Index + 0  :    ----hhvv -lllllll -------y yyyyyyyy\r
898 // Index + 4  :    -------x xxxxxxxx pccvhnnn nnnnnnnn\r
899 // v\r
900 // Index + 0  :    hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size\r
901 // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
902 \r
903 static void PrepareSprites(int full)\r
904 {\r
905   struct PicoVideo *pvid=&Pico.video;\r
906   int u=0,link=0,sblocks=0;\r
907   int table=0;\r
908   int *pd = HighPreSpr;\r
909 \r
910   table=pvid->reg[5]&0x7f;\r
911   if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
912   table<<=8; // Get sprite table address/2\r
913 \r
914   if (!full)\r
915   {\r
916     int pack;\r
917     // updates: tilecode, sx\r
918     for (u=0; u < 80 && (pack = *pd); u++, pd+=2)\r
919     {\r
920       unsigned int *sprite;\r
921       int code, code2, sx, sy, skip=0;\r
922 \r
923       sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
924 \r
925       // parse sprite info\r
926       code  = sprite[0];\r
927       code2 = sprite[1];\r
928       code2 &= ~0xfe000000;\r
929       code2 -=  0x00780000; // Get X coordinate + 8 in upper 16 bits\r
930       sx = code2>>16;\r
931 \r
932       if((sx <= 8-((pack>>28)<<3) && sx >= -0x76) || sx >= 328) skip=1<<23;\r
933       else if ((sy = (pack<<16)>>16) < 240 && sy > -32) {\r
934         int sbl = (2<<(pack>>28))-1;\r
935         sblocks |= sbl<<(sy>>3);\r
936       }\r
937 \r
938       *pd = (pack&~(1<<23))|skip;\r
939       *(pd+1) = code2;\r
940 \r
941       // Find next sprite\r
942       link=(code>>16)&0x7f;\r
943       if(!link) break; // End of sprites\r
944     }\r
945     SpriteBlocks |= sblocks;\r
946   }\r
947   else\r
948   {\r
949     for (; u < 80; u++)\r
950     {\r
951       unsigned int *sprite;\r
952       int code, code2, sx, sy, hv, height, width, skip=0, sx_min;\r
953 \r
954       sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
955 \r
956       // parse sprite info\r
957       code = sprite[0];\r
958       sy = (code&0x1ff)-0x80;\r
959       hv = (code>>24)&0xf;\r
960       height = (hv&3)+1;\r
961 \r
962       if(sy > 240 || sy + (height<<3) <= 0) skip|=1<<22;\r
963 \r
964       width  = (hv>>2)+1;\r
965       code2 = sprite[1];\r
966       sx = (code2>>16)&0x1ff;\r
967       sx -= 0x78; // Get X coordinate + 8\r
968       sx_min = 8-(width<<3);\r
969 \r
970       if((sx <= sx_min && sx >= -0x76) || sx >= 328) skip|=1<<23;\r
971       else if (sx > sx_min && !skip) {\r
972         int sbl = (2<<height)-1;\r
973         int shi = sy>>3;\r
974         if(shi < 0) shi=0; // negative sy\r
975         sblocks |= sbl<<shi;\r
976       }\r
977 \r
978       *pd++ = (width<<28)|(height<<24)|skip|(hv<<16)|((unsigned short)sy);\r
979       *pd++ = (sx<<16)|((unsigned short)code2);\r
980 \r
981       // Find next sprite\r
982       link=(code>>16)&0x7f;\r
983       if(!link) break; // End of sprites\r
984     }\r
985     SpriteBlocks = sblocks;\r
986     *pd = 0; // terminate\r
987   }\r
988 }\r
989 \r
990 static void DrawAllSprites(int *hcache, int maxwidth, int prio, int sh)\r
991 {\r
992   int i,u,n;\r
993   int sx1seen=0; // sprite with x coord 1 or 0 seen\r
994   int ntiles = 0; // tile counter for sprite limit emulation\r
995   int *sprites[40]; // Sprites to draw in fast mode\r
996   int *ps, pack, rs = rendstatus, scan=Scanline;\r
997 \r
998   if(rs&8) {\r
999     DrawAllSpritesInterlace(prio, maxwidth);\r
1000     return;\r
1001   }\r
1002   if(rs&0x11) {\r
1003     //dprintf("PrepareSprites(%i) [%i]", (rs>>4)&1, scan);\r
1004     PrepareSprites(rs&0x10);\r
1005     rendstatus=rs&~0x11;\r
1006   }\r
1007   if (!(SpriteBlocks & (1<<(scan>>3)))) return;\r
1008 \r
1009   if(((rs&4)||sh)&&prio==0)\r
1010     memset(HighSprZ, 0, 328);\r
1011   if(!(rs&4)&&prio) {\r
1012     if(hcache[0]) DrawSpritesFromCache(hcache, sh);\r
1013     return;\r
1014   }\r
1015 \r
1016   ps = HighPreSpr;\r
1017 \r
1018   // Index + 0  :    hhhhvvvv ab--hhvv yyyyyyyy yyyyyyyy // a: offscreen h, b: offs. v, h: horiz. size\r
1019   // Index + 4  :    xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8\r
1020 \r
1021   for(i=u=n=0; (pack = *ps) && n < 20; ps+=2, u++)\r
1022   {\r
1023     int sx, sy, row, pack2;\r
1024 \r
1025     if(pack & 0x00400000) continue;\r
1026 \r
1027     // get sprite info\r
1028     pack2 = *(ps+1);\r
1029     sx =  pack2>>16;\r
1030     sy = (pack <<16)>>16;\r
1031     row = scan-sy;\r
1032 \r
1033     //dprintf("x: %i y: %i %ix%i", sx, sy, (pack>>28)<<3, (pack>>21)&0x38);\r
1034 \r
1035     if(sx == -0x77) sx1seen|=1; // for masking mode 2\r
1036 \r
1037     // check if it is on this line\r
1038     if(row < 0 || row >= ((pack>>21)&0x38)) continue; // no\r
1039     n++; // number of sprites on this line (both visible and hidden, max is 20) [broken]\r
1040 \r
1041     // sprite limit\r
1042     ntiles += pack>>28;\r
1043     if(ntiles > 40) break;\r
1044 \r
1045     if(pack & 0x00800000) continue;\r
1046 \r
1047     // masking sprite?\r
1048     if(sx == -0x78) {\r
1049       if(!(sx1seen&1) || sx1seen==3) {\r
1050         break; // this sprite is not drawn and remaining sprites are masked\r
1051       }\r
1052       if((sx1seen>>8) == 0) sx1seen=(i+1)<<8;\r
1053       continue;\r
1054     }\r
1055     else if(sx == -0x77) {\r
1056       // masking mode2 (Outrun, Galaxy Force II, Shadow of the beast)\r
1057       if(sx1seen>>8) { i=(sx1seen>>8)-1; break; } // seen both 0 and 1\r
1058       sx1seen |= 2;\r
1059       continue;\r
1060     }\r
1061 \r
1062     // accurate sprites\r
1063     //dprintf("P:%i",((sx>>15)&1));\r
1064     if(rs&4) {\r
1065       // might need to skip this sprite\r
1066       if((pack2&0x8000) ^ (prio<<15)) continue;\r
1067       DrawSpriteZ(pack,pack2,sh|(prio<<1),(char)(0x1f-n));\r
1068       continue;\r
1069     }\r
1070 \r
1071     // sprite is good, save it's pointer\r
1072     sprites[i++]=ps;\r
1073   }\r
1074 \r
1075   // Go through sprites backwards:\r
1076   if(!(rs&4)) {\r
1077     for (i--; i>=0; i--)\r
1078       DrawSprite(sprites[i],&hcache,sh);\r
1079 \r
1080     // terminate cache list\r
1081     *hcache = 0;\r
1082   }\r
1083 }\r
1084 \r
1085 \r
1086 // --------------------------------------------\r
1087 \r
1088 #ifndef _ASM_DRAW_C\r
1089 static void BackFill(int reg7, int sh)\r
1090 {\r
1091   unsigned int back=0;\r
1092   unsigned int *pd=NULL,*end=NULL;\r
1093 \r
1094   // Start with a blank scanline (background colour):\r
1095   back=reg7&0x3f;\r
1096   back|=sh<<6;\r
1097   back|=back<<8;\r
1098   back|=back<<16;\r
1099 \r
1100   pd= (unsigned int *)(HighCol+8);\r
1101   end=(unsigned int *)(HighCol+8+320);\r
1102 \r
1103   do { pd[0]=pd[1]=pd[2]=pd[3]=back; pd+=4; } while (pd<end);\r
1104 }\r
1105 #endif\r
1106 \r
1107 // --------------------------------------------\r
1108 \r
1109 unsigned short HighPal[0x100];\r
1110 \r
1111 #ifndef _ASM_DRAW_C\r
1112 static void FinalizeLineBGR444(int sh)\r
1113 {\r
1114   unsigned short *pd=DrawLineDest;\r
1115   unsigned char  *ps=HighCol+8;\r
1116   unsigned short *pal=Pico.cram;\r
1117   int len, i, t;\r
1118 \r
1119   if (Pico.video.reg[12]&1) {\r
1120     len = 320;\r
1121   } else {\r
1122     if(!(PicoOpt&0x100)) pd+=32;\r
1123     len = 256;\r
1124   }\r
1125 \r
1126   if(sh) {\r
1127     pal=HighPal;\r
1128     if(Pico.m.dirtyPal) {\r
1129       blockcpy(pal, Pico.cram, 0x40*2);\r
1130       // shadowed pixels\r
1131       for(i = 0x3f; i >= 0; i--)\r
1132         pal[0x40|i] = pal[0xc0|i] = (unsigned short)((pal[i]>>1)&0x0777);\r
1133       // hilighted pixels\r
1134       for(i = 0x3f; i >= 0; i--) {\r
1135         t=pal[i]&0xeee;t+=0x444;if(t&0x10)t|=0xe;if(t&0x100)t|=0xe0;if(t&0x1000)t|=0xe00;t&=0xeee;\r
1136         pal[0x80|i]=(unsigned short)t;\r
1137       }\r
1138       Pico.m.dirtyPal = 0;\r
1139     }\r
1140   }\r
1141 \r
1142   for(i = 0; i < len; i++)\r
1143     pd[i] = pal[ps[i]];\r
1144 }\r
1145 \r
1146 \r
1147 static void FinalizeLineRGB555(int sh)\r
1148 {\r
1149   unsigned short *pd=DrawLineDest;\r
1150   unsigned char  *ps=HighCol+8;\r
1151   unsigned short *pal=HighPal;\r
1152   int len, i, t, dirtyPal = Pico.m.dirtyPal;\r
1153 \r
1154   if(dirtyPal) {\r
1155     unsigned short *ppal=Pico.cram;\r
1156     for(i = 0x3f; i >= 0; i--)\r
1157       pal[i] = (unsigned short) (((ppal[i]&0x00f)<<12)|((ppal[i]&0x0f0)<<3)|((ppal[i]&0xf00)>>7));\r
1158     Pico.m.dirtyPal = 0;\r
1159   }\r
1160 \r
1161   if (Pico.video.reg[12]&1) {\r
1162     len = 320;\r
1163   } else {\r
1164     if(!(PicoOpt&0x100)) pd+=32;\r
1165     len = 256;\r
1166   }\r
1167 \r
1168   if(sh) {\r
1169     if(dirtyPal) {\r
1170       // shadowed pixels\r
1171       for(i = 0x3f; i >= 0; i--)\r
1172         pal[0x40|i] = pal[0xc0|i] = (unsigned short)((pal[i]>>1)&0x738e);\r
1173       // hilighted pixels\r
1174       for(i = 0x3f; i >= 0; i--) {\r
1175         t=pal[i]&0xe71c;t+=0x4208;if(t&0x20)t|=0x1c;if(t&0x800)t|=0x700;if(t&0x10000)t|=0xe000;t&=0xe71c;\r
1176         pal[0x80|i]=(unsigned short)t;\r
1177       }\r
1178     }\r
1179   }\r
1180 \r
1181   for(i = 0; i < len; i++)\r
1182     pd[i] = pal[ps[i]];\r
1183 }\r
1184 #endif\r
1185 \r
1186 static void FinalizeLine8bit(int sh)\r
1187 {\r
1188   unsigned char *pd=DrawLineDest;\r
1189   int len, rs = rendstatus;\r
1190   static int dirty_count;\r
1191 \r
1192   if (!sh && Pico.m.dirtyPal == 1 && Scanline < 222) {\r
1193     // a hack for mid-frame palette changes\r
1194     if (!(rs & 0x20))\r
1195          dirty_count = 1;\r
1196     else dirty_count++;\r
1197     rs |= 0x20;\r
1198     rendstatus = rs;\r
1199     if (dirty_count == 3) {\r
1200       blockcpy(HighPal, Pico.cram, 0x40*2);\r
1201     } else if (dirty_count == 11) {\r
1202       blockcpy(HighPal+0x40, Pico.cram, 0x40*2);\r
1203     }\r
1204   }\r
1205 \r
1206   if (Pico.video.reg[12]&1) {\r
1207     len = 320;\r
1208   } else {\r
1209     if(!(PicoOpt&0x100)) pd+=32;\r
1210     len = 256;\r
1211   }\r
1212 \r
1213   if (!sh && rs & 0x20) {\r
1214     if (dirty_count >= 11) {\r
1215       blockcpy_or(pd, HighCol+8, len, 0x80);\r
1216     } else {\r
1217       blockcpy_or(pd, HighCol+8, len, 0x40);\r
1218     }\r
1219   } else {\r
1220     blockcpy(pd, HighCol+8, len);\r
1221   }\r
1222 }\r
1223 \r
1224 void (*FinalizeLine)(int sh) = FinalizeLineBGR444;\r
1225 \r
1226 // --------------------------------------------\r
1227 \r
1228 static int DrawDisplay(int sh)\r
1229 {\r
1230   struct PicoVideo *pvid=&Pico.video;\r
1231   int win=0,edge=0,hvwind=0;\r
1232   int maxw, maxcells;\r
1233 \r
1234   rendstatus&=~0xc0;\r
1235 \r
1236   if(pvid->reg[12]&1) {\r
1237     maxw = 328; maxcells = 40;\r
1238   } else {\r
1239     maxw = 264; maxcells = 32;\r
1240   }\r
1241 \r
1242   // Find out if the window is on this line:\r
1243   win=pvid->reg[0x12];\r
1244   edge=(win&0x1f)<<3;\r
1245 \r
1246   if (win&0x80) { if (Scanline>=edge) hvwind=1; }\r
1247   else          { if (Scanline< edge) hvwind=1; }\r
1248 \r
1249   if(!hvwind) { // we might have a vertical window here\r
1250     win=pvid->reg[0x11];\r
1251     edge=win&0x1f;\r
1252     if(win&0x80) {\r
1253       if(!edge) hvwind=1;\r
1254       else if(edge < (maxcells>>1)) hvwind=2;\r
1255     } else {\r
1256       if(!edge);\r
1257       else if(edge < (maxcells>>1)) hvwind=2;\r
1258       else hvwind=1;\r
1259     }\r
1260   }\r
1261 \r
1262   DrawLayer(1, HighCacheB, maxcells, sh);\r
1263   if(hvwind == 1)\r
1264     DrawWindow(0, maxcells>>1, 0, sh); // HighCacheAW\r
1265   else if(hvwind == 2) {\r
1266     // ahh, we have vertical window\r
1267     DrawLayer(0, HighCacheA, (win&0x80) ? edge<<1 : maxcells, sh);\r
1268     DrawWindow((win&0x80) ? edge : 0, (win&0x80) ? maxcells>>1 : edge, 0, sh); // HighCacheW\r
1269   } else\r
1270     DrawLayer(0, HighCacheA, maxcells, sh);\r
1271   DrawAllSprites(HighCacheS, maxw, 0, sh);\r
1272 \r
1273   if(HighCacheB[0])  DrawTilesFromCache(HighCacheB, sh);\r
1274   if(hvwind == 1)\r
1275     DrawWindow(0, maxcells>>1, 1, sh);\r
1276   else if(hvwind == 2) {\r
1277     if(HighCacheA[0]) DrawTilesFromCache(HighCacheA, sh);\r
1278     DrawWindow((win&0x80) ? edge : 0, (win&0x80) ? maxcells>>1 : edge, 1, sh);\r
1279   } else\r
1280     if(HighCacheA[0]) DrawTilesFromCache(HighCacheA, sh);\r
1281   DrawAllSprites(HighCacheS, maxw, 1, sh);\r
1282 \r
1283 #if 0\r
1284   {\r
1285     int *c, a, b;\r
1286     for (a = 0, c = HighCacheA; *c; c++, a++);\r
1287     for (b = 0, c = HighCacheB; *c; c++, b++);\r
1288     printf("%i:%03i: a=%i, b=%i\n", Pico.m.frame_count, Scanline, a, b);\r
1289   }\r
1290 #endif\r
1291 \r
1292   return 0;\r
1293 }\r
1294 \r
1295 \r
1296 static int Skip=0;\r
1297 \r
1298 PICO_INTERNAL void PicoFrameStart(void)\r
1299 {\r
1300   // prepare to do this frame\r
1301   rendstatus = (PicoOpt&0x80)>>5;    // accurate sprites\r
1302   if(rendstatus)\r
1303        Pico.video.status &= ~0x0020;\r
1304   else Pico.video.status |=  0x0020; // sprite collision\r
1305   if((Pico.video.reg[12]&6) == 6) rendstatus |= 8; // interlace mode\r
1306   if(Pico.m.dirtyPal) Pico.m.dirtyPal = 2; // reset dirty if needed\r
1307 \r
1308   PrepareSprites(1);\r
1309   Skip=0;\r
1310 }\r
1311 \r
1312 PICO_INTERNAL int PicoLine(int scan)\r
1313 {\r
1314   int sh;\r
1315   if (Skip>0) { Skip--; return 0; } // Skip rendering lines\r
1316 \r
1317   Scanline=scan;\r
1318   sh=(Pico.video.reg[0xC]&8)>>3; // shadow/hilight?\r
1319 \r
1320   // Draw screen:\r
1321   BackFill(Pico.video.reg[7], sh);\r
1322   if (Pico.video.reg[1]&0x40)\r
1323     DrawDisplay(sh);\r
1324 \r
1325   FinalizeLine(sh);\r
1326   //if (SpriteBlocks & (1<<(scan>>3))) for (sh=0; sh < 30; sh++) DrawLineDest[sh] = 0xf;\r
1327 \r
1328   Skip=PicoScan(Scanline,DrawLineDest);\r
1329 \r
1330   return 0;\r
1331 }\r
1332 \r
1333 \r
1334 void PicoDrawSetColorFormat(int which)\r
1335 {\r
1336     if (which == 2)\r
1337          FinalizeLine = FinalizeLine8bit;\r
1338     else if (which == 1)\r
1339          FinalizeLine = FinalizeLineRGB555;\r
1340     else FinalizeLine = FinalizeLineBGR444;\r
1341 }\r