1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
2 // Copyright (C) 1999-2003 Forgotten
3 // Copyright (C) 2004 Forgotten and the VBA development team
4 
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or(at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 #include <mednafen/mednafen.h>
20 
21 #include "gbGlobals.h"
22 #include "gb.h"
23 
24 namespace MDFN_IEN_GB
25 {
26 
27 static const uint8 gbInvertTab[256] =
28 {
29   0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,
30   0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
31   0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,
32   0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
33   0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
34   0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
35   0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,
36   0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
37   0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,
38   0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
39   0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,
40   0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
41   0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,
42   0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
43   0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
44   0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
45   0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,
46   0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
47   0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,
48   0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
49   0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,
50   0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
51   0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,
52   0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
53   0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
54   0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
55   0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,
56   0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
57   0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,
58   0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
59   0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,
60   0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
61 };
62 
63 union __gblmt
64 {
65  uint16 cgb[160];
66  uint8 dmg[160];
67  uint32 dmg_32[40];
68 } gbLineMix;
69 
70 static uint16 gbLineBuffer[160];
71 
72 template<bool cgb_mode>
DrawBG(void)73 static INLINE void DrawBG(void)
74 {
75   uint8 * bank0;
76   uint8 * bank1;
77 
78   if(cgb_mode) {
79     bank0 = &gbVram[0x0000];
80     bank1 = &gbVram[0x2000];
81   } else {
82     bank0 = &gbVram[0x0000];
83     bank1 = NULL;
84   }
85 
86   int tile_map = 0x1800;
87   if((register_LCDC & 8) != 0)
88     tile_map = 0x1c00;
89 
90   int tile_pattern = 0x0800;
91 
92   if((register_LCDC & 16) != 0)
93     tile_pattern = 0x0000;
94 
95   int x = 0;
96   int y = register_LY;
97 
98   if(y >= 144)
99     return;
100 
101   int sx = register_SCX;
102   int sy = register_SCY;
103 
104   sy+=y;
105 
106   sy &= 255;
107 
108   int tx = sx >> 3;
109   int ty = sy >> 3;
110 
111   int bx = 1 << (7 - (sx & 7));
112   int by = sy & 7;
113 
114   int tile_map_line_y = tile_map + ty * 32;
115 
116   int tile_map_address = tile_map_line_y + tx;
117 
118   uint8 attrs = 0;
119   if(bank1 != NULL)
120     attrs = bank1[tile_map_address];
121 
122   uint8 tile = bank0[tile_map_address];
123 
124   tile_map_address++;
125 
126   if((register_LCDC & 16) == 0) {
127     if(tile < 128) tile += 128;
128     else tile -= 128;
129   }
130 
131   int tile_pattern_address = tile_pattern + tile * 16 + by*2;
132 
133   if(register_LCDC & 0x80)
134   {
135     if((register_LCDC & 0x01 || cgb_mode) && (gblayerSettings & 0x01))
136     {
137       while(x < 160) {
138         uint8 tile_a = 0;
139         uint8 tile_b = 0;
140 
141         if(attrs & 0x40) {
142           tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
143         }
144 
145         if(attrs & 0x08) {
146           tile_a = bank1[tile_pattern_address++];
147           tile_b = bank1[tile_pattern_address];
148         } else {
149           tile_a = bank0[tile_pattern_address++];
150           tile_b = bank0[tile_pattern_address];
151         }
152 
153         if(attrs & 0x20) {
154           tile_a = gbInvertTab[tile_a];
155           tile_b = gbInvertTab[tile_b];
156         }
157 
158         while(bx > 0) {
159           uint8 c = (tile_a & bx) ? 1 : 0;
160           c += ((tile_b & bx) ? 2 : 0);
161 
162           gbLineBuffer[x] = c; // mark the gbLineBuffer color
163 
164           if(attrs & 0x80)
165             gbLineBuffer[x] |= 0x300;
166 
167           if(cgb_mode)
168 	  {
169            c = c + (attrs & 7)*4;
170            gbLineMix.cgb[x] = gbPalette[c];
171           }
172 	  else
173 	  {
174 	   gbLineMix.dmg[x] = gbBgp[c];
175           }
176 
177           x++;
178           if(x >= 160)
179             break;
180           bx >>= 1;
181         }
182         tx++;
183         if(tx == 32)
184           tx = 0;
185         bx = 128;
186 
187         if(bank1)
188           attrs = bank1[tile_map_line_y + tx];
189 
190         tile = bank0[tile_map_line_y + tx];
191 
192         if((register_LCDC & 16) == 0) {
193           if(tile < 128) tile += 128;
194           else tile -= 128;
195         }
196         tile_pattern_address = tile_pattern + tile * 16 + by * 2;
197       }
198     }
199     else
200     {
201      int fill_color = cgb_mode ? gbPalette[0] : 0;
202 
203      for(int i = 0; i < 160; i++)
204      {
205       if(cgb_mode)
206        gbLineMix.cgb[i] = fill_color;
207       else
208        gbLineMix.dmg[i] = fill_color;
209 
210       gbLineBuffer[i] = 0;
211      }
212     }
213 
214     // do the window display
215     if((register_LCDC & 0x20) && (gblayerSettings & 0x02)) {
216       int wy = register_WY;
217 
218       if(y >= wy) {
219         int wx = register_WX;
220         wx -= 7;
221 
222         if( wx <= 159 && gbWindowLine <= 143) {
223 
224           tile_map = 0x1800;
225 
226           if((register_LCDC & 0x40) != 0)
227             tile_map = 0x1c00;
228 
229           if(gbWindowLine == -1) {
230             gbWindowLine = 0;
231           }
232 
233           tx = 0;
234           ty = gbWindowLine >> 3;
235 
236           bx = 128;
237           by = gbWindowLine & 7;
238 
239           if(wx < 0) {
240             bx >>= (-wx);
241             wx = 0;
242           }
243 
244           tile_map_line_y = tile_map + ty * 32;
245 
246           tile_map_address = tile_map_line_y + tx;
247 
248           x = wx;
249 
250           tile = bank0[tile_map_address];
251 
252           attrs = 0;
253 
254           if(bank1)
255             attrs = bank1[tile_map_address];
256           tile_map_address++;
257 
258           if((register_LCDC & 16) == 0) {
259             if(tile < 128) tile += 128;
260             else tile -= 128;
261           }
262 
263           tile_pattern_address = tile_pattern + tile * 16 + by*2;
264 
265           while(x < 160) {
266             uint8 tile_a = 0;
267             uint8 tile_b = 0;
268 
269             if(attrs & 0x40) {
270               tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
271             }
272 
273             if(attrs & 0x08) {
274               tile_a = bank1[tile_pattern_address++];
275               tile_b = bank1[tile_pattern_address];
276             } else {
277               tile_a = bank0[tile_pattern_address++];
278               tile_b = bank0[tile_pattern_address];
279             }
280 
281             if(attrs & 0x20) {
282               tile_a = gbInvertTab[tile_a];
283               tile_b = gbInvertTab[tile_b];
284             }
285 
286             while(bx > 0) {
287               uint8 c = (tile_a & bx) != 0 ? 1 : 0;
288               c += ((tile_b & bx) != 0 ? 2 : 0);
289 
290               if(attrs & 0x80)
291                 gbLineBuffer[x] = 0x300 + c;
292               else
293                 gbLineBuffer[x] = 0x100 + c;
294 
295               if(cgb_mode)
296 	      {
297                c = c + (attrs & 7) * 4;
298                gbLineMix.cgb[x] = gbPalette[c];
299               }
300 	      else
301 	      {
302                gbLineMix.dmg[x] = gbBgp[c];
303               }
304 
305               x++;
306               if(x >= 160)
307                 break;
308               bx >>= 1;
309             }
310             tx++;
311             if(tx == 32)
312               tx = 0;
313             bx = 128;
314             tile = bank0[tile_map_line_y + tx];
315             if(bank1)
316               attrs = bank1[tile_map_line_y + tx];
317 
318             if((register_LCDC & 16) == 0) {
319               if(tile < 128) tile += 128;
320               else tile -= 128;
321             }
322             tile_pattern_address = tile_pattern + tile * 16 + by * 2;
323           }
324           gbWindowLine++;
325         }
326       }
327     }
328   }
329   else
330   {
331    const uint8 fill_color = cgb_mode ? gbPalette[0] : 12;
332 
333    for(int i = 0; i < 160; i++)
334    {
335     if(cgb_mode)
336      gbLineMix.cgb[i] = fill_color;
337     else
338      gbLineMix.dmg[i] = fill_color;
339 
340     gbLineBuffer[i] = 0;
341    }
342   }
343 }
344 
345 template<bool cgb_mode>
DrawSpriteTile(int tile,int x,int y,int t,int flags,int size,int spriteNumber)346 static INLINE void DrawSpriteTile(int tile, int x,int y,int t, int flags,
347                       int size,int spriteNumber)
348 {
349   uint8 * bank0;
350   uint8 * bank1;
351   if(cgb_mode) {
352     if(register_VBK & 1) {
353       bank0 = &gbVram[0x0000];
354       bank1 = &gbVram[0x2000];
355     } else {
356       bank0 = &gbVram[0x0000];
357       bank1 = &gbVram[0x2000];
358     }
359   } else {
360     bank0 = &gbVram[0x0000];
361     bank1 = NULL;
362   }
363 
364   int init = 0x0000;
365 
366   uint8 *pal = gbObp0;
367 
368   int flipx = (flags & 0x20);
369   int flipy = (flags & 0x40);
370 
371   if((flags & 0x10))
372     pal = gbObp1;
373 
374   if(flipy) {
375     t = (size ? 15 : 7) - t;
376   }
377 
378   int prio =  flags & 0x80;
379 
380   int address = init + tile * 16 + 2*t;
381   int a = 0;
382   int b = 0;
383 
384   if(cgb_mode && flags & 0x08) {
385     a = bank1[address++];
386     b = bank1[address++];
387   } else {
388     a = bank0[address++];
389     b = bank0[address++];
390   }
391 
392   for(int xx = 0; xx < 8; xx++) {
393     uint8 mask = 1 << (7-xx);
394     uint8 c = 0;
395     if( (a & mask))
396       c++;
397     if( (b & mask))
398       c+=2;
399 
400     if(c==0) continue;
401 
402     int xxx = xx+x;
403     if(flipx)
404       xxx = (7-xx+x);
405 
406     if(xxx < 0 || xxx > 159)
407       continue;
408 
409     uint16 color = gbLineBuffer[xxx];
410 
411     if(prio) {
412       if(color < 0x200 && ((color & 0xFF) != 0))
413         continue;
414     }
415     if(color >= 0x300 && color != 0x300)
416       continue;
417     else if(color >= 0x200 && color < 0x300) {
418       int sprite = color & 0xff;
419 
420       int spriteX = gbOAM[4 * sprite + 1] - 8;
421 
422       if(spriteX == x) {
423         if(sprite < spriteNumber)
424           continue;
425       } else {
426         if(cgb_mode) {
427           if(sprite < spriteNumber)
428             continue;
429         } else {
430           if(spriteX < x+8)
431             continue;
432         }
433       }
434     }
435 
436 
437     gbLineBuffer[xxx] = 0x200 + spriteNumber;
438 
439     // make sure that sprites will work even in CGB mode
440     if(cgb_mode)
441     {
442      c = c + (flags & 0x07)*4 + 32;
443      gbLineMix.cgb[xxx] = gbPalette[c];
444     }
445     else
446     {
447      gbLineMix.dmg[xxx] = pal[c];
448     }
449   }
450 }
451 
452 template<bool cgb_mode>
DrawSprites(void)453 static void DrawSprites(void)
454 {
455   int x = 0;
456   int y = 0;
457   int count = 0;
458   int size = (register_LCDC & 4) >> 2;
459 
460   if(!(register_LCDC & 0x80))
461     return;
462 
463   if((register_LCDC & 2) && (gblayerSettings & 0x04)) {
464     int yc = register_LY;
465 
466     int address = 0;
467     for(int i = 0; i < 40; i++) {
468       y = gbOAM[address++];
469       x = gbOAM[address++];
470       int tile = gbOAM[address++] &~ size;
471       int flags = gbOAM[address++];
472 
473       if(x > 0 && y > 0 && x < 168 && y < 160) {
474         // check if sprite intersects current line
475         int t = yc - y + 16;
476 
477         if((unsigned)t < (8U << size)) {
478           DrawSpriteTile<cgb_mode>(tile, x-8, yc, t, flags, size, i);
479           count++;
480         }
481       }
482       // sprite limit reached!
483       if(count >= 10)
484         break;
485     }
486   }
487 }
488 
489 
gbRenderLine(void)490 void gbRenderLine(void)
491 {
492  if(gbCgbMode)
493  {
494   DrawBG<true>();
495   DrawSprites<true>();
496  }
497  else
498  {
499   DrawBG<false>();
500   DrawSprites<false>();
501  }
502 }
503 
504 }
505