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  }