added copyright line to top of source files next to license information
[cyclone68000.git] / Pico / Draw.cpp
CommitLineData
15eb0001 1\r
4abeeb4b 2// This file is part of the PicoDrive Megadrive Emulator\r
3\r
c41b9b97 4// Copyright (c) 2011 FinalDave (emudave (at) gmail.com)\r
5\r
4abeeb4b 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
15eb0001 11#include "PicoInt.h"\r
12#ifndef __GNUC__\r
13#pragma warning (disable:4706) // Disable assignment with conditional\r
14#endif\r
15\r
16int (*PicoScan)(unsigned int num,unsigned short *data)=NULL;\r
17\r
18// Line colour indices - in the format 00ppcccc pp=palette, cccc=colour\r
19static unsigned short HighCol[32+320+8]; // Gap for 32 column, and messy border on right\r
20static int Scanline=0; // Scanline\r
21\r
22int PicoMask=0xfff; // Mask of which layers to draw\r
23\r
24static 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
45static 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
65struct 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
74static int WrongPri=0; // 1 if there were tiles which are the wrong priority\r
75\r
76static 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
110static 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
150static 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
177static 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
219static 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
261static 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
277static 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
299static 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
310static 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
326static int Skip=0;\r
327\r
328\r
329int 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