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