b4d0ec7631064aa887796cf874b844a36da6070a
[picodrive.git] / pico / draw2.c
1 /*\r
2  * tile renderer\r
3  * (C) notaz, 2006-2008\r
4  * (C) irixxxx, 2020-2023\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 #include "pico_int.h"\r
11 \r
12 #define START_ROW  0 // which row of tiles to start rendering at?\r
13 #define END_ROW   28 // ..end\r
14 \r
15 #define VSRAM      0 // 2-cell vscroll (broken for line based hscroll)\r
16 #define INTERLACE  0 // interlace mode 2\r
17 \r
18 #define TILE_ROWS END_ROW-START_ROW\r
19 \r
20 // note: this is not implemented in ARM asm\r
21 #if defined(DRAW2_OVERRIDE_LINE_WIDTH)\r
22 #define LINE_WIDTH DRAW2_OVERRIDE_LINE_WIDTH\r
23 #else\r
24 #define LINE_WIDTH 328\r
25 #endif\r
26 \r
27 static unsigned char PicoDraw2FB_[LINE_WIDTH * (8+240+8) + 8];\r
28 \r
29 static u32 HighCache2A[2*41*(TILE_ROWS+1)+1+1]; // caches for high layers\r
30 static u32 HighCache2B[2*41*(TILE_ROWS+1)+1+1];\r
31 \r
32 unsigned short *PicoCramHigh=PicoMem.cram; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now)\r
33 void (*PicoPrepareCram)(void) = NULL;      // prepares PicoCramHigh for renderer to use\r
34 \r
35 \r
36 // stuff available in asm:\r
37 #ifdef _ASM_DRAW_C\r
38 void BackFillFull(void *dst, int reg7, int lwidth);\r
39 void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,\r
40                    struct PicoEState *est);\r
41 void DrawTilesFromCacheF(u32 *hc, struct PicoEState *est);\r
42 void DrawWindowFull(int start, int end, int prio, struct PicoEState *est);\r
43 void DrawSpriteFull(unsigned int *sprite, struct PicoEState *est);\r
44 #else\r
45 \r
46 \r
47 static int TileXnormYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)\r
48 {\r
49         unsigned int pack=0; unsigned int t=0, blank = 1;\r
50         unsigned short *vram = est->PicoMem_vram;\r
51         int i, inc=2;\r
52 \r
53 #if INTERLACE\r
54         if (est->rendstatus & PDRAW_INTERLACE) inc = 4;\r
55 #endif\r
56         for(i=8; i; i--, addr+=inc, pd += est->Draw2Width) {\r
57                 pack=*(u32 *)(vram+addr); // Get 8 pixels\r
58                 if(!pack) continue;\r
59 \r
60                 t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);\r
61                 t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);\r
62                 t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);\r
63                 t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t    )|pal);\r
64                 t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);\r
65                 t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);\r
66                 t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);\r
67                 t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);\r
68                 blank = 0;\r
69         }\r
70 \r
71         return blank; // Tile blank?\r
72 }\r
73 \r
74 static int TileXflipYnorm(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)\r
75 {\r
76         unsigned int pack=0; unsigned int t=0, blank = 1;\r
77         unsigned short *vram = est->PicoMem_vram;\r
78         int i, inc=2;\r
79 \r
80 #if INTERLACE\r
81         if (est->rendstatus & PDRAW_INTERLACE) inc = 4;\r
82 #endif\r
83         for(i=8; i; i--, addr+=inc, pd += est->Draw2Width) {\r
84                 pack=*(u32 *)(vram+addr); // Get 8 pixels\r
85                 if(!pack) continue;\r
86 \r
87                 t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);\r
88                 t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);\r
89                 t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);\r
90                 t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);\r
91                 t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t    )|pal);\r
92                 t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);\r
93                 t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);\r
94                 t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);\r
95                 blank = 0;\r
96         }\r
97         return blank; // Tile blank?\r
98 }\r
99 \r
100 static int TileXnormYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)\r
101 {\r
102         unsigned int pack=0; unsigned int t=0, blank = 1;\r
103         unsigned short *vram = est->PicoMem_vram;\r
104         int i, inc=2;\r
105 \r
106 #if INTERLACE\r
107         if (est->rendstatus & PDRAW_INTERLACE) inc = 4, addr += 16;\r
108 #endif\r
109         addr+=14;\r
110         for(i=8; i; i--, addr-=inc, pd += est->Draw2Width) {\r
111                 pack=*(u32 *)(vram+addr); // Get 8 pixels\r
112                 if(!pack) continue;\r
113 \r
114                 t=pack&0x0000f000; if (t) pd[0]=(unsigned char)((t>>12)|pal);\r
115                 t=pack&0x00000f00; if (t) pd[1]=(unsigned char)((t>> 8)|pal);\r
116                 t=pack&0x000000f0; if (t) pd[2]=(unsigned char)((t>> 4)|pal);\r
117                 t=pack&0x0000000f; if (t) pd[3]=(unsigned char)((t    )|pal);\r
118                 t=pack&0xf0000000; if (t) pd[4]=(unsigned char)((t>>28)|pal);\r
119                 t=pack&0x0f000000; if (t) pd[5]=(unsigned char)((t>>24)|pal);\r
120                 t=pack&0x00f00000; if (t) pd[6]=(unsigned char)((t>>20)|pal);\r
121                 t=pack&0x000f0000; if (t) pd[7]=(unsigned char)((t>>16)|pal);\r
122                 blank = 0;\r
123         }\r
124 \r
125         return blank; // Tile blank?\r
126 }\r
127 \r
128 static int TileXflipYflip(unsigned char *pd,int addr,unsigned char pal, struct PicoEState *est)\r
129 {\r
130         unsigned int pack=0; unsigned int t=0, blank = 1;\r
131         unsigned short *vram = est->PicoMem_vram;\r
132         int i, inc=2;\r
133 \r
134 #if INTERLACE\r
135         if (est->rendstatus & PDRAW_INTERLACE) inc = 4, addr += 16;\r
136 #endif\r
137         addr+=14;\r
138         for(i=8; i; i--, addr-=inc, pd += est->Draw2Width) {\r
139                 pack=*(u32 *)(vram+addr); // Get 8 pixels\r
140                 if(!pack) continue;\r
141 \r
142                 t=pack&0x000f0000; if (t) pd[0]=(unsigned char)((t>>16)|pal);\r
143                 t=pack&0x00f00000; if (t) pd[1]=(unsigned char)((t>>20)|pal);\r
144                 t=pack&0x0f000000; if (t) pd[2]=(unsigned char)((t>>24)|pal);\r
145                 t=pack&0xf0000000; if (t) pd[3]=(unsigned char)((t>>28)|pal);\r
146                 t=pack&0x0000000f; if (t) pd[4]=(unsigned char)((t    )|pal);\r
147                 t=pack&0x000000f0; if (t) pd[5]=(unsigned char)((t>> 4)|pal);\r
148                 t=pack&0x00000f00; if (t) pd[6]=(unsigned char)((t>> 8)|pal);\r
149                 t=pack&0x0000f000; if (t) pd[7]=(unsigned char)((t>>12)|pal);\r
150                 blank = 0;\r
151         }\r
152         return blank; // Tile blank?\r
153 }\r
154 \r
155 \r
156 // start: (tile_start<<16)|row_start, end: [same]\r
157 static void DrawWindowFull(int start, int end, int prio, struct PicoEState *est)\r
158 {\r
159         struct PicoVideo *pvid=&est->Pico->video;\r
160         int nametab, nametab_step, trow, tilex, blank=-1, code;\r
161         unsigned char *scrpos = est->Draw2FB;\r
162         int scrstart = est->Draw2Start;\r
163         int tile_start, tile_end; // in cells\r
164 \r
165         // parse ranges\r
166         tile_start = start>>16;\r
167         tile_end = end>>16;\r
168         start = start<<16>>16;\r
169         end = end<<16>>16;\r
170 \r
171         // Find name table line:\r
172         if (!(est->rendstatus & PDRAW_32_COLS))\r
173         {\r
174                 nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode\r
175                 nametab_step = 1<<6;\r
176         }\r
177         else\r
178         {\r
179                 nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode\r
180                 nametab_step = 1<<5;\r
181                 if (est->rendstatus & PDRAW_BORDER_32)\r
182                         scrpos += 32;\r
183         }\r
184         nametab += nametab_step*(start-scrstart);\r
185 \r
186         // check priority\r
187         code=est->PicoMem_vram[nametab+tile_start];\r
188         if ((code>>15) != prio) return; // hack: just assume that whole window uses same priority\r
189 \r
190         scrpos+=8*est->Draw2Width+8;\r
191         scrpos+=8*est->Draw2Width*(start-scrstart);\r
192 \r
193         // do a window until we reach planestart row\r
194         for(trow = start; trow < end; trow++, nametab+=nametab_step) { // current tile row\r
195                 for (tilex=tile_start; tilex<tile_end; tilex++)\r
196                 {\r
197                         int code,addr,zero=0;\r
198 //                      unsigned short *pal=NULL;\r
199                         unsigned char pal;\r
200 \r
201                         code=est->PicoMem_vram[nametab+tilex];\r
202                         if (code==blank) continue;\r
203 \r
204                         // Get tile address/2:\r
205 #if INTERLACE\r
206                         if (est->rendstatus & PDRAW_INTERLACE)\r
207                                 addr=(code&0x3ff)<<5;\r
208                         else\r
209 #endif\r
210                                 addr=(code&0x7ff)<<4;\r
211 \r
212 //                      pal=PicoCramHigh+((code>>9)&0x30);\r
213                         pal=(unsigned char)((code>>9)&0x30);\r
214 \r
215                         switch((code>>11)&3) {\r
216                                 case 0: zero=TileXnormYnorm(scrpos+(tilex<<3),addr,pal,est); break;\r
217                                 case 1: zero=TileXflipYnorm(scrpos+(tilex<<3),addr,pal,est); break;\r
218                                 case 2: zero=TileXnormYflip(scrpos+(tilex<<3),addr,pal,est); break;\r
219                                 case 3: zero=TileXflipYflip(scrpos+(tilex<<3),addr,pal,est); break;\r
220                         }\r
221                         if(zero) blank=code; // We know this tile is blank now\r
222                 }\r
223 \r
224                 scrpos += est->Draw2Width*8;\r
225         }\r
226 }\r
227 \r
228 \r
229 static void DrawLayerFull(int plane, u32 *hcache, int planestart, int planeend,\r
230                           struct PicoEState *est)\r
231 {\r
232         struct PicoVideo *pvid=&est->Pico->video;\r
233         static char shift[4]={5,6,5,7}; // 32,64 or 128 sized tilemaps\r
234         int width, height, ymask, htab;\r
235         int nametab, hscroll=0, vscroll, cells;\r
236         unsigned char *scrpos;\r
237         int scrstart = est->Draw2Start;\r
238         int blank=-1, xmask, nametab_row, trow;\r
239 \r
240         // parse ranges\r
241         cells = (planeend>>16)-(planestart>>16);\r
242         planestart = planestart<<16>>16;\r
243         planeend = planeend<<16>>16;\r
244 \r
245         // Work out the Tiles to draw\r
246 \r
247         htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
248 //      if ( pvid->reg[11]&2)     htab+=Scanline<<1; // Offset by line\r
249 //      if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile\r
250         htab+=plane; // A or B\r
251 \r
252         if(!(pvid->reg[11]&3)) { // full screen scroll\r
253                 // Get horizontal scroll value\r
254                 hscroll=est->PicoMem_vram[htab&0x7fff];\r
255                 htab = 0; // this marks that we don't have to update scroll value\r
256         }\r
257 \r
258         // Work out the name table size: 32 64 or 128 tiles (0-3)\r
259         width=pvid->reg[16];\r
260         height=(width>>4)&3; width&=3;\r
261 \r
262         xmask=(1<<shift[width ])-1; // X Mask in tiles\r
263         ymask=(height<<5)|0x1f;     // Y Mask in tiles\r
264         if(width == 1)   ymask&=0x3f;\r
265         else if(width>1) ymask =0x1f;\r
266 \r
267         // Find name table:\r
268         if (plane==0) nametab=(pvid->reg[2]&0x38)<< 9; // A\r
269         else          nametab=(pvid->reg[4]&0x07)<<12; // B\r
270 \r
271         scrpos = est->Draw2FB;\r
272         if ((~est->rendstatus & (PDRAW_BORDER_32|PDRAW_32_COLS)) == 0)\r
273                 scrpos += 32;\r
274         scrpos+=8*est->Draw2Width*(planestart-scrstart);\r
275 \r
276         if((pvid->reg[11]&4)||(PicoMem.vsram[plane]&7))\r
277                 planeend++; // we (may) have vertically clipped tiles due to vscroll, so we need 1 more row\r
278         for(trow = planestart; trow < planeend; trow++) { // current tile row\r
279                 int cellc=cells,tilex,dx,vsidx=0;\r
280 \r
281                 // Get vertical scroll value:\r
282                 vscroll=PicoMem.vsram[plane];//&0x1ff;\r
283 #if VSRAM\r
284                 if ((est->rendstatus & PDRAW_32_COLS) && (pvid->reg[11]&4)) // H32 + 2-cell mode\r
285                         vscroll=PicoMem.vsram[plane+0x20];//&0x1ff;\r
286 #endif\r
287 #if INTERLACE\r
288                 if (est->rendstatus & PDRAW_INTERLACE)\r
289                         vscroll >>= 1;\r
290 #endif\r
291                 nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row\r
292 \r
293                 // Find the tile row in the name table\r
294                 //ts.line=(vscroll+Scanline)&ymask;\r
295                 //ts.nametab+=(ts.line>>3)<<shift[width];\r
296 \r
297                 // update hscroll if needed\r
298                 if(htab) {\r
299                         int htaddr=htab+(trow<<4);\r
300                         if(trow) htaddr-=(vscroll&7)<<1;\r
301                         hscroll=est->PicoMem_vram[htaddr&0x7fff];\r
302                 }\r
303 \r
304                 // Draw tiles across screen:\r
305                 tilex=(-hscroll)>>3;\r
306                 dx=((hscroll-1)&7)+1;\r
307                 if(dx != 8) cellc++, vsidx--; // have hscroll, do more cells\r
308 \r
309                 for (; cellc; dx+=8,tilex++,cellc--)\r
310                 {\r
311                         int code=0,addr=0,zero=0,scroff;\r
312 //                      unsigned short *pal=NULL;\r
313                         unsigned char pal;\r
314 \r
315 #if VSRAM\r
316                         if ((pvid->reg[11]&4) && !(vsidx&1)) { // 2-cell mode\r
317                                 vscroll=PicoMem.vsram[vsidx+plane];//&0x1ff;\r
318 #if INTERLACE\r
319                                 if (est->rendstatus & PDRAW_INTERLACE)\r
320                                         vscroll >>= 1;\r
321 #endif\r
322                                 nametab_row = nametab + (((trow+(vscroll>>3))&ymask)<<shift[width]); // pointer to nametable entries for this row\r
323                         }\r
324 #endif\r
325                         vsidx++;\r
326 \r
327                         code=est->PicoMem_vram[nametab_row+(tilex&xmask)];\r
328                         if (code==blank) continue;\r
329 \r
330                         if (code>>15) { // high priority tile\r
331                                 *hcache++ = code|(dx<<16)|(trow<<27); // cache it\r
332                                 *hcache++ = 8-(vscroll&7); // push y-offset to tilecache\r
333                                 continue;\r
334                         }\r
335 \r
336                         // Get tile address/2:\r
337 #if INTERLACE\r
338                         if (est->rendstatus & PDRAW_INTERLACE)\r
339                                 addr=(code&0x3ff)<<5;\r
340                         else\r
341 #endif\r
342                                 addr=(code&0x7ff)<<4;\r
343 \r
344 //                      pal=PicoCramHigh+((code>>9)&0x30);\r
345                         pal=(unsigned char)((code>>9)&0x30);\r
346 \r
347                         scroff=(8-(vscroll&7))*est->Draw2Width;\r
348                         switch((code>>11)&3) {\r
349                                 case 0: zero=TileXnormYnorm(scrpos+scroff+dx,addr,pal,est); break;\r
350                                 case 1: zero=TileXflipYnorm(scrpos+scroff+dx,addr,pal,est); break;\r
351                                 case 2: zero=TileXnormYflip(scrpos+scroff+dx,addr,pal,est); break;\r
352                                 case 3: zero=TileXflipYflip(scrpos+scroff+dx,addr,pal,est); break;\r
353                         }\r
354                         if(zero) blank=code; // We know this tile is blank now\r
355                 }\r
356 \r
357                 scrpos += est->Draw2Width*8;\r
358         }\r
359 \r
360         *hcache = 0; // terminate cache\r
361 }\r
362 \r
363 \r
364 static void DrawTilesFromCacheF(u32 *hc, struct PicoEState *est)\r
365 {\r
366         u32 code;\r
367         int addr, zero = 0, vscroll;\r
368         unsigned int prevy=0xFFFFFFFF;\r
369 //      unsigned short *pal;\r
370         unsigned char pal;\r
371         short blank=-1; // The tile we know is blank\r
372         unsigned char *scrpos = est->Draw2FB, *pd = 0;\r
373         int scrstart = est->Draw2Start;\r
374 \r
375         if ((~est->rendstatus & (PDRAW_BORDER_32|PDRAW_32_COLS)) == 0)\r
376                 scrpos += 32;\r
377 \r
378         while((code=*hc++)) {\r
379                 vscroll=(*hc++ - START_ROW*8)*est->Draw2Width;\r
380                 if((short)code == blank) continue;\r
381 \r
382                 // y pos\r
383                 if(((unsigned)code>>27) != prevy) {\r
384                         prevy = (unsigned)code>>27;\r
385                         pd = scrpos + (prevy-scrstart)*est->Draw2Width*8;\r
386                 }\r
387 \r
388                 // Get tile address/2:\r
389 #if INTERLACE\r
390                 if (est->rendstatus & PDRAW_INTERLACE)\r
391                         addr=(code&0x3ff)<<5;\r
392                 else\r
393 #endif\r
394                         addr=(code&0x7ff)<<4;\r
395 //              pal=PicoCramHigh+((code>>9)&0x30);\r
396                 pal=(unsigned char)((code>>9)&0x30);\r
397 \r
398                 switch((code>>11)&3) {\r
399                         case 0: zero=TileXnormYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;\r
400                         case 1: zero=TileXflipYnorm(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;\r
401                         case 2: zero=TileXnormYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;\r
402                         case 3: zero=TileXflipYflip(pd+vscroll+((code>>16)&0x1ff),addr,pal,est); break;\r
403                 }\r
404 \r
405                 if(zero) blank=(short)code;\r
406         }\r
407 }\r
408 \r
409 \r
410 // sx and sy are coords of virtual screen with 8pix borders on top and on left\r
411 static void DrawSpriteFull(u32 *sprite, struct PicoEState *est)\r
412 {\r
413         int width=0,height=0;\r
414 //      unsigned short *pal=NULL;\r
415         unsigned char pal;\r
416         int tile,code,tdeltax,tdeltay;\r
417         unsigned char *scrpos;\r
418         int scrstart = est->Draw2Start;\r
419         int sx, sy;\r
420 \r
421         sy=sprite[0];\r
422         height=sy>>24;\r
423 #if INTERLACE\r
424         if (est->rendstatus & PDRAW_INTERLACE)\r
425                 sy = ((sy>>1)&0x1ff)-0x78;\r
426         else\r
427 #endif\r
428                 sy=(sy&0x1ff)-0x78; // Y\r
429         width=(height>>2)&3; height&=3;\r
430         width++; height++; // Width and height in tiles\r
431 \r
432         code=sprite[1];\r
433         sx=((code>>16)&0x1ff)-0x78; // X\r
434 \r
435         tile=code&0x7ff; // Tile number\r
436         tdeltax=height; // Delta to increase tile by going right\r
437         tdeltay=1;      // Delta to increase tile by going down\r
438         if (code&0x1000) { tile+=tdeltax-1; tdeltay=-tdeltay; } // Flip Y\r
439         if (code&0x0800) { tile+=tdeltax*(width-1); tdeltax=-tdeltax; } // Flip X\r
440 \r
441         //delta<<=4; // Delta of address\r
442 //      pal=PicoCramHigh+((code>>9)&0x30); // Get palette pointer\r
443         pal=(unsigned char)((code>>9)&0x30);\r
444 \r
445         // goto first vertically visible tile\r
446         sy -= scrstart*8;\r
447         while(sy <= 0) { sy+=8; tile+=tdeltay; height--; }\r
448 \r
449         scrpos = est->Draw2FB;\r
450         if ((~est->rendstatus & (PDRAW_BORDER_32|PDRAW_32_COLS)) == 0)\r
451                 scrpos += 32;\r
452         scrpos+=sy*est->Draw2Width;\r
453 \r
454         for (; height > 0; height--, sy+=8, tile+=tdeltay)\r
455         {\r
456                 int w = width, x=sx, t=tile, s;\r
457 \r
458                 if(sy >= END_ROW*8+8) return; // offscreen\r
459 \r
460                 for (; w; w--,x+=8,t+=tdeltax)\r
461                 {\r
462                         if(x<=0)   continue;\r
463                         if(x>=328) break; // Offscreen\r
464 \r
465                         t&=0x7fff; // Clip tile address\r
466 #if INTERLACE\r
467                         if (est->rendstatus & PDRAW_INTERLACE)\r
468                                 s=5;\r
469                         else\r
470 #endif\r
471                                 s=4;\r
472                         switch((code>>11)&3) {\r
473                                 case 0: TileXnormYnorm(scrpos+x,t<<s,pal,est); break;\r
474                                 case 1: TileXflipYnorm(scrpos+x,t<<s,pal,est); break;\r
475                                 case 2: TileXnormYflip(scrpos+x,t<<s,pal,est); break;\r
476                                 case 3: TileXflipYflip(scrpos+x,t<<s,pal,est); break;\r
477                         }\r
478                 }\r
479 \r
480                 scrpos+=8*est->Draw2Width;\r
481         }\r
482 }\r
483 #endif\r
484 \r
485 \r
486 static void DrawAllSpritesFull(int prio, int maxwidth, struct PicoEState *est)\r
487 {\r
488         struct PicoVideo *pvid=&est->Pico->video;\r
489         int table=0,maskrange=0;\r
490         int i,u,link=0;\r
491         u32 *sprites[80]; // Sprites\r
492         int y_min=START_ROW*8, y_max=END_ROW*8; // for a simple sprite masking\r
493         int max_sprites = !(est->rendstatus & PDRAW_32_COLS) ? 80 : 64;\r
494 \r
495         if (est->rendstatus & PDRAW_30_ROWS)\r
496                 y_min += 8, y_max += 8;\r
497 \r
498         table=pvid->reg[5]&0x7f;\r
499         if (!(est->rendstatus & PDRAW_32_COLS)) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
500         table<<=8; // Get sprite table address/2\r
501 \r
502         for (i = u = 0; u < max_sprites && link < max_sprites; u++)\r
503         {\r
504                 u32 *sprite=NULL;\r
505                 int code, code2, sx, sy, height;\r
506 \r
507                 sprite=(u32 *)(est->PicoMem_vram+((table+(link<<2))&0x7ffc)); // Find sprite\r
508 \r
509                 // get sprite info\r
510                 code = sprite[0];\r
511 \r
512                 // check if it is not hidden vertically\r
513 #if INTERLACE\r
514                 if (est->rendstatus & PDRAW_INTERLACE)\r
515                         sy = ((code>>1)&0x1ff)-0x80;\r
516                 else\r
517 #endif\r
518                         sy = (code&0x1ff)-0x80;\r
519                 height = (((code>>24)&3)+1)<<3;\r
520                 if(sy+height <= y_min || sy > y_max) goto nextsprite;\r
521 \r
522                 // masking sprite?\r
523                 code2=sprite[1];\r
524                 sx = (code2>>16)&0x1ff;\r
525                 if(!sx) {\r
526                         int to = sy+height; // sy ~ from\r
527                         if(maskrange) {\r
528                                 // try to merge with previous range\r
529                                 if((maskrange>>16)+1 >= sy && (maskrange>>16) <= to && (maskrange&0xffff) < sy) sy = (maskrange&0xffff);\r
530                                 else if((maskrange&0xffff)-1 <= to && (maskrange&0xffff) >= sy && (maskrange>>16) > to) to = (maskrange>>16);\r
531                         }\r
532                         // support only very simple masking (top and bottom of screen)\r
533                         if(sy <= y_min && to+1 > y_min) y_min = to+1;\r
534                         else if(to >= y_max && sy-1 < y_max) y_max = sy-1;\r
535                         else maskrange=sy|(to<<16);\r
536 \r
537                         goto nextsprite;\r
538                 }\r
539 \r
540                 // priority\r
541                 if(((code2>>15)&1) != prio) goto nextsprite; // wrong priority\r
542 \r
543                 // check if sprite is not hidden horizontally\r
544                 sx -= 0x78; // Get X coordinate + 8\r
545                 if(sx <= -8*3 || sx >= maxwidth) goto nextsprite;\r
546 \r
547                 // sprite is good, save it's index\r
548                 sprites[i++]=sprite;\r
549 \r
550                 nextsprite:\r
551                 // Find next sprite\r
552                 link=(code>>16)&0x7f;\r
553                 if(!link) break; // End of sprites\r
554         }\r
555 \r
556         // Go through sprites backwards:\r
557         for (i--; i >= 0; i--)\r
558         {\r
559                 DrawSpriteFull(sprites[i], est);\r
560         }\r
561 }\r
562 \r
563 #ifndef _ASM_DRAW_C\r
564 static void BackFillFull(unsigned char *dst, int reg7, int lwidth)\r
565 {\r
566         unsigned int back;\r
567         int i;\r
568 \r
569         // Start with a background color:\r
570         back=reg7&0x3f;\r
571         back|=back<<8;\r
572         back|=back<<16;\r
573 \r
574         for (i = 0, dst += 8*lwidth; i < (END_ROW-START_ROW)*8; i++, dst += lwidth)\r
575                 memset32(dst+8, back, 320/4);\r
576 }\r
577 #endif\r
578 \r
579 static void DrawDisplayFull(void)\r
580 {\r
581         struct PicoEState *est = &Pico.est;\r
582         struct PicoVideo *pvid=&est->Pico->video;\r
583         int win, edge=0, hvwin=0; // LSb->MSb: hwin&plane, vwin&plane, full\r
584         int scrstart=START_ROW, scrend = END_ROW; // our render area\r
585         int planestart, planeend; // plane A start/end when window shares display with plane A (in tile rows or columns)\r
586         int winstart, winend;     // same for window\r
587         int maxw, maxcolc; // max width and col cells\r
588 \r
589         if(est->rendstatus & PDRAW_32_COLS) {\r
590                 maxw = 264; maxcolc = 32;\r
591         } else {\r
592                 maxw = 328; maxcolc = 40;\r
593         }\r
594         if(est->rendstatus & PDRAW_30_ROWS) {\r
595                 // In 240 line mode, the top and bottom 8 lines are omitted\r
596                 // since this renderer always renders 224 lines\r
597                 scrstart ++, scrend ++;\r
598         }\r
599         est->Draw2Start = scrstart;\r
600 \r
601         planestart = scrstart, planeend = scrend;\r
602         winstart = scrstart, winend = scrend;\r
603 \r
604         // horizontal window?\r
605         if ((win=pvid->reg[0x12]))\r
606         {\r
607                 hvwin=1; // hwindow shares display with plane A\r
608                 edge=win&0x1f;\r
609                 if(win == 0x80) {\r
610                         // fullscreen window\r
611                         hvwin=4;\r
612                 } else if(win < 0x80) {\r
613                         // window on the top\r
614                              if(edge <= scrstart) hvwin=0; // window not visible in our drawing region\r
615                         else if(edge >= scrend)   hvwin=4;\r
616                         else planestart = winend = edge;\r
617                 } else if(win > 0x80) {\r
618                         // window at the bottom\r
619                         if(edge >= scrend) hvwin=0;\r
620                         else planeend = winstart = edge;\r
621                 }\r
622         }\r
623 \r
624         // check for vertical window, but only if win is not fullscreen\r
625         if (hvwin != 4)\r
626         {\r
627                 win=pvid->reg[0x11];\r
628                 edge=win&0x1f;\r
629                 if (win&0x80) {\r
630                         if(!edge) hvwin=4;\r
631                         else if(edge < (maxcolc>>1)) {\r
632                                 // window is on the right\r
633                                 hvwin|=2;\r
634                                 planeend|=edge<<17;\r
635                                 winstart|=edge<<17;\r
636                                 winend|=maxcolc<<16;\r
637                         }\r
638                 } else {\r
639                         if(edge >= (maxcolc>>1)) hvwin=4;\r
640                         else if(edge) {\r
641                                 // window is on the left\r
642                                 hvwin|=2;\r
643                                 winend|=edge<<17;\r
644                                 planestart|=edge<<17;\r
645                                 planeend|=maxcolc<<16;\r
646                         }\r
647                 }\r
648         }\r
649 \r
650         if (hvwin==1) { winend|=maxcolc<<16; planeend|=maxcolc<<16; }\r
651 \r
652         HighCache2A[0] = HighCache2B[0] = 0;\r
653         if (!(pvid->debug_p & PVD_KILL_B))\r
654                 DrawLayerFull(1, HighCache2B, scrstart, (maxcolc<<16)|scrend, est);\r
655         if (!(pvid->debug_p & PVD_KILL_A)) switch (hvwin)\r
656         {\r
657                 case 4:\r
658                 // fullscreen window\r
659                 DrawWindowFull(scrstart, (maxcolc<<16)|scrend, 0, est);\r
660                 break;\r
661 \r
662                 case 3:\r
663                 // we have plane A and both v and h windows\r
664                 DrawLayerFull(0, HighCache2A, planestart, planeend, est);\r
665                 DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 0, est); // h\r
666                 DrawWindowFull((winstart&~0xff)|scrstart, (winend&~0xff)|scrend, 0, est);    // v\r
667                 break;\r
668 \r
669                 case 2:\r
670                 case 1:\r
671                 // both window and plane A visible, window is vertical XOR horizontal\r
672                 DrawLayerFull(0, HighCache2A, planestart, planeend, est);\r
673                 DrawWindowFull(winstart, winend, 0, est);\r
674                 break;\r
675 \r
676                 default:\r
677                 // fullscreen plane A\r
678                 DrawLayerFull(0, HighCache2A, scrstart, (maxcolc<<16)|scrend, est);\r
679                 break;\r
680         }\r
681         if (!(pvid->debug_p & PVD_KILL_S_LO))\r
682                 DrawAllSpritesFull(0, maxw, est);\r
683 \r
684         if (HighCache2B[0]) DrawTilesFromCacheF(HighCache2B, est);\r
685         if (HighCache2A[0]) DrawTilesFromCacheF(HighCache2A, est);\r
686         if (!(pvid->debug_p & PVD_KILL_A)) switch (hvwin)\r
687         {\r
688                 case 4:\r
689                 // fullscreen window\r
690                 DrawWindowFull(scrstart, (maxcolc<<16)|scrend, 1, est);\r
691                 break;\r
692 \r
693                 case 3:\r
694                 // we have plane A and both v and h windows\r
695                 DrawWindowFull( winstart&~0xff0000, (winend&~0xff0000)|(maxcolc<<16), 1, est); // h\r
696                 DrawWindowFull((winstart&~0xff)|scrstart, (winend&~0xff)|scrend, 1, est);    // v\r
697                 break;\r
698 \r
699                 case 2:\r
700                 case 1:\r
701                 // both window and plane A visible, window is vertical XOR horizontal\r
702                 DrawWindowFull(winstart, winend, 1, est);\r
703                 break;\r
704         }\r
705         if (!(pvid->debug_p & PVD_KILL_S_HI))\r
706                 DrawAllSpritesFull(1, maxw, est);\r
707 }\r
708 \r
709 \r
710 PICO_INTERNAL void PicoFrameFull(void)\r
711 {\r
712         pprof_start(draw);\r
713 \r
714         // prepare cram?\r
715         if (PicoPrepareCram) PicoPrepareCram();\r
716 \r
717         // Draw screen:\r
718         BackFillFull(Pico.est.Draw2FB, Pico.video.reg[7], Pico.est.Draw2Width);\r
719         if (Pico.video.reg[1] & 0x40)\r
720                 DrawDisplayFull();\r
721 \r
722         // clear top and bottom 8 lines in 240 mode, since draw2 only does 224\r
723         if (Pico.est.rendstatus & PDRAW_30_ROWS) {\r
724                 unsigned char *pd = Pico.est.Draw2FB;\r
725                 int i;\r
726 \r
727                 for (i = 8; i > 0; i--, pd += Pico.est.Draw2Width)\r
728                         memset32((int *)pd, 0xe0e0e0e0, 328/4);\r
729                 pd += Pico.est.Draw2Width*(END_ROW-START_ROW)*8;\r
730                 for (i = 8; i > 0; i--, pd += Pico.est.Draw2Width)\r
731                         memset32((int *)pd, 0xe0e0e0e0, 328/4);\r
732         }\r
733 \r
734         pprof_end(draw);\r
735 }\r
736 \r
737 void PicoDraw2SetOutBuf(void *dest, int incr)\r
738 {\r
739         if (dest) {\r
740                 Pico.est.Draw2FB = dest;\r
741                 Pico.est.Draw2Width = incr;\r
742         } else {\r
743                 Pico.est.Draw2FB = PicoDraw2FB_;\r
744                 Pico.est.Draw2Width = LINE_WIDTH;\r
745         }\r
746 }\r
747 \r
748 void PicoDraw2Init(void)\r
749 {\r
750         PicoDraw2SetOutBuf(NULL, 0);\r
751 }\r