| 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 |