Added 0.030 of PicoDrive and moved license files into root
[cyclone68000.git] / Pico / Draw.cpp
CommitLineData
15eb0001 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
7int (*PicoScan)(unsigned int num,unsigned short *data)=NULL;\r
8\r
9// Line colour indices - in the format 00ppcccc pp=palette, cccc=colour\r
10static unsigned short HighCol[32+320+8]; // Gap for 32 column, and messy border on right\r
11static int Scanline=0; // Scanline\r
12\r
13int PicoMask=0xfff; // Mask of which layers to draw\r
14\r
15static 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
36static 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
56struct 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
65static int WrongPri=0; // 1 if there were tiles which are the wrong priority\r
66\r
67static 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
101static 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
141static 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
168static 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
210static 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
252static 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
268static 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
290static 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
301static 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
317static int Skip=0;\r
318\r
319\r
320int 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