render.c
1 #include "shared.h" 2 #include "emuapi.h" 3 4 /* Background drawing function */ 5 static void (*render_bg)(int line); 6 7 /* Pointer to output buffer */ 8 static byte *linebuf; 9 10 /* Internal buffer for drawing non 8-bit displays */ 11 static byte internal_buffer[0x100]; 12 13 /* Pattern cache */ 14 //static byte cache[0x20000]; 15 16 /* Dirty pattern info */ 17 byte svram_dirty[0x200]; 18 byte sis_vram_dirty; 19 20 /* Pixel look-up table */ 21 //#include "genlut.h" 22 #include "smslut.h" 23 //static byte lut[0x10000]; 24 25 26 /* Attribute expansion table */ 27 static UINT32 atex[4] = 28 { 29 0x00000000, 30 0x10101010, 31 0x20202020, 32 0x30303030, 33 }; 34 35 /* Display sizes */ 36 static int vp_vstart; 37 static int vp_vend; 38 static int vp_hstart; 39 static int vp_hend; 40 41 /* Macros to access memory 32-bits at a time (from MAME's drawgfx.c) */ 42 43 #ifdef ALIGN_DWORD 44 45 static __inline__ UINT32 read_dword(void *address) 46 { 47 if ((UINT32)address & 3) 48 { 49 #ifdef LSB_FIRST /* little endian version */ 50 return ( *((byte *)address) + 51 (*((byte *)address+1) << 8) + 52 (*((byte *)address+2) << 16) + 53 (*((byte *)address+3) << 24) ); 54 #else /* big endian version */ 55 return ( *((byte *)address+3) + 56 (*((byte *)address+2) << 8) + 57 (*((byte *)address+1) << 16) + 58 (*((byte *)address) << 24) ); 59 #endif 60 } 61 else 62 return *(UINT32 *)address; 63 } 64 65 66 static __inline__ void write_dword(void *address, UINT32 data) 67 { 68 if ((UINT32)address & 3) 69 { 70 #ifdef LSB_FIRST 71 *((byte *)address) = data; 72 *((byte *)address+1) = (data >> 8); 73 *((byte *)address+2) = (data >> 16); 74 *((byte *)address+3) = (data >> 24); 75 #else 76 *((byte *)address+3) = data; 77 *((byte *)address+2) = (data >> 8); 78 *((byte *)address+1) = (data >> 16); 79 *((byte *)address) = (data >> 24); 80 #endif 81 return; 82 } 83 else 84 *(UINT32 *)address = data; 85 } 86 #else 87 #define read_dword(address) *(UINT32 *)address 88 #define write_dword(address,data) *(UINT32 *)address=data 89 #endif 90 91 92 /****************************************************************************/ 93 94 95 /* Initialize the rendering data */ 96 void render_init(void) 97 { 98 render_reset(); 99 } 100 101 102 /* Reset the rendering data */ 103 void render_reset(void) 104 { 105 int i; 106 107 /* Clear display bitmap */ 108 // memset(sbitmap.data, 0, sbitmap.pitch * sbitmap.height); 109 110 /* Clear palette */ 111 for(i = 0; i < PALETTE_SIZE; i += 1) 112 { 113 palette_sync(i); 114 } 115 116 /* Invalidate pattern cache */ 117 sis_vram_dirty = 1; 118 memset(svram_dirty, 1, 0x200); 119 memset(cache, 0, CACHE_SIZE); 120 121 /* Set up viewport size */ 122 if(IS_GG) 123 { 124 vp_vstart = 24; 125 vp_vend = 168; 126 vp_hstart = 6; 127 vp_hend = 26; 128 } 129 else 130 { 131 vp_vstart = 0; 132 vp_vend = 192; 133 vp_hstart = 0; 134 vp_hend = 32; 135 } 136 137 /* Pick render routine */ 138 render_bg = IS_GG ? render_bg_gg : render_bg_sms; 139 } 140 141 142 /* Draw a line of the display */ 143 void render_line(int line) 144 { 145 /* Ensure we're within the viewport range */ 146 if((line < vp_vstart) || (line >= vp_vend)) return; 147 148 /* Point to current line in output buffer */ 149 linebuf = &internal_buffer[0]; 150 151 /* Update pattern cache */ 152 update_cache(); 153 154 /* Blank line */ 155 if( (!(vdp.reg[1] & 0x40)) || (((vdp.reg[2] & 1) == 0) && (IS_SMS))) 156 { 157 memset(linebuf + (vp_hstart << 3), BACKDROP_COLOR, BMP_WIDTH); 158 } 159 else 160 { 161 /* Draw background */ 162 render_bg(line); 163 164 /* Draw sprites */ 165 render_obj(line); 166 167 /* Blank leftmost column of display */ 168 if(vdp.reg[0] & 0x20) 169 { 170 memset(linebuf, BACKDROP_COLOR, 8); 171 } 172 } 173 for (int i=0; i<BMP_WIDTH ;i++) 174 internal_buffer[i] &= 0x1F; 175 emu_DrawLinePal16(&internal_buffer[BMP_X_OFFSET], BMP_WIDTH , BMP_HEIGHT, line); 176 } 177 178 179 /* Draw the Master System background */ 180 void render_bg_sms(int line) 181 { 182 int locked = 0; 183 int v_line = (line + vdp.reg[9]) % 224; 184 int v_row = (v_line & 7) << 3; 185 int hscroll = ((vdp.reg[0] & 0x40) && (line < 0x10)) ? 0 : (0x100 - vdp.reg[8]); 186 int column = vp_hstart; 187 UINT16 attr; 188 UINT16 *nt = (UINT16 *)&vdp.vram[vdp.ntab + ((v_line >> 3) << 6)]; 189 int nt_scroll = (hscroll >> 3); 190 int shift = (hscroll & 7); 191 UINT32 atex_mask; 192 UINT32 *cache_ptr; 193 UINT32 *linebuf_ptr = (UINT32 *)&linebuf[0 - shift]; 194 195 /* Draw first column (clipped) */ 196 if(shift) 197 { 198 int x, c, a; 199 200 attr = nt[(column + nt_scroll) & 0x1F]; 201 202 #ifndef LSB_FIRST 203 attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); 204 #endif 205 a = (attr >> 7) & 0x30; 206 207 for(x = shift; x < 8; x += 1) 208 { 209 c = cache[((attr & 0x7FF) << 6) | (v_row) | (x)]; 210 linebuf[(0 - shift) + (x) ] = ((c) | (a)); 211 } 212 213 column += 1; 214 } 215 216 /* Draw a line of the background */ 217 for(; column < vp_hend; column += 1) 218 { 219 /* Stop vertical scrolling for leftmost eight columns */ 220 if((vdp.reg[0] & 0x80) && (!locked) && (column >= 24)) 221 { 222 locked = 1; 223 v_row = (line & 7) << 3; 224 nt = (UINT16 *)&vdp.vram[((vdp.reg[2] << 10) & 0x3800) + ((line >> 3) << 6)]; 225 } 226 227 /* Get name table attribute word */ 228 attr = nt[(column + nt_scroll) & 0x1F]; 229 230 #ifndef LSB_FIRST 231 attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); 232 #endif 233 /* Expand priority and palette bits */ 234 atex_mask = atex[(attr >> 11) & 3]; 235 236 /* Point to a line of pattern data in cache */ 237 cache_ptr = (UINT32 *)&cache[((attr & 0x7FF) << 6) | (v_row)]; 238 239 /* Copy the left half, adding the attribute bits in */ 240 write_dword( &linebuf_ptr[(column << 1)] , read_dword( &cache_ptr[0] ) | (atex_mask)); 241 242 /* Copy the right half, adding the attribute bits in */ 243 write_dword( &linebuf_ptr[(column << 1) | (1)], read_dword( &cache_ptr[1] ) | (atex_mask)); 244 } 245 246 /* Draw last column (clipped) */ 247 if(shift) 248 { 249 int x, c, a; 250 251 char *p = &linebuf[(0 - shift)+(column << 3)]; 252 253 attr = nt[(column + nt_scroll) & 0x1F]; 254 255 #ifndef LSB_FIRST 256 attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); 257 #endif 258 a = (attr >> 7) & 0x30; 259 260 for(x = 0; x < shift; x += 1) 261 { 262 c = cache[((attr & 0x7FF) << 6) | (v_row) | (x)]; 263 p[x] = ((c) | (a)); 264 } 265 } 266 } 267 268 269 /* Draw the Game Gear background */ 270 void render_bg_gg(int line) 271 { 272 int v_line = (line + vdp.reg[9]) % 224; 273 int v_row = (v_line & 7) << 3; 274 int hscroll = (0x100 - vdp.reg[8]); 275 int column; 276 UINT16 attr; 277 UINT16 *nt = (UINT16 *)&vdp.vram[vdp.ntab + ((v_line >> 3) << 6)]; 278 int nt_scroll = (hscroll >> 3); 279 UINT32 atex_mask; 280 UINT32 *cache_ptr; 281 UINT32 *linebuf_ptr = (UINT32 *)&linebuf[0 - (hscroll & 7)]; 282 283 /* Draw a line of the background */ 284 for(column = vp_hstart; column <= vp_hend; column += 1) 285 { 286 /* Get name table attribute word */ 287 attr = nt[(column + nt_scroll) & 0x1F]; 288 289 #ifndef LSB_FIRST 290 attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); 291 #endif 292 /* Expand priority and palette bits */ 293 atex_mask = atex[(attr >> 11) & 3]; 294 295 /* Point to a line of pattern data in cache */ 296 cache_ptr = (UINT32 *)&cache[((attr & 0x7FF) << 6) | (v_row)]; 297 298 /* Copy the left half, adding the attribute bits in */ 299 write_dword( &linebuf_ptr[(column << 1)] , read_dword( &cache_ptr[0] ) | (atex_mask)); 300 301 /* Copy the right half, adding the attribute bits in */ 302 write_dword( &linebuf_ptr[(column << 1) | (1)], read_dword( &cache_ptr[1] ) | (atex_mask)); 303 } 304 } 305 306 307 /* Draw sprites */ 308 void render_obj(int line) 309 { 310 int i; 311 312 /* Sprite count for current line (8 max.) */ 313 int count = 0; 314 315 /* Sprite dimensions */ 316 int width = 8; 317 int height = (vdp.reg[1] & 0x02) ? 16 : 8; 318 319 /* Pointer to sprite attribute table */ 320 byte *st = (byte *)&vdp.vram[vdp.satb]; 321 322 /* Adjust dimensions for double size sprites */ 323 if(vdp.reg[1] & 0x01) 324 { 325 width *= 2; 326 height *= 2; 327 } 328 329 /* Draw sprites in front-to-back order */ 330 for(i = 0; i < 64; i += 1) 331 { 332 /* Sprite Y position */ 333 int yp = st[i]; 334 335 /* End of sprite list marker? */ 336 if(yp == 208) return; 337 338 /* Actual Y position is +1 */ 339 yp += 1; 340 341 /* Wrap Y coordinate for sprites > 240 */ 342 if(yp > 240) yp -= 256; 343 344 /* Check if sprite falls on current line */ 345 if((line >= yp) && (line < (yp + height))) 346 { 347 byte *linebuf_ptr; 348 349 /* Width of sprite */ 350 int start = 0; 351 int end = width; 352 353 /* Sprite X position */ 354 int xp = st[0x80 + (i << 1)]; 355 356 /* Pattern name */ 357 int n = st[0x81 + (i << 1)]; 358 359 /* Bump sprite count */ 360 count += 1; 361 362 /* Too many sprites on this line ? */ 363 if((vdp.limit) && (count == 9)) return; 364 365 /* X position shift */ 366 if(vdp.reg[0] & 0x08) xp -= 8; 367 368 /* Add MSB of pattern name */ 369 if(vdp.reg[6] & 0x04) n |= 0x0100; 370 371 /* Mask LSB for 8x16 sprites */ 372 if(vdp.reg[1] & 0x02) n &= 0x01FE; 373 374 /* Point to offset in line buffer */ 375 linebuf_ptr = (byte *)&linebuf[xp]; 376 377 /* Clip sprites on left edge */ 378 if(xp < 0) 379 { 380 start = (0 - xp); 381 } 382 383 /* Clip sprites on right edge */ 384 if((xp + width) > 256) 385 { 386 end = (256 - xp); 387 } 388 389 /* Draw double size sprite */ 390 if(vdp.reg[1] & 0x01) 391 { 392 int x; 393 byte *cache_ptr = (byte *)&cache[(n << 6) | (((line - yp) >> 1) << 3)]; 394 395 /* Draw sprite line */ 396 for(x = start; x < end; x += 1) 397 { 398 /* Source pixel from cache */ 399 byte sp = cache_ptr[(x >> 1)]; 400 401 /* Only draw opaque sprite pixels */ 402 if(sp) 403 { 404 /* Background pixel from line buffer */ 405 byte bg = linebuf_ptr[x]; 406 407 /* Look up result */ 408 linebuf_ptr[x] = lut[(bg << 8) | (sp)]; /* + COL_OFFSET; */ 409 410 /* Set sprite collision flag */ 411 if(bg & 0x40) vdp.status |= 0x20; 412 } 413 } 414 } 415 else /* Regular size sprite (8x8 / 8x16) */ 416 { 417 int x; 418 byte *cache_ptr = (byte *)&cache[(n << 6) | ((line - yp) << 3)]; 419 420 /* Draw sprite line */ 421 for(x = start; x < end; x += 1) 422 { 423 /* Source pixel from cache */ 424 byte sp = cache_ptr[x]; 425 426 /* Only draw opaque sprite pixels */ 427 if(sp) 428 { 429 /* Background pixel from line buffer */ 430 byte bg = linebuf_ptr[x]; 431 432 /* Look up result */ 433 linebuf_ptr[x] = lut[(bg << 8) | (sp)]; /* + COL_OFFSET; */ 434 435 /* Set sprite collision flag */ 436 if(bg & 0x40) vdp.status |= 0x20; 437 } 438 } 439 } 440 } 441 } 442 } 443 444 445 /* Update pattern cache with modified tiles */ 446 void update_cache(void) 447 { 448 int i, x, y, c; 449 int b0, b1, b2, b3; 450 int i0, i1, i2, i3; 451 452 if(!sis_vram_dirty) return; 453 sis_vram_dirty = 0; 454 455 for(i = 0; i < 0x200; i += 1) 456 { 457 if(svram_dirty[i]) 458 { 459 svram_dirty[i] = 0; 460 461 for(y = 0; y < 8; y += 1) 462 { 463 b0 = vdp.vram[(i << 5) | (y << 2) | (0)]; 464 b1 = vdp.vram[(i << 5) | (y << 2) | (1)]; 465 b2 = vdp.vram[(i << 5) | (y << 2) | (2)]; 466 b3 = vdp.vram[(i << 5) | (y << 2) | (3)]; 467 468 for(x = 0; x < 8; x += 1) 469 { 470 i0 = (b0 >> (7 - x)) & 1; 471 i1 = (b1 >> (7 - x)) & 1; 472 i2 = (b2 >> (7 - x)) & 1; 473 i3 = (b3 >> (7 - x)) & 1; 474 475 c = (i3 << 3 | i2 << 2 | i1 << 1 | i0); 476 477 cache[0x00000 | (i << 6) | ((y ) << 3) | (x )] = c; 478 cache[0x08000 | (i << 6) | ((y ) << 3) | (7-x)] = c; 479 cache[0x10000 | (i << 6) | ((7-y) << 3) | (x )] = c; 480 cache[0x18000 | (i << 6) | ((7-y) << 3) | (7-x)] = c; 481 } 482 } 483 } 484 } 485 } 486 487 /* Update a palette entry */ 488 void palette_sync(int index) 489 { 490 int r, g, b; 491 492 if(IS_GG) 493 { 494 r = ((vdp.cram[(index << 1) | 0] >> 1) & 7) << 5; 495 g = ((vdp.cram[(index << 1) | 0] >> 5) & 7) << 5; 496 b = ((vdp.cram[(index << 1) | 1] >> 1) & 7) << 5; 497 } 498 else 499 { 500 r = ((vdp.cram[index] >> 0) & 3) << 6; 501 g = ((vdp.cram[index] >> 2) & 3) << 6; 502 b = ((vdp.cram[index] >> 4) & 3) << 6; 503 } 504 505 emu_SetPaletteEntry(r,g,b,index); 506 507 sbitmap.pal.dirty[index] = sbitmap.pal.update = 1; 508 }