Added 0.030 of PicoDrive and moved license files into root
[cyclone68000.git] / Pico / Draw.cpp
1 \r
2 #include "PicoInt.h"\r
3 #ifndef __GNUC__\r
4 #pragma warning (disable:4706) // Disable assignment with conditional\r
5 #endif\r
6 \r
7 int (*PicoScan)(unsigned int num,unsigned short *data)=NULL;\r
8 \r
9 // Line colour indices - in the format 00ppcccc pp=palette, cccc=colour\r
10 static unsigned short HighCol[32+320+8]; // Gap for 32 column, and messy border on right\r
11 static int Scanline=0; // Scanline\r
12 \r
13 int PicoMask=0xfff; // Mask of which layers to draw\r
14 \r
15 static int TileNorm(unsigned short *pd,int addr,unsigned int *pal)\r
16 {\r
17   unsigned int pack=0; unsigned int t=0;\r
18 \r
19   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
20   if (pack)\r
21   {\r
22     t=pack&0x0000f000; if (t) { t=pal[t>>12]; pd[0]=(unsigned short)t; }\r
23     t=pack&0x00000f00; if (t) { t=pal[t>> 8]; pd[1]=(unsigned short)t; }\r
24     t=pack&0x000000f0; if (t) { t=pal[t>> 4]; pd[2]=(unsigned short)t; }\r
25     t=pack&0x0000000f; if (t) { t=pal[t    ]; pd[3]=(unsigned short)t; }\r
26     t=pack&0xf0000000; if (t) { t=pal[t>>28]; pd[4]=(unsigned short)t; }\r
27     t=pack&0x0f000000; if (t) { t=pal[t>>24]; pd[5]=(unsigned short)t; }\r
28     t=pack&0x00f00000; if (t) { t=pal[t>>20]; pd[6]=(unsigned short)t; }\r
29     t=pack&0x000f0000; if (t) { t=pal[t>>16]; pd[7]=(unsigned short)t; }\r
30     return 0;\r
31   }\r
32 \r
33   return 1; // Tile blank\r
34 }\r
35 \r
36 static int TileFlip(unsigned short *pd,int addr,unsigned int *pal)\r
37 {\r
38   unsigned int pack=0; unsigned int t=0;\r
39 \r
40   pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels\r
41   if (pack)\r
42   {\r
43     t=pack&0x000f0000; if (t) { t=pal[t>>16]; pd[0]=(unsigned short)t; }\r
44     t=pack&0x00f00000; if (t) { t=pal[t>>20]; pd[1]=(unsigned short)t; }\r
45     t=pack&0x0f000000; if (t) { t=pal[t>>24]; pd[2]=(unsigned short)t; }\r
46     t=pack&0xf0000000; if (t) { t=pal[t>>28]; pd[3]=(unsigned short)t; }\r
47     t=pack&0x0000000f; if (t) { t=pal[t    ]; pd[4]=(unsigned short)t; }\r
48     t=pack&0x000000f0; if (t) { t=pal[t>> 4]; pd[5]=(unsigned short)t; }\r
49     t=pack&0x00000f00; if (t) { t=pal[t>> 8]; pd[6]=(unsigned short)t; }\r
50     t=pack&0x0000f000; if (t) { t=pal[t>>12]; pd[7]=(unsigned short)t; }\r
51     return 0;\r
52   }\r
53   return 1; // Tile blank\r
54 }\r
55 \r
56 struct TileStrip\r
57 {\r
58   int nametab; // Position in VRAM of name table (for this tile line)\r
59   int line;    // Line number in pixels 0x000-0x3ff within the virtual tilemap \r
60   int hscroll; // Horizontal scroll value in pixels for the line\r
61   int xmask;   // X-Mask (0x1f - 0x7f) for horizontal wraparound in the tilemap\r
62   int high;    // High or low tiles\r
63 };\r
64 \r
65 static int WrongPri=0; // 1 if there were tiles which are the wrong priority\r
66 \r
67 static int DrawStrip(struct TileStrip ts)\r
68 {\r
69   int tilex=0,dx=0,ty=0;\r
70   int blank=-1; // The tile we know is blank\r
71 \r
72   WrongPri=0;\r
73 \r
74   // Draw tiles across screen:\r
75   tilex=(-ts.hscroll)>>3;\r
76   ty=(ts.line&7)<<1; // Y-Offset into tile\r
77   for (dx=((ts.hscroll-1)&7)+1; dx<328; dx+=8,tilex++)\r
78   {\r
79     int code=0,addr=0,zero=0;\r
80     unsigned int *pal=NULL;\r
81 \r
82     code=Pico.vram[ts.nametab+(tilex&ts.xmask)];\r
83     if (code==blank) continue;\r
84     if ((code>>15)!=ts.high) { WrongPri=1; continue; }\r
85 \r
86     // Get tile address/2:\r
87     addr=(code&0x7ff)<<4;\r
88     if (code&0x1000) addr+=14-ty; else addr+=ty; // Y-flip\r
89 \r
90     pal=Pico.highpal+((code>>9)&0x30);\r
91 \r
92     if (code&0x0800) zero=TileFlip(HighCol+24+dx,addr,pal);\r
93     else             zero=TileNorm(HighCol+24+dx,addr,pal);\r
94 \r
95     if (zero) blank=code; // We know this tile is blank now\r
96   }\r
97 \r
98   return 0;\r
99 }\r
100 \r
101 static int DrawLayer(int plane,int high)\r
102 {\r
103   struct PicoVideo *pvid=&Pico.video;\r
104   static char shift[4]={5,6,6,7}; // 32,64 or 128 sized tilemaps\r
105   struct TileStrip ts;\r
106 \r
107   // Work out the TileStrip to draw\r
108 \r
109   // Get vertical scroll value:\r
110   int vscroll=Pico.vsram[plane];\r
111 \r
112   int htab=pvid->reg[13]<<9; // Horizontal scroll table address\r
113   if ( pvid->reg[11]&2)     htab+=Scanline<<1; // Offset by line\r
114   if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile\r
115   htab+=plane; // A or B\r
116 \r
117   // Get horizontal scroll value\r
118   ts.hscroll=Pico.vram[htab&0x7fff];\r
119 \r
120   // Work out the name table size: 32 64 or 128 tiles (0-3)\r
121   int width=pvid->reg[16];\r
122   int height=(width>>4)&3; width&=3;\r
123 \r
124   ts.xmask=(1<<shift[width ])-1; // X Mask in tiles\r
125   int ymask=(8<<shift[height])-1; // Y Mask in pixels\r
126 \r
127   // Find name table:\r
128   if (plane==0) ts.nametab=(pvid->reg[2]&0x38)<< 9; // A\r
129   else          ts.nametab=(pvid->reg[4]&0x07)<<12; // B\r
130 \r
131   // Find the line in the name table\r
132   ts.line=(vscroll+Scanline)&ymask;\r
133   ts.nametab+=(ts.line>>3)<<shift[width];\r
134 \r
135   ts.high=high;\r
136 \r
137   DrawStrip(ts);\r
138   return 0;\r
139 }\r
140 \r
141 static int DrawWindow(int high)\r
142 {\r
143   struct PicoVideo *pvid=&Pico.video;\r
144   struct TileStrip ts;\r
145 \r
146   ts.line=Scanline;\r
147   ts.hscroll=0;\r
148 \r
149   // Find name table line:\r
150   if (Pico.video.reg[12]&1)\r
151   {\r
152     ts.nametab=(pvid->reg[3]&0x3c)<<9; // 40-cell mode\r
153     ts.nametab+=(ts.line>>3)<<6;\r
154   }\r
155   else\r
156   {\r
157     ts.nametab=(pvid->reg[3]&0x3e)<<9; // 32-cell mode\r
158     ts.nametab+=(ts.line>>3)<<5;\r
159   }\r
160 \r
161   ts.xmask=0x3f;\r
162   ts.high=high;\r
163 \r
164   DrawStrip(ts);\r
165   return 0;\r
166 }\r
167 \r
168 static int DrawSprite(int sy,unsigned short *sprite,int high)\r
169 {\r
170   int sx=0,width=0,height=0;\r
171   int row=0,code=0;\r
172   unsigned int *pal=NULL;\r
173   int tile=0,delta=0;\r
174   int i=0;\r
175 \r
176   code=sprite[2];\r
177   if ((code>>15)!=high) { WrongPri=1; return 0; } // Wrong priority\r
178 \r
179   height=sprite[1]>>8;\r
180   width=(height>>2)&3; height&=3;\r
181   width++; height++; // Width and height in tiles\r
182   if (Scanline>=sy+(height<<3)) return 0; // Not on this line after all\r
183 \r
184   row=Scanline-sy; // Row of the sprite we are on\r
185   pal=Pico.highpal+((code>>9)&0x30); // Get palette pointer\r
186   if (code&0x1000) row=(height<<3)-1-row; // Flip Y\r
187 \r
188   tile=code&0x7ff; // Tile number\r
189   tile+=row>>3; // Tile number increases going down\r
190   delta=height; // Delta to increase tile by going right\r
191   if (code&0x0800) { tile+=delta*(width-1); delta=-delta; } // Flip X\r
192 \r
193   tile<<=4; tile+=(row&7)<<1; // Tile address\r
194   delta<<=4; // Delta of address\r
195 \r
196   sx=(sprite[3]&0x1ff)-0x78; // Get X coordinate + 8\r
197 \r
198   for (i=0; i<width; i++,sx+=8,tile+=delta)\r
199   {\r
200     if (sx<=0 || sx>=328) continue; // Offscreen\r
201 \r
202     tile&=0x7fff; // Clip tile address\r
203     if (code&0x0800) TileFlip(HighCol+24+sx,tile,pal);\r
204     else             TileNorm(HighCol+24+sx,tile,pal);\r
205   }\r
206 \r
207   return 0;\r
208 }\r
209 \r
210 static int DrawAllSprites(int high)\r
211 {\r
212   struct PicoVideo *pvid=&Pico.video;\r
213   int table=0;\r
214   int i=0,link=0;\r
215   unsigned char spin[80]; // Sprite index\r
216 \r
217   WrongPri=0;\r
218 \r
219   table=pvid->reg[5]&0x7f;\r
220   if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode\r
221   table<<=8; // Get sprite table address/2\r
222 \r
223   for (;;)\r
224   {\r
225     unsigned short *sprite=NULL;\r
226     \r
227     spin[i]=(unsigned char)link;\r
228     sprite=Pico.vram+((table+(link<<2))&0x7ffc); // Find sprite\r
229 \r
230     // Find next sprite\r
231     link=sprite[1]&0x7f;\r
232     if (link==0 || i>=79) break; // End of sprites\r
233     i++;\r
234   }\r
235 \r
236   // Go through sprites backwards:\r
237   for ( ;i>=0; i--)\r
238   {\r
239     unsigned short *sprite=NULL;\r
240     int sy=0;\r
241     \r
242     sprite=Pico.vram+((table+(spin[i]<<2))&0x7ffc); // Find sprite\r
243 \r
244     sy=(sprite[0]&0x1ff)-0x80; // Get Y coordinate\r
245 \r
246     if (Scanline>=sy && Scanline<sy+32) DrawSprite(sy,sprite,high); // Possibly on this line\r
247   }\r
248 \r
249   return 0;\r
250 }\r
251 \r
252 static void BackFill()\r
253 {\r
254   unsigned int back=0;\r
255   unsigned int *pd=NULL,*end=NULL;\r
256 \r
257   // Start with a blank scanline (background colour):\r
258   back=Pico.video.reg[7]&0x3f;\r
259   back=Pico.highpal[back];\r
260   back|=back<<16;\r
261 \r
262   pd= (unsigned int *)(HighCol+32);\r
263   end=(unsigned int *)(HighCol+32+320);\r
264 \r
265   do { pd[0]=pd[1]=pd[2]=pd[3]=back; pd+=4; } while (pd<end);\r
266 }\r
267 \r
268 static int DrawDisplay()\r
269 {\r
270   int win=0,edge=0,full=0;\r
271   int bhigh=1,ahigh=1,shigh=1;\r
272 \r
273   // Find out if the window is on this line:\r
274   win=Pico.video.reg[0x12];\r
275   edge=(win&0x1f)<<3;\r
276 \r
277   if (win&0x80) { if (Scanline>=edge) full=1; }\r
278   else          { if (Scanline< edge) full=1; }\r
279 \r
280   if (PicoMask&0x04) { DrawLayer(1,0); bhigh=WrongPri; }\r
281   if (PicoMask&0x08) { if (full) DrawWindow(0); else DrawLayer(0,0);  ahigh=WrongPri; }\r
282   if (PicoMask&0x10) { DrawAllSprites(0); shigh=WrongPri; }\r
283   \r
284   if (bhigh) if (PicoMask&0x20) DrawLayer(1,1);\r
285   if (ahigh) if (PicoMask&0x40) { if (full) DrawWindow(1); else DrawLayer(0,1); }\r
286   if (shigh) if (PicoMask&0x80) DrawAllSprites(1);\r
287   return 0;\r
288 }\r
289 \r
290 static int UpdatePalette()\r
291 {\r
292   int c=0;\r
293 \r
294   // Update palette:\r
295   for (c=0;c<64;c++) Pico.highpal[c]=(unsigned short)PicoCram(Pico.cram[c]);\r
296   Pico.m.dirtyPal=0;\r
297 \r
298   return 0;\r
299 }\r
300 \r
301 static int Overlay()\r
302 {\r
303   int col=0,x=0;\r
304 \r
305   if (PmovAction==0) return 0;\r
306   if (Scanline>=4) return 0;\r
307 \r
308   if (PmovAction&1) col =0x00f;\r
309   if (PmovAction&2) col|=0x0f0;\r
310   col=PicoCram(col);\r
311 \r
312   for (x=0;x<4;x++) HighCol[32+x]=(unsigned short)col;\r
313 \r
314   return 0;\r
315 }\r
316 \r
317 static int Skip=0;\r
318 \r
319 \r
320 int PicoLine(int scan)\r
321 {\r
322   if (Skip>0) { Skip--; return 0; } // Skip rendering lines\r
323 \r
324   Scanline=scan;\r
325 \r
326   if (Pico.m.dirtyPal) UpdatePalette();\r
327 \r
328   // Draw screen:\r
329   if (PicoMask&0x02) BackFill();\r
330   if (Pico.video.reg[1]&0x40) DrawDisplay();\r
331 \r
332   Overlay();\r
333 \r
334   if (Pico.video.reg[12]&1)\r
335   {\r
336     Skip=PicoScan(Scanline,HighCol+32); // 40-column mode\r
337   }\r
338   else\r
339   {\r
340     // Crop, centre and return 32-column mode\r
341     memset(HighCol,    0,64); // Left border\r
342     memset(HighCol+288,0,64); // Right border\r
343     Skip=PicoScan(Scanline,HighCol);\r
344   }\r
345 \r
346   return 0;\r
347 }\r
348 \r