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