SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Core/display.c
1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <string.h>
5 #include <math.h>
6 #include "gb.h"
7
8 const GB_palette_t GB_PALETTE_GREY = {{{0x00, 0x00, 0x00}, {0x55, 0x55, 0x55}, {0xAA, 0xAA, 0xAA}, {0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF}}};
9 const GB_palette_t GB_PALETTE_DMG = {{{0x08, 0x18, 0x10}, {0x39, 0x61, 0x39}, {0x84, 0xA5, 0x63}, {0xC6, 0xDE, 0x8C}, {0xD2, 0xE6, 0xA6}}};
10 const GB_palette_t GB_PALETTE_MGB = {{{0x07, 0x10, 0x0E}, {0x3A, 0x4C, 0x3A}, {0x81, 0x8D, 0x66}, {0xC2, 0xCE, 0x93}, {0xCF, 0xDA, 0xAC}}};
11 const GB_palette_t GB_PALETTE_GBL = {{{0x0A, 0x1C, 0x15}, {0x35, 0x78, 0x62}, {0x56, 0xB4, 0x95}, {0x7F, 0xE2, 0xC3}, {0x91, 0xEA, 0xD0}}};
12
13 void GB_update_dmg_palette(GB_gameboy_t *gb)
14 {
15 const GB_palette_t *palette = gb->dmg_palette ?: &GB_PALETTE_GREY;
16 if (gb->rgb_encode_callback && !GB_is_cgb(gb)) {
17 gb->object_palettes_rgb[4] = gb->object_palettes_rgb[0] = gb->background_palettes_rgb[0] =
18 gb->rgb_encode_callback(gb, palette->colors[3].r, palette->colors[3].g, palette->colors[3].b);
19 gb->object_palettes_rgb[5] = gb->object_palettes_rgb[1] = gb->background_palettes_rgb[1] =
20 gb->rgb_encode_callback(gb, palette->colors[2].r, palette->colors[2].g, palette->colors[2].b);
21 gb->object_palettes_rgb[6] = gb->object_palettes_rgb[2] = gb->background_palettes_rgb[2] =
22 gb->rgb_encode_callback(gb, palette->colors[1].r, palette->colors[1].g, palette->colors[1].b);
23 gb->object_palettes_rgb[7] = gb->object_palettes_rgb[3] = gb->background_palettes_rgb[3] =
24 gb->rgb_encode_callback(gb, palette->colors[0].r, palette->colors[0].g, palette->colors[0].b);
25
26 // LCD off color
27 gb->background_palettes_rgb[4] =
28 gb->rgb_encode_callback(gb, palette->colors[4].r, palette->colors[4].g, palette->colors[4].b);
29 }
30 }
31
32 void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette)
33 {
34 gb->dmg_palette = palette;
35 GB_update_dmg_palette(gb);
36 }
37
38 const GB_palette_t *GB_get_palette(GB_gameboy_t *gb)
39 {
40 return gb->dmg_palette;
41 }
42
43 void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback)
44 {
45 if (!callback) {
46 GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
47 }
48 gb->vblank_callback = callback;
49 }
50
51 void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
52 {
53 if (!callback) {
54 GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
55 }
56
57 gb->rgb_encode_callback = callback;
58 GB_update_dmg_palette(gb);
59
60 for (unsigned i = 0; i < 32; i++) {
61 GB_palette_changed(gb, true, i * 2);
62 GB_palette_changed(gb, false, i * 2);
63 }
64 }
65
66 void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output)
67 {
68 GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
69 gb->screen = output;
70 }
71
72 /* FIFO functions */
73
74 static inline unsigned fifo_size(GB_fifo_t *fifo)
75 {
76 return fifo->size;
77 }
78
79 static void fifo_clear(GB_fifo_t *fifo)
80 {
81 fifo->read_end = fifo->size = 0;
82 }
83
84 static const GB_fifo_item_t *fifo_pop(GB_fifo_t *fifo)
85 {
86 assert(fifo->size);
87 assert(fifo->size <= 8);
88 GB_fifo_item_t *ret = &fifo->fifo[fifo->read_end];
89 fifo->read_end++;
90 fifo->read_end &= (GB_FIFO_LENGTH - 1);
91 fifo->size--;
92 return ret;
93 }
94
95 static void fifo_push_bg_row(GB_fifo_t *fifo, uint8_t lower, uint8_t upper, uint8_t palette, bool bg_priority, bool flip_x)
96 {
97 assert(fifo->size == 0);
98 fifo->size = 8;
99 if (!flip_x) {
100 unrolled for (unsigned i = 0; i < 8; i++) {
101 fifo->fifo[i] = (GB_fifo_item_t) {
102 (lower >> 7) | ((upper >> 7) << 1),
103 palette,
104 0,
105 bg_priority,
106 };
107 lower <<= 1;
108 upper <<= 1;
109 }
110 }
111 else {
112 unrolled for (unsigned i = 0; i < 8; i++) {
113 fifo->fifo[i] = (GB_fifo_item_t) {
114 (lower & 1) | ((upper & 1) << 1),
115 palette,
116 0,
117 bg_priority,
118 };
119 lower >>= 1;
120 upper >>= 1;
121 }
122 }
123 }
124
125 static void fifo_overlay_object_row(GB_fifo_t *fifo, uint8_t lower, uint8_t upper, uint8_t palette, bool bg_priority, uint8_t priority, bool flip_x)
126 {
127 while (fifo->size < GB_FIFO_LENGTH) {
128 fifo->fifo[(fifo->read_end + fifo->size) & (GB_FIFO_LENGTH - 1)] = (GB_fifo_item_t) {0,};
129 fifo->size++;
130 }
131
132 uint8_t flip_xor = flip_x? 0: 0x7;
133
134 unrolled for (unsigned i = 8; i--;) {
135 uint8_t pixel = (lower >> 7) | ((upper >> 7) << 1);
136 GB_fifo_item_t *target = &fifo->fifo[(fifo->read_end + (i ^ flip_xor)) & (GB_FIFO_LENGTH - 1)];
137 if (pixel != 0 && (target->pixel == 0 || target->priority > priority)) {
138 target->pixel = pixel;
139 target->palette = palette;
140 target->bg_priority = bg_priority;
141 target->priority = priority;
142 }
143 lower <<= 1;
144 upper <<= 1;
145 }
146 }
147
148
149 /*
150 Each line is 456 cycles. Without scrolling, objects or a window:
151 Mode 2 - 80 cycles / OAM Transfer
152 Mode 3 - 172 cycles / Rendering
153 Mode 0 - 204 cycles / HBlank
154
155 Mode 1 is VBlank
156 */
157
158 #define MODE2_LENGTH (80)
159 #define LINE_LENGTH (456)
160 #define LINES (144)
161 #define WIDTH (160)
162 #define BORDERED_WIDTH 256
163 #define BORDERED_HEIGHT 224
164 #define VIRTUAL_LINES (LCDC_PERIOD / LINE_LENGTH) // = 154
165
166 typedef struct __attribute__((packed)) {
167 uint8_t y;
168 uint8_t x;
169 uint8_t tile;
170 uint8_t flags;
171 } object_t;
172
173 void GB_display_vblank(GB_gameboy_t *gb, GB_vblank_type_t type)
174 {
175 gb->vblank_just_occured = true;
176 gb->cycles_since_vblank_callback = 0;
177 gb->lcd_disabled_outside_of_vblank = false;
178
179 #ifndef GB_DISABLE_DEBUGGER
180 gb->last_frame_idle_cycles = gb->current_frame_idle_cycles;
181 gb->last_frame_busy_cycles = gb->current_frame_busy_cycles;
182 gb->current_frame_idle_cycles = 0;
183 gb->current_frame_busy_cycles = 0;
184
185 if (gb->usage_frame_count++ == 60) {
186 gb->last_second_idle_cycles = gb->current_second_idle_cycles;
187 gb->last_second_busy_cycles = gb->current_second_busy_cycles;
188 gb->current_second_idle_cycles = 0;
189 gb->current_second_busy_cycles = 0;
190 gb->usage_frame_count = 0;
191 }
192 #endif
193
194 /* TODO: Slow in turbo mode! */
195 if (GB_is_hle_sgb(gb)) {
196 GB_sgb_render(gb, type != GB_VBLANK_TYPE_NORMAL_FRAME);
197 }
198
199 if (gb->turbo) {
200 #ifndef GB_DISABLE_DEBUGGER
201 if (unlikely(gb->backstep_instructions)) return;
202 #endif
203 if (GB_timing_sync_turbo(gb)) {
204 if (gb->vblank_callback && gb->enable_skipped_frame_vblank_callbacks) {
205 gb->vblank_callback(gb, GB_VBLANK_TYPE_SKIPPED_FRAME);
206 }
207 return;
208 }
209 }
210
211 if (GB_is_cgb(gb) && type == GB_VBLANK_TYPE_NORMAL_FRAME && gb->frame_repeat_countdown > 0 && gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
212 GB_handle_rumble(gb);
213
214 if (gb->vblank_callback) {
215 gb->vblank_callback(gb, GB_VBLANK_TYPE_REPEAT);
216 }
217 GB_timing_sync(gb);
218 return;
219 }
220
221 bool is_ppu_stopped = !GB_is_cgb(gb) && gb->stopped && gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE;
222
223 if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE) || is_ppu_stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
224 /* LCD is off, set screen to white or black (if LCD is on in stop mode) */
225 if (!GB_is_sgb(gb)) {
226 uint32_t color = 0;
227 if (GB_is_cgb(gb)) {
228 color = GB_convert_rgb15(gb, 0x7FFF, false);
229 }
230 else {
231 color = is_ppu_stopped ?
232 gb->background_palettes_rgb[0] :
233 gb->background_palettes_rgb[4];
234 }
235 if (gb->border_mode == GB_BORDER_ALWAYS) {
236 for (unsigned y = 0; y < LINES; y++) {
237 for (unsigned x = 0; x < WIDTH; x++) {
238 gb ->screen[x + y * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH] = color;
239 }
240 }
241 }
242 else {
243 for (unsigned i = 0; i < WIDTH * LINES; i++) {
244 gb ->screen[i] = color;
245 }
246 }
247 }
248 }
249
250 if (!gb->disable_rendering && gb->border_mode == GB_BORDER_ALWAYS && !GB_is_sgb(gb)) {
251 GB_borrow_sgb_border(gb);
252 uint32_t border_colors[16 * 4];
253
254 if (!gb->has_sgb_border && GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_E) {
255 uint16_t colors[] = {
256 0x2095, 0x5129, 0x1EAF, 0x1EBA, 0x4648,
257 0x30DA, 0x69AD, 0x2B57, 0x2B5D, 0x632C,
258 0x1050, 0x3C84, 0x0E07, 0x0E18, 0x2964,
259 };
260 unsigned index = gb->rom? gb->rom[0x14E] % 5 : 0;
261 if (gb->model == GB_MODEL_CGB_0) {
262 index = 1; // CGB 0 was only available in indigo!
263 }
264 else if (gb->model == GB_MODEL_CGB_A) {
265 index = 0; // CGB A was only available in red!
266 }
267 gb->borrowed_border.palette[0] = LE16(colors[index]);
268 gb->borrowed_border.palette[10] = LE16(colors[5 + index]);
269 gb->borrowed_border.palette[14] = LE16(colors[10 + index]);
270
271 }
272
273 for (unsigned i = 0; i < 16 * 4; i++) {
274 border_colors[i] = GB_convert_rgb15(gb, LE16(gb->borrowed_border.palette[i]), true);
275 }
276
277 for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
278 for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
279 if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
280 continue;
281 }
282 uint16_t tile = LE16(gb->borrowed_border.map[tile_x + tile_y * 32]);
283 uint8_t flip_x = (tile & 0x4000)? 0:7;
284 uint8_t flip_y = (tile & 0x8000)? 7:0;
285 uint8_t palette = (tile >> 10) & 3;
286 for (unsigned y = 0; y < 8; y++) {
287 unsigned base = (tile & 0xFF) * 32 + (y ^ flip_y) * 2;
288 for (unsigned x = 0; x < 8; x++) {
289 uint8_t bit = 1 << (x ^ flip_x);
290 uint8_t color = ((gb->borrowed_border.tiles[base] & bit) ? 1 : 0) |
291 ((gb->borrowed_border.tiles[base + 1] & bit) ? 2 : 0) |
292 ((gb->borrowed_border.tiles[base + 16] & bit) ? 4 : 0) |
293 ((gb->borrowed_border.tiles[base + 17] & bit) ? 8 : 0);
294 uint32_t *output = gb->screen + tile_x * 8 + x + (tile_y * 8 + y) * 256;
295 if (color == 0) {
296 *output = border_colors[0];
297 }
298 else {
299 *output = border_colors[color + palette * 16];
300 }
301 }
302 }
303 }
304 }
305 }
306 GB_handle_rumble(gb);
307
308 if (gb->vblank_callback) {
309 gb->vblank_callback(gb, type);
310 }
311 GB_timing_sync(gb);
312 }
313
314 static inline void temperature_tint(double temperature, double *r, double *g, double *b)
315 {
316 if (temperature >= 0) {
317 *r = 1;
318 *g = pow(1 - temperature, 0.375);
319 if (temperature >= 0.75) {
320 *b = 0;
321 }
322 else {
323 *b = sqrt(0.75 - temperature) / sqrt(0.75);
324 }
325 }
326 else {
327 *b = 1;
328 double squared = pow(temperature, 2);
329 *g = 0.125 * squared + 0.3 * temperature + 1.0;
330 *r = 0.21875 * squared + 0.5 * temperature + 1.0;
331 }
332 }
333
334 static inline uint8_t scale_channel(uint8_t x)
335 {
336 return (x << 3) | (x >> 2);
337 }
338
339 static inline uint8_t scale_channel_with_curve(uint8_t x)
340 {
341 return inline_const(uint8_t[], {0,6,12,20,28,36,45,56,66,76,88,100,113,125,137,149,161,172,182,192,202,210,218,225,232,238,243,247,250,252,254,255})[x];
342 }
343
344 static inline uint8_t scale_channel_with_curve_agb(uint8_t x)
345 {
346 return inline_const(uint8_t[], {0,3,8,14,20,26,33,40,47,54,62,70,78,86,94,103,112,120,129,138,147,157,166,176,185,195,205,215,225,235,245,255})[x];
347 }
348
349 static inline uint8_t scale_channel_with_curve_sgb(uint8_t x)
350 {
351 return inline_const(uint8_t[], {0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255})[x];
352 }
353
354
355 uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border)
356 {
357 uint8_t r = (color) & 0x1F;
358 uint8_t g = (color >> 5) & 0x1F;
359 uint8_t b = (color >> 10) & 0x1F;
360
361 if (gb->color_correction_mode == GB_COLOR_CORRECTION_DISABLED || (for_border && !gb->has_sgb_border)) {
362 r = scale_channel(r);
363 g = scale_channel(g);
364 b = scale_channel(b);
365 }
366 else if (GB_is_sgb(gb) || for_border) {
367 r = scale_channel_with_curve_sgb(r);
368 g = scale_channel_with_curve_sgb(g);
369 b = scale_channel_with_curve_sgb(b);
370 }
371 else {
372 bool agb = gb->model > GB_MODEL_CGB_E;
373 r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r);
374 g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g);
375 b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b);
376
377 if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
378 uint8_t new_r, new_g, new_b;
379 if (g != b) { // Minor optimization
380 double gamma = 2.2;
381 if (gb->color_correction_mode < GB_COLOR_CORRECTION_REDUCE_CONTRAST) {
382 /* Don't use absolutely gamma-correct mixing for the high-contrast
383 modes, to prevent the blue hues from being too washed out */
384 gamma = 1.6;
385 }
386
387 // TODO: Optimze pow out using a LUT
388 if (agb) {
389 new_g = round(pow((pow(g / 255.0, gamma) * 5 + pow(b / 255.0, gamma)) / 6, 1 / gamma) * 255);
390 }
391 else {
392 new_g = round(pow((pow(g / 255.0, gamma) * 3 + pow(b / 255.0, gamma)) / 4, 1 / gamma) * 255);
393 }
394 }
395 else {
396 new_g = g;
397 }
398
399 new_r = r;
400 new_b = b;
401 if (gb->color_correction_mode == GB_COLOR_CORRECTION_REDUCE_CONTRAST) {
402 r = new_r;
403 g = new_g;
404 b = new_b;
405
406 new_r = new_r * 15 / 16 + ( g + b) / 32;
407 new_g = new_g * 15 / 16 + (r + b) / 32;
408 new_b = new_b * 15 / 16 + (r + g ) / 32;
409
410 if (agb) {
411 new_r = new_r * (224 - 20) / 255 + 20;
412 new_g = new_g * (220 - 18) / 255 + 18;
413 new_b = new_b * (216 - 16) / 255 + 16;
414 }
415 else {
416 new_r = new_r * (220 - 40) / 255 + 40;
417 new_g = new_g * (224 - 36) / 255 + 36;
418 new_b = new_b * (216 - 32) / 255 + 32;
419 }
420 }
421 else if (gb->color_correction_mode == GB_COLOR_CORRECTION_LOW_CONTRAST) {
422 r = new_r;
423 g = new_g;
424 b = new_b;
425
426 new_r = new_r * 15 / 16 + ( g + b) / 32;
427 new_g = new_g * 15 / 16 + (r + b) / 32;
428 new_b = new_b * 15 / 16 + (r + g ) / 32;
429
430 if (agb) {
431 new_r = new_r * (167 - 27) / 255 + 27;
432 new_g = new_g * (165 - 24) / 255 + 24;
433 new_b = new_b * (157 - 22) / 255 + 22;
434 }
435 else {
436 new_r = new_r * (162 - 45) / 255 + 45;
437 new_g = new_g * (167 - 41) / 255 + 41;
438 new_b = new_b * (157 - 38) / 255 + 38;
439 }
440 }
441 else if (gb->color_correction_mode == GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST) {
442 uint8_t old_max = MAX(r, MAX(g, b));
443 uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
444
445 if (new_max != 0) {
446 new_r = new_r * old_max / new_max;
447 new_g = new_g * old_max / new_max;
448 new_b = new_b * old_max / new_max;
449 }
450
451 uint8_t old_min = MIN(r, MIN(g, b));
452 uint8_t new_min = MIN(new_r, MIN(new_g, new_b));
453
454 if (new_min != 0xFF) {
455 new_r = 0xFF - (0xFF - new_r) * (0xFF - old_min) / (0xFF - new_min);
456 new_g = 0xFF - (0xFF - new_g) * (0xFF - old_min) / (0xFF - new_min);
457 new_b = 0xFF - (0xFF - new_b) * (0xFF - old_min) / (0xFF - new_min);
458 }
459 }
460 r = new_r;
461 g = new_g;
462 b = new_b;
463 }
464 }
465
466 if (gb->light_temperature) {
467 double light_r, light_g, light_b;
468 temperature_tint(gb->light_temperature, &light_r, &light_g, &light_b);
469 r = round(light_r * r);
470 g = round(light_g * g);
471 b = round(light_b * b);
472 }
473
474 return gb->rgb_encode_callback(gb, r, g, b);
475 }
476
477 void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index)
478 {
479 if (!gb->rgb_encode_callback || !GB_is_cgb(gb)) return;
480 uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->object_palettes_data;
481 uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8);
482
483 (background_palette? gb->background_palettes_rgb : gb->object_palettes_rgb)[index / 2] = GB_convert_rgb15(gb, color, false);
484 }
485
486 void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode)
487 {
488 gb->color_correction_mode = mode;
489 if (GB_is_cgb(gb)) {
490 nounroll for (unsigned i = 0; i < 32; i++) {
491 GB_palette_changed(gb, false, i * 2);
492 GB_palette_changed(gb, true, i * 2);
493 }
494 }
495 }
496
497 void GB_set_light_temperature(GB_gameboy_t *gb, double temperature)
498 {
499 gb->light_temperature = temperature;
500 if (GB_is_cgb(gb)) {
501 nounroll for (unsigned i = 0; i < 32; i++) {
502 GB_palette_changed(gb, false, i * 2);
503 GB_palette_changed(gb, true, i * 2);
504 }
505 }
506 }
507
508 static void wy_check(GB_gameboy_t *gb)
509 {
510 if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)) return;
511
512 uint8_t comparison = gb->current_line;
513 if ((!GB_is_cgb(gb) || gb->cgb_double_speed) && gb->ly_for_comparison != (uint8_t)-1) {
514 comparison = gb->ly_for_comparison;
515 }
516
517 if ((gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) &&
518 gb->io_registers[GB_IO_WY] == comparison) {
519 gb->wy_triggered = true;
520 }
521 }
522
523 void GB_STAT_update(GB_gameboy_t *gb)
524 {
525 if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)) return;
526 if (GB_is_dma_active(gb) && (gb->io_registers[GB_IO_STAT] & 3) == 2) {
527 gb->io_registers[GB_IO_STAT] &= ~3;
528 }
529
530 bool previous_interrupt_line = gb->stat_interrupt_line;
531 /* Set LY=LYC bit */
532 if (gb->ly_for_comparison != (uint16_t)-1 || (gb->model <= GB_MODEL_CGB_C && !gb->cgb_double_speed)) {
533 if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) {
534 gb->lyc_interrupt_line = true;
535 gb->io_registers[GB_IO_STAT] |= 4;
536 }
537 else {
538 if (gb->ly_for_comparison != (uint16_t)-1) {
539 gb->lyc_interrupt_line = false;
540 }
541 gb->io_registers[GB_IO_STAT] &= ~4;
542 }
543 }
544
545 switch (gb->mode_for_interrupt) {
546 case 0: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 8; break;
547 case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break;
548 case 2: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; break;
549 default: gb->stat_interrupt_line = false;
550 }
551
552 /* User requested a LY=LYC interrupt and the LY=LYC bit is on */
553 if ((gb->io_registers[GB_IO_STAT] & 0x40) && gb->lyc_interrupt_line) {
554 gb->stat_interrupt_line = true;
555 }
556
557 if (gb->stat_interrupt_line && !previous_interrupt_line) {
558 gb->io_registers[GB_IO_IF] |= 2;
559 }
560 }
561
562 void GB_lcd_off(GB_gameboy_t *gb)
563 {
564 gb->cycles_for_line = 0;
565 gb->display_state = 0;
566 gb->display_cycles = 0;
567 /* When the LCD is disabled, state is constant */
568
569 if (gb->hdma_on_hblank && (gb->io_registers[GB_IO_STAT] & 3)) {
570 gb->hdma_on = true;
571 }
572
573 /* When the LCD is off, LY is 0 and STAT mode is 0. */
574 gb->io_registers[GB_IO_LY] = 0;
575 gb->io_registers[GB_IO_STAT] &= ~3;
576
577
578 gb->oam_read_blocked = false;
579 gb->vram_read_blocked = false;
580 gb->oam_write_blocked = false;
581 gb->vram_write_blocked = false;
582 gb->cgb_palettes_blocked = false;
583
584 gb->current_line = 0;
585 gb->ly_for_comparison = 0;
586
587 gb->accessed_oam_row = -1;
588 gb->wy_triggered = false;
589
590 if (unlikely(gb->lcd_line_callback)) {
591 gb->lcd_line_callback(gb, 0);
592 }
593 }
594
595 static inline uint8_t oam_read(GB_gameboy_t *gb, uint8_t addr)
596 {
597 if (unlikely(gb->oam_ppu_blocked)) {
598 return 0xFF;
599 }
600 if (unlikely(gb->dma_current_dest <= 0xA0 && gb->dma_current_dest > 0)) { // TODO: what happens in the last and first M cycles?
601 if (gb->hdma_in_progress) {
602 return GB_read_oam(gb, (gb->hdma_current_src & ~1) | (addr & 1));
603 }
604 if (gb->dma_current_dest != 0xA0) {
605 return gb->oam[(gb->dma_current_dest & ~1) | (addr & 1)];
606 }
607 }
608 return gb->oam[addr];
609 }
610
611 static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
612 {
613 if (likely(!GB_is_dma_active(gb) || gb->halted || gb->stopped)) {
614 gb->mode2_y_bus = oam_read(gb, index * 4);
615 gb->mode2_x_bus = oam_read(gb, index * 4 + 1);
616 }
617
618 if (unlikely(gb->n_visible_objs == 10)) return;
619
620 /* TODO: It appears that DMA blocks PPU access to OAM, but it needs verification. */
621 if (unlikely(GB_is_dma_active(gb) && (gb->halted || gb->stopped))) {
622 if (gb->model < GB_MODEL_CGB_E) {
623 return;
624 }
625 /* CGB-0 to CGB-D: Halted DMA blocks Mode 2;
626 Pre-CGB: Unit specific behavior, some units read FFs, some units read using
627 several different corruption pattterns. For simplicity, we emulate
628 FFs. */
629 }
630
631 if (unlikely(gb->oam_ppu_blocked)) {
632 return;
633 }
634
635 bool height_16 = (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_SIZE) != 0;
636 signed y = gb->mode2_y_bus - 16;
637 /* This reverse sorts the visible objects by location and priority */
638 if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
639 unsigned j = 0;
640 for (; j < gb->n_visible_objs; j++) {
641 if (gb->objects_x[j] <= gb->mode2_x_bus) break;
642 }
643 memmove(gb->visible_objs + j + 1, gb->visible_objs + j, gb->n_visible_objs - j);
644 memmove(gb->objects_x + j + 1, gb->objects_x + j, gb->n_visible_objs - j);
645 memmove(gb->objects_y + j + 1, gb->objects_y + j, gb->n_visible_objs - j);
646 gb->visible_objs[j] = index;
647 gb->objects_x[j] = gb->mode2_x_bus;
648 gb->objects_y[j] = gb->mode2_y_bus;
649 gb->n_visible_objs++;
650 }
651 }
652
653 static void render_pixel_if_possible(GB_gameboy_t *gb)
654 {
655 const GB_fifo_item_t *fifo_item = NULL;
656 const GB_fifo_item_t *oam_fifo_item = NULL;
657 bool draw_oam = false;
658 bool bg_enabled = true, bg_priority = false;
659
660 // Rendering (including scrolling adjustment) does not occur as long as an object at x=0 is pending
661 if (gb->n_visible_objs != 0 &&
662 (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN || GB_is_cgb(gb)) &&
663 gb->objects_x[gb->n_visible_objs - 1] == 0) {
664 return;
665 }
666
667 if (unlikely(!fifo_size(&gb->bg_fifo))) return;
668
669 if (unlikely(gb->insert_bg_pixel)) {
670 gb->insert_bg_pixel = false;
671 fifo_item = ({static const GB_fifo_item_t empty_item = {0,}; &empty_item;});
672 }
673 else {
674 fifo_item = fifo_pop(&gb->bg_fifo);
675 }
676 bg_priority = fifo_item->bg_priority;
677
678 if (fifo_size(&gb->oam_fifo)) {
679 oam_fifo_item = fifo_pop(&gb->oam_fifo);
680 if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN) && unlikely(!gb->objects_disabled)) {
681 draw_oam = true;
682 bg_priority |= oam_fifo_item->bg_priority;
683 }
684 }
685
686 // (gb->position_in_line + 16 < 8) is (gb->position_in_line < -8) in unsigned logic
687 if (((uint8_t)(gb->position_in_line + 16) < 8)) {
688 if (gb->position_in_line == (uint8_t)-17) {
689 gb->position_in_line = -16;
690 }
691 else if ((gb->position_in_line & 7) == (gb->io_registers[GB_IO_SCX] & 7)) {
692 gb->position_in_line = -8;
693 }
694 else if (gb->window_is_being_fetched && (gb->position_in_line & 7) == 6 && (gb->io_registers[GB_IO_SCX] & 7) == 7) { // TODO: Why does this happen?
695 gb->position_in_line = -8;
696 }
697 else if (gb->position_in_line == (uint8_t) -9) {
698 gb->position_in_line = -16;
699 return;
700 }
701 else {
702 gb->line_has_fractional_scrolling = true;
703 }
704 }
705
706 gb->window_is_being_fetched = false;
707
708 /* Drop pixels for scrollings */
709 if (gb->position_in_line >= 160 || (gb->disable_rendering && !gb->sgb)) {
710 gb->position_in_line++;
711 return;
712 }
713
714 /* Mixing */
715
716 if ((gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN) == 0) {
717 if (gb->cgb_mode) {
718 bg_priority = false;
719 }
720 else {
721 bg_enabled = false;
722 }
723 }
724
725 if (unlikely(gb->background_disabled)) {
726 bg_enabled = false;
727 static const GB_fifo_item_t empty_item = {0,};
728 fifo_item = &empty_item;
729 }
730
731 uint8_t icd_pixel = 0;
732 uint32_t *dest = NULL;
733 if (!gb->sgb) {
734 if (gb->border_mode != GB_BORDER_ALWAYS) {
735 dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH;
736 }
737 else {
738 dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH;
739 }
740 }
741
742 {
743 uint8_t pixel = bg_enabled? fifo_item->pixel : 0;
744 if (pixel && bg_priority) {
745 draw_oam = false;
746 }
747 if (!gb->cgb_mode) {
748 pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
749 }
750 if (gb->sgb) {
751 if (gb->current_lcd_line < LINES) {
752 gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
753 }
754 }
755 else if (gb->model & GB_MODEL_NO_SFC_BIT) {
756 if (gb->icd_pixel_callback) {
757 icd_pixel = pixel;
758 }
759 }
760 else if (gb->cgb_palettes_ppu_blocked) {
761 *dest = gb->rgb_encode_callback(gb, 0, 0, 0);
762 }
763 else {
764 *dest = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
765 }
766 }
767
768 if (draw_oam) {
769 uint8_t pixel = oam_fifo_item->pixel;
770 if (!gb->cgb_mode) {
771 /* Todo: Verify access timings */
772 pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
773 }
774 if (gb->sgb) {
775 if (gb->current_lcd_line < LINES) {
776 gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel;
777 }
778 }
779 else if (gb->model & GB_MODEL_NO_SFC_BIT) {
780 if (gb->icd_pixel_callback) {
781 icd_pixel = pixel;
782 }
783 }
784 else if (gb->cgb_palettes_ppu_blocked) {
785 *dest = gb->rgb_encode_callback(gb, 0, 0, 0);
786 }
787 else {
788 *dest = gb->object_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
789 }
790 }
791
792 if (gb->model & GB_MODEL_NO_SFC_BIT) {
793 if (gb->icd_pixel_callback) {
794 gb->icd_pixel_callback(gb, icd_pixel);
795 }
796 }
797
798 gb->position_in_line++;
799 gb->lcd_x++;
800 }
801
802 static inline void dma_sync(GB_gameboy_t *gb, unsigned *cycles)
803 {
804 if (unlikely(GB_is_dma_active(gb))) {
805 unsigned offset = *cycles - gb->display_cycles; // Time passed in 8MHz ticks
806 if (offset) {
807 *cycles = gb->display_cycles;
808 if (!gb->cgb_double_speed) {
809 offset >>= 1; // Convert to T-cycles
810 }
811 unsigned old = gb->dma_cycles;
812 gb->dma_cycles = offset;
813 GB_dma_run(gb);
814 gb->dma_cycles = old - offset;
815 }
816 }
817 }
818
819 static inline uint8_t fetcher_y(GB_gameboy_t *gb)
820 {
821 return gb->wx_triggered? gb->window_y : gb->current_line + gb->io_registers[GB_IO_SCY];
822 }
823
824 static inline uint8_t vram_read(GB_gameboy_t *gb, uint16_t addr)
825 {
826 if (unlikely(gb->vram_ppu_blocked)) {
827 return 0xFF;
828 }
829 if (unlikely(gb->hdma_in_progress)) {
830 gb->addr_for_hdma_conflict = addr;
831 return 0;
832 }
833 // TODO: what if both?
834 else if (unlikely(gb->dma_current_dest <= 0xA0 && gb->dma_current_dest > 0 && (gb->dma_current_src & 0xE000) == 0x8000)) { // TODO: what happens in the last and first M cycles?
835 // DMAing from VRAM!
836 /* TODO: AGS has its own, very different pattern, but AGS is not currently a supported model */
837 /* TODO: Research this when researching odd modes */
838 /* TODO: probably not 100% on the first few reads during halt/stop modes*/
839 unsigned offset = 1 - (gb->halted || gb->stopped);
840 if (GB_is_cgb(gb)) {
841 if (gb->dma_ppu_vram_conflict) {
842 addr = (gb->dma_ppu_vram_conflict_addr & 0x1FFF) | (addr & 0x2000);
843 }
844 else if (gb->dma_cycles_modulo && !gb->halted && !gb->stopped) {
845 addr &= 0x2000;
846 addr |= ((gb->dma_current_src - offset) & 0x1FFF);
847 }
848 else {
849 addr &= 0x2000 | ((gb->dma_current_src - offset) & 0x1FFF);
850 gb->dma_ppu_vram_conflict_addr = addr;
851 gb->dma_ppu_vram_conflict = !gb->halted && !gb->stopped;
852 }
853 }
854 else {
855 addr |= ((gb->dma_current_src - offset) & 0x1FFF);
856 }
857 gb->oam[gb->dma_current_dest - offset] = gb->vram[(addr & 0x1FFF) | (gb->cgb_vram_bank? 0x2000 : 0)];
858 }
859 return gb->vram[addr];
860 }
861
862 typedef enum {
863 /* VRAM reads take 2 T-cycles. In read address is determined in the first
864 cycle, and the read actually completes in the second cycle.*/
865 GB_FETCHER_GET_TILE_T1,
866 GB_FETCHER_GET_TILE_T2,
867 GB_FETCHER_GET_TILE_DATA_LOWER_T1,
868 GB_FETCHER_GET_TILE_DATA_LOWER_T2,
869 GB_FETCHER_GET_TILE_DATA_HIGH_T1,
870 GB_FETCHER_GET_TILE_DATA_HIGH_T2,
871 GB_FETCHER_PUSH,
872 } fetcher_step_t;
873
874 static uint8_t data_for_tile_sel_glitch(GB_gameboy_t *gb, bool *should_use, bool *cgb_d_glitch)
875 {
876 /*
877 Based on Matt Currie's research here:
878 https://github.com/mattcurrie/mealybug-tearoom-tests/blob/master/the-comprehensive-game-boy-ppu-documentation.md#tile_sel-bit-4
879 */
880 *should_use = true;
881 *cgb_d_glitch = false;
882
883 if (gb->last_tileset) {
884 if (gb->model != GB_MODEL_CGB_D) {
885 *should_use = !(gb->current_tile & 0x80);
886 return gb->current_tile;
887 }
888 *cgb_d_glitch = true;
889 *should_use = false;
890 gb->last_tile_data_address &= ~0x1000;
891 if (gb->fetcher_state == GB_FETCHER_GET_TILE_DATA_LOWER_T2) {
892 *cgb_d_glitch = true;
893 return 0;
894 }
895 return 0;
896 }
897 return gb->data_for_sel_glitch;
898 }
899
900 internal void GB_update_wx_glitch(GB_gameboy_t *gb)
901 {
902 if (!GB_is_cgb(gb)) return;
903 if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) || !gb->wy_triggered) {
904 gb->cgb_wx_glitch = false;
905 return;
906 }
907 if (unlikely(gb->io_registers[GB_IO_WX] == 0)) {
908 // (gb->position_in_line + 16 <= 8) is (gb->position_in_line <= -8) in unsigned
909 gb->cgb_wx_glitch = ((uint8_t)(gb->position_in_line + 16) <= 8 ||
910 (gb->position_in_line == (uint8_t)-7 && gb->line_has_fractional_scrolling));
911 return;
912 }
913 gb->cgb_wx_glitch = (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == gb->io_registers[GB_IO_WX];
914 }
915
916 static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles)
917 {
918 switch ((fetcher_step_t)gb->fetcher_state) {
919 case GB_FETCHER_GET_TILE_T1: {
920 GB_update_wx_glitch(gb);
921 uint16_t map = 0x1800;
922
923 if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) {
924 gb->wx_triggered = false;
925 }
926
927 if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_MAP && !gb->wx_triggered) {
928 map = 0x1C00;
929 }
930 else if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_MAP && gb->wx_triggered) {
931 map = 0x1C00;
932 }
933
934 uint8_t y = fetcher_y(gb);
935 uint8_t x = 0;
936 if (gb->wx_triggered) {
937 x = gb->window_tile_x;
938 }
939 else if ((uint8_t)(gb->position_in_line + 16) < 8) {
940 x = gb->io_registers[GB_IO_SCX] >> 3;
941 }
942 else {
943 x = ((gb->io_registers[GB_IO_SCX] + gb->position_in_line + 8 - (GB_is_cgb(gb) && !gb->during_object_fetch)) / 8) & 0x1F;
944 }
945 if (gb->model > GB_MODEL_CGB_C) {
946 /* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */
947 gb->fetcher_y = y;
948 }
949 gb->last_tile_index_address = map + x + y / 8 * 32;
950 }
951 gb->fetcher_state++;
952 break;
953 case GB_FETCHER_GET_TILE_T2: {
954 if (gb->cgb_wx_glitch) {
955 gb->fetcher_state++;
956 break;
957 }
958 dma_sync(gb, cycles);
959 gb->current_tile = vram_read(gb, gb->last_tile_index_address);
960 if (GB_is_cgb(gb)) {
961 /* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
962 This probably means the CGB has a 16-bit data bus for the VRAM. */
963 gb->current_tile_attributes = vram_read(gb, gb->last_tile_index_address + 0x2000);
964 }
965 }
966 gb->fetcher_state++;
967 break;
968
969 case GB_FETCHER_GET_TILE_DATA_LOWER_T1: {
970 GB_update_wx_glitch(gb);
971
972 uint8_t y_flip = 0;
973 uint16_t tile_address = 0;
974 uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
975
976 gb->last_tileset = gb->io_registers[GB_IO_LCDC] & GB_LCDC_TILE_SEL;
977 if (gb->last_tileset) {
978 tile_address = gb->current_tile * 0x10;
979 }
980 else {
981 tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
982 }
983 if (gb->current_tile_attributes & 8) {
984 tile_address += 0x2000;
985 }
986 if (gb->current_tile_attributes & 0x40) {
987 y_flip = 0x7;
988 }
989 gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2;
990 }
991 gb->fetcher_state++;
992 break;
993
994 case GB_FETCHER_GET_TILE_DATA_LOWER_T2: {
995 if (gb->cgb_wx_glitch) {
996 gb->current_tile_data[0] = gb->current_tile_data[1];
997 gb->fetcher_state++;
998 break;
999 }
1000 dma_sync(gb, cycles);
1001 bool use_glitched = false;
1002 bool cgb_d_glitch = false;
1003 if (gb->tile_sel_glitch) {
1004 gb->current_tile_data[0] = data_for_tile_sel_glitch(gb, &use_glitched, &cgb_d_glitch);
1005 }
1006 if (!use_glitched) {
1007 gb->current_tile_data[0] = vram_read(gb, gb->last_tile_data_address);
1008 }
1009 if (gb->last_tileset && gb->tile_sel_glitch) {
1010 gb->data_for_sel_glitch = vram_read(gb, gb->last_tile_data_address);
1011 }
1012 else if (cgb_d_glitch) {
1013 gb->data_for_sel_glitch = vram_read(gb, gb->last_tile_data_address & ~0x1000);
1014 }
1015 }
1016 gb->fetcher_state++;
1017 break;
1018
1019 case GB_FETCHER_GET_TILE_DATA_HIGH_T1: {
1020 GB_update_wx_glitch(gb);
1021
1022 uint16_t tile_address = 0;
1023 uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);
1024
1025 gb->last_tileset = gb->io_registers[GB_IO_LCDC] & GB_LCDC_TILE_SEL;
1026 if (gb->last_tileset) {
1027 tile_address = gb->current_tile * 0x10;
1028 }
1029 else {
1030 tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000;
1031 }
1032 if (gb->current_tile_attributes & 8) {
1033 tile_address += 0x2000;
1034 }
1035 uint8_t y_flip = 0;
1036 if (gb->current_tile_attributes & 0x40) {
1037 y_flip = 0x7;
1038 }
1039 gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1;
1040 }
1041 gb->fetcher_state++;
1042 break;
1043
1044 case GB_FETCHER_GET_TILE_DATA_HIGH_T2: {
1045 if (gb->cgb_wx_glitch) {
1046 gb->current_tile_data[1] = gb->current_tile_data[0];
1047 gb->fetcher_state++;
1048 if (gb->wx_triggered) {
1049 gb->window_tile_x++;
1050 gb->window_tile_x &= 0x1F;
1051 }
1052 break;
1053 }
1054 dma_sync(gb, cycles);
1055 bool use_glitched = false;
1056 bool cgb_d_glitch = false;
1057 if (gb->tile_sel_glitch) {
1058 gb->current_tile_data[1] = data_for_tile_sel_glitch(gb, &use_glitched, &cgb_d_glitch);
1059 if (cgb_d_glitch) {
1060 gb->last_tile_data_address--;
1061 }
1062 }
1063 if (!use_glitched) {
1064 gb->data_for_sel_glitch = gb->current_tile_data[1] =
1065 vram_read(gb, gb->last_tile_data_address);
1066 }
1067 if (gb->last_tileset && gb->tile_sel_glitch) {
1068 gb->data_for_sel_glitch = vram_read(gb, gb->last_tile_data_address);
1069
1070 }
1071 else if (cgb_d_glitch) {
1072 gb->data_for_sel_glitch = vram_read(gb, (gb->tile_sel_glitch & ~0x1000) + 1);
1073 }
1074 }
1075 if (gb->wx_triggered) {
1076 gb->window_tile_x++;
1077 gb->window_tile_x &= 0x1F;
1078 }
1079
1080 // fallthrough
1081 default:
1082 case GB_FETCHER_PUSH: {
1083 gb->fetcher_state = GB_FETCHER_PUSH;
1084 if (fifo_size(&gb->bg_fifo) > 0) break;
1085
1086 if (unlikely(gb->wy_triggered && !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) && !GB_is_cgb(gb) && !gb->disable_window_pixel_insertion_glitch)) {
1087 /* See https://github.com/LIJI32/SameBoy/issues/278 for documentation */
1088 uint8_t logical_position = gb->position_in_line + 7;
1089 if (logical_position > 167) {
1090 logical_position = 0;
1091 }
1092 if (gb->io_registers[GB_IO_WX] == logical_position) {
1093 gb->bg_fifo.read_end--;
1094 gb->bg_fifo.read_end &= GB_FIFO_LENGTH - 1;
1095 gb->bg_fifo.fifo[gb->bg_fifo.read_end] = (GB_fifo_item_t){0,};
1096 gb->bg_fifo.size = 1;
1097 break;
1098 }
1099 }
1100
1101 fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
1102 gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
1103 gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
1104 }
1105 break;
1106 }
1107 }
1108
1109 static uint16_t get_object_line_address(GB_gameboy_t *gb, uint8_t y, uint8_t tile, uint8_t flags)
1110 {
1111 bool height_16 = (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_SIZE) != 0; /* Todo: Which T-cycle actually reads this? */
1112 uint8_t tile_y = (gb->current_line - y) & (height_16? 0xF : 7);
1113
1114 if (flags & 0x40) { /* Flip Y */
1115 tile_y ^= height_16? 0xF : 7;
1116 }
1117
1118 /* Todo: I'm not 100% sure an access to OAM can't trigger the OAM bug while we're accessing this */
1119 uint16_t line_address = (height_16? tile & 0xFE : tile) * 0x10 + tile_y * 2;
1120
1121 if (gb->cgb_mode && (flags & 0x8)) { /* Use VRAM bank 2 */
1122 line_address += 0x2000;
1123 }
1124 return line_address;
1125 }
1126
1127 static inline uint8_t flip(uint8_t x)
1128 {
1129 x = (x & 0xF0) >> 4 | (x & 0x0F) << 4;
1130 x = (x & 0xCC) >> 2 | (x & 0x33) << 2;
1131 x = (x & 0xAA) >> 1 | (x & 0x55) << 1;
1132 return x;
1133 }
1134
1135 static inline void get_tile_data(const GB_gameboy_t *gb, uint8_t tile_x, uint8_t y, uint16_t map, uint8_t *attributes, uint8_t *data0, uint8_t *data1)
1136 {
1137 uint8_t current_tile = gb->vram[map + (tile_x & 0x1F) + y / 8 * 32];
1138 *attributes = GB_is_cgb(gb)? gb->vram[0x2000 + map + (tile_x & 0x1F) + y / 8 * 32] : 0;
1139
1140 uint16_t tile_address = 0;
1141
1142 if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_TILE_SEL) {
1143 tile_address = current_tile * 0x10;
1144 }
1145 else {
1146 tile_address = (int8_t)current_tile * 0x10 + 0x1000;
1147 }
1148 if (*attributes & 8) {
1149 tile_address += 0x2000;
1150 }
1151 uint8_t y_flip = 0;
1152 if (*attributes & 0x40) {
1153 y_flip = 0x7;
1154 }
1155
1156 *data0 = gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
1157 *data1 = gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1];
1158
1159 if (*attributes & 0x20) {
1160 *data0 = flip(*data0);
1161 *data1 = flip(*data1);
1162 }
1163
1164 }
1165
1166 static void render_line(GB_gameboy_t *gb)
1167 {
1168 if (gb->disable_rendering) return;
1169 if (!gb->screen) return;
1170 if (gb->current_line > 144) return; // Corrupt save state
1171
1172 struct {
1173 unsigned pixel:2; // Color, 0-3
1174 unsigned priority:6; // Object priority – 0 in DMG, OAM index in CGB
1175 unsigned palette:3; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
1176 bool bg_priority:1; // BG priority bit
1177 } _object_buffer[160 + 16]; // allocate extra to avoid per pixel checks
1178 static const uint8_t empty_object_buffer[sizeof(_object_buffer)];
1179 const typeof(_object_buffer[0]) *object_buffer;
1180
1181 if (gb->n_visible_objs && !gb->objects_disabled && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN)) {
1182 object_buffer = &_object_buffer[0];
1183 object_t *objects = (object_t *) &gb->oam;
1184 memset(_object_buffer, 0, sizeof(_object_buffer));
1185
1186 while (gb->n_visible_objs) {
1187 unsigned object_index = gb->visible_objs[gb->n_visible_objs - 1];
1188 unsigned priority = (gb->object_priority == GB_OBJECT_PRIORITY_X)? 0 : object_index;
1189 const object_t *object = &objects[object_index];
1190 gb->n_visible_objs--;
1191
1192 uint16_t line_address = get_object_line_address(gb, object->y, object->tile, object->flags);
1193 uint8_t data0 = gb->vram[line_address];
1194 uint8_t data1 = gb->vram[line_address + 1];
1195 if (gb->n_visible_objs == 0) {
1196 gb->data_for_sel_glitch = data1;
1197 }
1198 if (object->flags & 0x20) {
1199 data0 = flip(data0);
1200 data1 = flip(data1);
1201 }
1202
1203 typeof(_object_buffer[0]) *p = _object_buffer + object->x;
1204 if (object->x >= 168) {
1205 continue;
1206 }
1207 unrolled for (unsigned x = 0; x < 8; x++) {
1208 unsigned pixel = (data0 >> 7) | ((data1 >> 7) << 1);
1209 data0 <<= 1;
1210 data1 <<= 1;
1211 if (pixel && (!p->pixel || priority < p->priority)) {
1212 p->pixel = pixel;
1213 p->priority = priority;
1214
1215 if (gb->cgb_mode) {
1216 p->palette = object->flags & 0x7;
1217 }
1218 else {
1219 p->palette = (object->flags & 0x10) >> 4;
1220 }
1221 p->bg_priority = object->flags & 0x80;
1222 }
1223 p++;
1224 }
1225 }
1226 }
1227 else {
1228 object_buffer = (const void *)empty_object_buffer;
1229 }
1230
1231
1232 uint32_t *restrict p = gb->screen;
1233 typeof(object_buffer[0]) *object_buffer_pointer = object_buffer + 8;
1234 if (gb->border_mode == GB_BORDER_ALWAYS) {
1235 p += (BORDERED_WIDTH - (WIDTH)) / 2 + BORDERED_WIDTH * (BORDERED_HEIGHT - LINES) / 2;
1236 p += BORDERED_WIDTH * gb->current_line;
1237 }
1238 else {
1239 p += WIDTH * gb->current_line;
1240 }
1241
1242 if (unlikely(gb->background_disabled) || (!gb->cgb_mode && !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN))) {
1243 uint32_t bg = gb->background_palettes_rgb[gb->cgb_mode? 0 : (gb->io_registers[GB_IO_BGP] & 3)];
1244 for (unsigned i = 160; i--;) {
1245 if (unlikely(object_buffer_pointer->pixel)) {
1246 uint8_t pixel = object_buffer_pointer->pixel;
1247 if (!gb->cgb_mode) {
1248 pixel = ((gb->io_registers[GB_IO_OBP0 + object_buffer_pointer->palette] >> (pixel << 1)) & 3);
1249 }
1250 *(p++) = gb->object_palettes_rgb[pixel + (object_buffer_pointer->palette & 7) * 4];
1251 }
1252 else {
1253 *(p++) = bg;
1254 }
1255 object_buffer_pointer++;
1256 }
1257 return;
1258 }
1259
1260 unsigned pixels = 0;
1261 uint8_t tile_x = gb->io_registers[GB_IO_SCX] / 8;
1262 unsigned fractional_scroll = gb->io_registers[GB_IO_SCX] & 7;
1263 uint16_t map = 0x1800;
1264 if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_MAP) {
1265 map = 0x1C00;
1266 }
1267 uint8_t y = gb->current_line + gb->io_registers[GB_IO_SCY];
1268 uint8_t attributes;
1269 uint8_t data0, data1;
1270 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1271
1272 #define DO_PIXEL() \
1273 uint8_t pixel = (data0 >> 7) | ((data1 >> 7) << 1);\
1274 data0 <<= 1;\
1275 data1 <<= 1;\
1276 \
1277 if (unlikely(object_buffer_pointer->pixel) && (pixel == 0 || !(object_buffer_pointer->bg_priority || (attributes & 0x80)) || !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN))) {\
1278 pixel = object_buffer_pointer->pixel;\
1279 if (!gb->cgb_mode) {\
1280 pixel = ((gb->io_registers[GB_IO_OBP0 + object_buffer_pointer->palette] >> (pixel << 1)) & 3);\
1281 }\
1282 *(p++) = gb->object_palettes_rgb[pixel + (object_buffer_pointer->palette & 7) * 4];\
1283 }\
1284 else {\
1285 if (!gb->cgb_mode) {\
1286 pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);\
1287 }\
1288 *(p++) = gb->background_palettes_rgb[pixel + (attributes & 7) * 4];\
1289 }\
1290 pixels++;\
1291 object_buffer_pointer++\
1292
1293 // First 1-8 pixels
1294 data0 <<= fractional_scroll;
1295 data1 <<= fractional_scroll;
1296 bool check_window = gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE);
1297 nounroll for (unsigned i = fractional_scroll; i < 8; i++) {
1298 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1299 activate_window:
1300 check_window = false;
1301 map = gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_MAP? 0x1C00 : 0x1800;
1302 tile_x = -1;
1303 y = ++gb->window_y;
1304 break;
1305 }
1306 DO_PIXEL();
1307 }
1308 tile_x++;
1309
1310 while (pixels < 160 - 8) {
1311 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1312 nounroll for (unsigned i = 0; i < 8; i++) {
1313 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1314 goto activate_window;
1315 }
1316 DO_PIXEL();
1317 }
1318 tile_x++;
1319 }
1320
1321 gb->fetcher_state = (160 - pixels) & 7;
1322 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1323 while (pixels < 160) {
1324 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1325 goto activate_window;
1326 }
1327 DO_PIXEL();
1328 }
1329 tile_x++;
1330
1331 get_tile_data(gb, tile_x, y, map, &attributes, gb->current_tile_data, gb->current_tile_data + 1);
1332 #undef DO_PIXEL
1333 }
1334
1335 static void render_line_sgb(GB_gameboy_t *gb)
1336 {
1337 if (gb->current_line > 144) return; // Corrupt save state
1338
1339 struct {
1340 unsigned pixel:2; // Color, 0-3
1341 unsigned palette:1; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
1342 bool bg_priority:1; // BG priority bit
1343 } _object_buffer[160 + 16]; // allocate extra to avoid per pixel checks
1344 static const uint8_t empty_object_buffer[sizeof(_object_buffer)];
1345 const typeof(_object_buffer[0]) *object_buffer;
1346
1347 if (gb->n_visible_objs && !gb->objects_disabled && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN)) {
1348 object_buffer = &_object_buffer[0];
1349 object_t *objects = (object_t *) &gb->oam;
1350 memset(_object_buffer, 0, sizeof(_object_buffer));
1351
1352 while (gb->n_visible_objs) {
1353 const object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
1354 gb->n_visible_objs--;
1355
1356 uint16_t line_address = get_object_line_address(gb, object->y, object->tile, object->flags);
1357 uint8_t data0 = gb->vram[line_address];
1358 uint8_t data1 = gb->vram[line_address + 1];
1359 if (object->flags & 0x20) {
1360 data0 = flip(data0);
1361 data1 = flip(data1);
1362 }
1363
1364 typeof(_object_buffer[0]) *p = _object_buffer + object->x;
1365 if (object->x >= 168) {
1366 continue;
1367 }
1368 unrolled for (unsigned x = 0; x < 8; x++) {
1369 unsigned pixel = (data0 >> 7) | ((data1 >> 7) << 1);
1370 data0 <<= 1;
1371 data1 <<= 1;
1372 if (!p->pixel) {
1373 p->pixel = pixel;
1374 p->palette = (object->flags & 0x10) >> 4;
1375 p->bg_priority = object->flags & 0x80;
1376 }
1377 p++;
1378 }
1379 }
1380 }
1381 else {
1382 object_buffer = (const void *)empty_object_buffer;
1383 }
1384
1385
1386 uint8_t *restrict p = gb->sgb->screen_buffer;
1387 typeof(object_buffer[0]) *object_buffer_pointer = object_buffer + 8;
1388 p += WIDTH * gb->current_line;
1389
1390 if (unlikely(gb->background_disabled) || (!gb->cgb_mode && !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN))) {
1391 for (unsigned i = 160; i--;) {
1392 if (unlikely(object_buffer_pointer->pixel)) {
1393 uint8_t pixel = object_buffer_pointer->pixel;
1394 pixel = ((gb->io_registers[GB_IO_OBP0 + object_buffer_pointer->palette] >> (pixel << 1)) & 3);
1395 *(p++) = pixel;
1396 }
1397 else {
1398 *(p++) = gb->io_registers[GB_IO_BGP] & 3;
1399 }
1400 object_buffer_pointer++;
1401 }
1402 return;
1403 }
1404
1405 unsigned pixels = 0;
1406 uint8_t tile_x = gb->io_registers[GB_IO_SCX] / 8;
1407 unsigned fractional_scroll = gb->io_registers[GB_IO_SCX] & 7;
1408 uint16_t map = 0x1800;
1409 if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_MAP) {
1410 map = 0x1C00;
1411 }
1412 uint8_t y = gb->current_line + gb->io_registers[GB_IO_SCY];
1413 uint8_t attributes;
1414 uint8_t data0, data1;
1415 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1416
1417 #define DO_PIXEL() \
1418 uint8_t pixel = (data0 >> 7) | ((data1 >> 7) << 1);\
1419 data0 <<= 1;\
1420 data1 <<= 1;\
1421 \
1422 if (unlikely(object_buffer_pointer->pixel) && (pixel == 0 || !object_buffer_pointer->bg_priority || !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN))) {\
1423 pixel = object_buffer_pointer->pixel;\
1424 pixel = ((gb->io_registers[GB_IO_OBP0 + object_buffer_pointer->palette] >> (pixel << 1)) & 3);\
1425 *(p++) = pixel;\
1426 }\
1427 else {\
1428 pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);\
1429 *(p++) = pixel;\
1430 }\
1431 pixels++;\
1432 object_buffer_pointer++\
1433
1434 // First 1-8 pixels
1435 data0 <<= fractional_scroll;
1436 data1 <<= fractional_scroll;
1437 bool check_window = gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE);
1438 nounroll for (unsigned i = fractional_scroll; i < 8; i++) {
1439 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1440 activate_window:
1441 check_window = false;
1442 map = gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_MAP? 0x1C00 : 0x1800;
1443 tile_x = -1;
1444 y = ++gb->window_y;
1445 break;
1446 }
1447 DO_PIXEL();
1448 }
1449 tile_x++;
1450
1451 while (pixels < 160 - 8) {
1452 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1453 nounroll for (unsigned i = 0; i < 8; i++) {
1454 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1455 goto activate_window;
1456 }
1457 DO_PIXEL();
1458 }
1459 tile_x++;
1460 }
1461
1462 get_tile_data(gb, tile_x, y, map, &attributes, &data0, &data1);
1463 while (pixels < 160) {
1464 if (check_window && gb->io_registers[GB_IO_WX] == pixels + 7) {
1465 goto activate_window;
1466 }
1467 DO_PIXEL();
1468 }
1469 }
1470
1471 static inline uint16_t mode3_batching_length(GB_gameboy_t *gb)
1472 {
1473 if (gb->position_in_line != (uint8_t)-16) return 0;
1474 if (gb->model & GB_MODEL_NO_SFC_BIT) return 0;
1475 if (gb->hdma_on) return 0;
1476 if (gb->stopped) return 0;
1477 if (GB_is_dma_active(gb)) return 0;
1478 if (gb->wx_triggered) return 0;
1479 if (gb->wy_triggered) {
1480 if (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) {
1481 if ((gb->io_registers[GB_IO_WX] < 7 || gb->io_registers[GB_IO_WX] == 166 || gb->io_registers[GB_IO_WX] == 167)) {
1482 return 0;
1483 }
1484 }
1485 else {
1486 if (gb->io_registers[GB_IO_WX] < 167 && !GB_is_cgb(gb)) {
1487 return 0;
1488 }
1489 }
1490 }
1491
1492 // No objects or window, timing is trivial
1493 if (gb->n_visible_objs == 0 && !(gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE))) return 167 + (gb->io_registers[GB_IO_SCX] & 7);
1494
1495 if (gb->hdma_on_hblank) return 0;
1496
1497 // 300 is a bit more than the maximum Mode 3 length
1498
1499 // No HBlank interrupt
1500 if (!(gb->io_registers[GB_IO_STAT] & 0x8)) return 300;
1501 // No STAT interrupt requested
1502 if (!(gb->interrupt_enable & 2)) return 300;
1503
1504
1505 return 0;
1506 }
1507
1508 static inline uint8_t x_for_object_match(GB_gameboy_t *gb)
1509 {
1510 uint8_t ret = gb->position_in_line + 8;
1511 if (ret > (uint8_t)-16) return 0;
1512 return ret;
1513 }
1514
1515 static void update_frame_parity(GB_gameboy_t *gb)
1516 {
1517 if (gb->model <= GB_MODEL_CGB_E) {
1518 gb->is_odd_frame ^= true;
1519 }
1520 else {
1521 // Faster than division, it's normally only once
1522 while (gb->frame_parity_ticks > LCDC_PERIOD * 2) {
1523 gb->frame_parity_ticks -= LCDC_PERIOD * 2;
1524 gb->is_odd_frame ^= true;
1525 }
1526 }
1527 }
1528
1529 /*
1530 TODO: It seems that the STAT register's mode bits are always "late" by 4 T-cycles.
1531 The PPU logic can be greatly simplified if that delay is simply emulated.
1532 */
1533 void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
1534 {
1535 if (gb->wy_triggered) {
1536 gb->wy_check_scheduled = false;
1537 }
1538 else if (gb->wy_check_scheduled) {
1539 force = true;
1540 unsigned cycles_to_check;
1541 // TODO: When speed-switching while the LCD is on, the modulo might be affected. Odd-modes are going to be fun.
1542 if (gb->cgb_double_speed) {
1543 cycles_to_check = (8 - ((gb->wy_check_modulo + 6) & 7));
1544 }
1545 else if (GB_is_cgb(gb)) {
1546 cycles_to_check = (8 - ((gb->wy_check_modulo + 0) & 7));
1547 }
1548 else {
1549 cycles_to_check = (8 - ((gb->wy_check_modulo + 2) & 7));
1550 }
1551
1552 if (cycles >= cycles_to_check) {
1553 gb->wy_check_scheduled = false;
1554 GB_display_run(gb, cycles_to_check, true);
1555 wy_check(gb);
1556 if (gb->display_state == 21 && GB_is_cgb(gb) && !gb->cgb_double_speed) {
1557 gb->wy_just_checked = true;
1558 }
1559 cycles -= cycles_to_check;
1560 }
1561 }
1562
1563 if (unlikely((gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE) && (signed)(gb->cycles_for_line * 2 + cycles + gb->display_cycles) > LINE_LENGTH * 2)) {
1564 unsigned first_batch = (LINE_LENGTH * 2 - gb->cycles_for_line * 2 + gb->display_cycles);
1565 GB_display_run(gb, first_batch, force);
1566 cycles -= first_batch;
1567 if (gb->display_state == 22) {
1568 gb->io_registers[GB_IO_STAT] &= ~3;
1569 gb->mode_for_interrupt = 0;
1570 GB_STAT_update(gb);
1571 }
1572 gb->display_state = 9;
1573 gb->display_cycles = 0;
1574 }
1575 if (unlikely(gb->delayed_glitch_hblank_interrupt && cycles && gb->current_line < LINES)) {
1576 gb->delayed_glitch_hblank_interrupt = false;
1577 gb->mode_for_interrupt = 0;
1578 GB_STAT_update(gb);
1579 gb->mode_for_interrupt = 3;
1580 }
1581 gb->cycles_since_vblank_callback += cycles / 2;
1582
1583 gb->frame_parity_ticks += cycles;
1584 gb->wy_check_modulo += cycles;
1585
1586 if (cycles < gb->frame_repeat_countdown) {
1587 gb->frame_repeat_countdown -= cycles;
1588 }
1589 else {
1590 gb->frame_repeat_countdown = 0;
1591 }
1592
1593 /* The PPU does not advance while in STOP mode on the DMG */
1594 if (gb->stopped && !GB_is_cgb(gb)) {
1595 if (gb->cycles_since_vblank_callback >= LCDC_PERIOD) {
1596 GB_display_vblank(gb, GB_VBLANK_TYPE_ARTIFICIAL);
1597 }
1598 return;
1599 }
1600
1601 GB_BATCHABLE_STATE_MACHINE(gb, display, cycles, 2, !force) {
1602 GB_STATE(gb, display, 1);
1603 GB_STATE(gb, display, 2);
1604 GB_STATE(gb, display, 3);
1605 GB_STATE(gb, display, 4);
1606 GB_STATE(gb, display, 5);
1607 GB_STATE(gb, display, 6);
1608 GB_STATE(gb, display, 7);
1609 GB_STATE(gb, display, 8);
1610 GB_STATE(gb, display, 9);
1611 GB_STATE(gb, display, 10);
1612 GB_STATE(gb, display, 11);
1613 GB_STATE(gb, display, 12);
1614 GB_STATE(gb, display, 13);
1615 GB_STATE(gb, display, 14);
1616 GB_STATE(gb, display, 15);
1617 GB_STATE(gb, display, 16);
1618 GB_STATE(gb, display, 17);
1619
1620 GB_STATE(gb, display, 19);
1621 GB_STATE(gb, display, 20);
1622 GB_STATE(gb, display, 21);
1623 GB_STATE(gb, display, 22);
1624 GB_STATE(gb, display, 23);
1625 GB_STATE(gb, display, 24);
1626 GB_STATE(gb, display, 26);
1627 GB_STATE(gb, display, 27);
1628 GB_STATE(gb, display, 28);
1629 GB_STATE(gb, display, 29);
1630
1631 GB_STATE(gb, display, 31);
1632 GB_STATE(gb, display, 32);
1633 GB_STATE(gb, display, 33);
1634 GB_STATE(gb, display, 34);
1635 GB_STATE(gb, display, 35);
1636 GB_STATE(gb, display, 36);
1637 GB_STATE(gb, display, 37);
1638 GB_STATE(gb, display, 38);
1639 GB_STATE(gb, display, 39);
1640 GB_STATE(gb, display, 40);
1641 GB_STATE(gb, display, 41);
1642 GB_STATE(gb, display, 42);
1643 GB_STATE(gb, display, 43);
1644 }
1645
1646 gb->wy_check_modulo = cycles;
1647 gb->wy_just_checked = false;
1648
1649 if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)) {
1650 while (true) {
1651 if (gb->cycles_since_vblank_callback < LCDC_PERIOD) {
1652 GB_SLEEP(gb, display, 1, LCDC_PERIOD - gb->cycles_since_vblank_callback);
1653 }
1654 update_frame_parity(gb); // TODO: test actual timing
1655 GB_display_vblank(gb, GB_VBLANK_TYPE_LCD_OFF);
1656 }
1657 return;
1658 }
1659
1660 if (!GB_is_cgb(gb)) {
1661 GB_SLEEP(gb, display, 23, 1);
1662 }
1663
1664 /* Handle mode 2 on the very first line 0 */
1665 gb->current_line = 0;
1666 gb->window_y = -1;
1667 gb->wy_triggered = false;
1668 gb->position_in_line = -16;
1669 gb->line_has_fractional_scrolling = false;
1670
1671 gb->ly_for_comparison = 0;
1672 gb->io_registers[GB_IO_STAT] &= ~3;
1673 gb->mode_for_interrupt = -1;
1674 gb->oam_read_blocked = false;
1675 gb->vram_read_blocked = false;
1676 gb->oam_write_blocked = false;
1677 gb->vram_write_blocked = false;
1678 gb->cgb_palettes_blocked = false;
1679 gb->cycles_for_line = MODE2_LENGTH - 4;
1680 GB_STAT_update(gb);
1681 GB_SLEEP(gb, display, 2, MODE2_LENGTH - 4);
1682
1683 gb->oam_write_blocked = true;
1684 gb->cycles_for_line += 2;
1685 GB_STAT_update(gb);
1686 GB_SLEEP(gb, display, 34, 2);
1687
1688 gb->n_visible_objs = 0;
1689 gb->orig_n_visible_objs = 0;
1690 gb->cycles_for_line += 8; // Mode 0 is shorter on the first line 0, so we augment cycles_for_line by 8 extra cycles.
1691
1692 gb->io_registers[GB_IO_STAT] &= ~3;
1693 gb->io_registers[GB_IO_STAT] |= 3;
1694 gb->mode_for_interrupt = 3;
1695
1696 gb->oam_write_blocked = true;
1697 gb->oam_read_blocked = true;
1698 gb->vram_read_blocked = gb->cgb_double_speed;
1699 gb->vram_write_blocked = gb->cgb_double_speed;
1700 if (!GB_is_cgb(gb)) {
1701 gb->vram_read_blocked = true;
1702 gb->vram_write_blocked = true;
1703 }
1704 gb->cycles_for_line += 2;
1705 GB_SLEEP(gb, display, 37, 2);
1706
1707 gb->cgb_palettes_blocked = true;
1708 gb->cycles_for_line += 3;
1709 GB_SLEEP(gb, display, 38, 3);
1710
1711 gb->vram_read_blocked = true;
1712 gb->vram_write_blocked = true;
1713 gb->wx_triggered = false;
1714 goto mode_3_start;
1715
1716 // Mode 3 abort, state 9
1717 display9: {
1718 // TODO: Timing of things in this scenario is almost completely untested
1719 if (gb->current_line < LINES && !GB_is_sgb(gb) && !gb->disable_rendering) {
1720 GB_log(gb, "The ROM is preventing line %d from fully rendering, this could damage a real device's LCD display.\n", gb->current_line);
1721 uint32_t *dest = NULL;
1722 if (gb->border_mode != GB_BORDER_ALWAYS) {
1723 dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH;
1724 }
1725 else {
1726 dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH;
1727 }
1728 uint32_t color = GB_is_cgb(gb)? GB_convert_rgb15(gb, 0x7FFF, false) : gb->background_palettes_rgb[4];
1729 while (gb->lcd_x < 160) {
1730 *(dest++) = color;
1731 gb->lcd_x++;
1732 }
1733 }
1734 gb->n_visible_objs = gb->orig_n_visible_objs;
1735 gb->current_line++;
1736 wy_check(gb);
1737 gb->cycles_for_line = 0;
1738 if (gb->current_line != LINES) {
1739 gb->cycles_for_line = 2;
1740 GB_SLEEP(gb, display, 28, 2);
1741 gb->io_registers[GB_IO_LY] = gb->current_line;
1742 if (gb->position_in_line >= 156 && gb->position_in_line < (uint8_t)-16) {
1743 gb->delayed_glitch_hblank_interrupt = true;
1744 }
1745 GB_STAT_update(gb);
1746 gb->position_in_line = -15;
1747 goto mode_3_start;
1748 }
1749 else {
1750 if (gb->position_in_line >= 156 && gb->position_in_line < (uint8_t)-16) {
1751 gb->delayed_glitch_hblank_interrupt = true;
1752 }
1753 gb->position_in_line = -16;
1754 gb->line_has_fractional_scrolling = false;
1755 }
1756 }
1757
1758 while (true) {
1759 /* Lines 0 - 143 */
1760 for (; gb->current_line < LINES; gb->current_line++) {
1761 wy_check(gb);
1762
1763 if (unlikely(gb->lcd_line_callback)) {
1764 gb->lcd_line_callback(gb, gb->current_line);
1765 }
1766
1767 gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed;
1768 gb->accessed_oam_row = 0;
1769
1770 GB_SLEEP(gb, display, 35, 2);
1771 gb->oam_write_blocked = GB_is_cgb(gb);
1772
1773 GB_SLEEP(gb, display, 6, 1);
1774 gb->io_registers[GB_IO_LY] = gb->current_line;
1775 gb->oam_read_blocked = !gb->cgb_double_speed || gb->model >= GB_MODEL_CGB_D;
1776 gb->ly_for_comparison = gb->current_line? -1 : 0;
1777
1778 /* The OAM STAT interrupt occurs 1 T-cycle before STAT actually changes, except on line 0.
1779 PPU glitch? */
1780 if (gb->current_line != 0) {
1781 gb->mode_for_interrupt = 2;
1782 gb->io_registers[GB_IO_STAT] &= ~3;
1783 }
1784 else if (!GB_is_cgb(gb)) {
1785 gb->io_registers[GB_IO_STAT] &= ~3;
1786 }
1787 GB_STAT_update(gb);
1788
1789 GB_SLEEP(gb, display, 7, 1);
1790 gb->oam_read_blocked = true;
1791 gb->io_registers[GB_IO_STAT] &= ~3;
1792 gb->io_registers[GB_IO_STAT] |= 2;
1793 gb->mode_for_interrupt = 2;
1794 gb->oam_write_blocked = true;
1795 gb->ly_for_comparison = gb->current_line;
1796 wy_check(gb);
1797
1798 GB_STAT_update(gb);
1799 gb->mode_for_interrupt = -1;
1800 GB_STAT_update(gb);
1801 gb->n_visible_objs = 0;
1802 gb->orig_n_visible_objs = 0;
1803
1804 if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) {
1805 GB_BATCHPOINT(gb, display, 5, 80);
1806 }
1807 for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) {
1808 if (GB_is_cgb(gb)) {
1809 add_object_from_index(gb, gb->oam_search_index);
1810 /* The CGB does not care about the accessed OAM row as there's no OAM bug */
1811 }
1812 GB_SLEEP(gb, display, 8, 2);
1813 if (!GB_is_cgb(gb)) {
1814 add_object_from_index(gb, gb->oam_search_index);
1815 gb->accessed_oam_row = (gb->oam_search_index & ~1) * 4 + 8;
1816 }
1817 if (gb->oam_search_index == 37) {
1818 gb->vram_read_blocked = !GB_is_cgb(gb);
1819 gb->vram_write_blocked = false;
1820 gb->cgb_palettes_blocked = false;
1821 gb->oam_write_blocked = GB_is_cgb(gb);
1822 }
1823 }
1824 gb->cycles_for_line = MODE2_LENGTH + 4;
1825 gb->orig_n_visible_objs = gb->n_visible_objs;
1826 gb->accessed_oam_row = -1;
1827 gb->io_registers[GB_IO_STAT] &= ~3;
1828 gb->io_registers[GB_IO_STAT] |= 3;
1829 gb->mode_for_interrupt = 3;
1830 gb->vram_read_blocked = true;
1831 gb->vram_write_blocked = true;
1832 gb->cgb_palettes_blocked = false;
1833 gb->oam_write_blocked = true;
1834 gb->oam_read_blocked = true;
1835
1836 GB_STAT_update(gb);
1837
1838
1839 gb->cycles_for_line += 3;
1840 GB_SLEEP(gb, display, 10, 3);
1841
1842 gb->cgb_palettes_blocked = true;
1843 gb->cycles_for_line += 2;
1844 GB_SLEEP(gb, display, 32, 2);
1845 mode_3_start:
1846 gb->disable_window_pixel_insertion_glitch = false;
1847
1848 fifo_clear(&gb->bg_fifo);
1849 fifo_clear(&gb->oam_fifo);
1850 /* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */
1851 fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
1852 gb->lcd_x = 0;
1853
1854 /* The actual rendering cycle */
1855 gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
1856 if ((gb->mode3_batching_length = mode3_batching_length(gb))) {
1857 GB_BATCHPOINT(gb, display, 3, gb->mode3_batching_length);
1858 if (GB_BATCHED_CYCLES(gb, display) >= gb->mode3_batching_length) {
1859 // Successfully batched!
1860 gb->lcd_x = gb->position_in_line = 160;
1861 gb->cycles_for_line += gb->mode3_batching_length;
1862 if (gb->sgb) {
1863 render_line_sgb(gb);
1864 }
1865 else {
1866 render_line(gb);
1867 }
1868 GB_SLEEP(gb, display, 4, gb->mode3_batching_length);
1869 goto skip_slow_mode_3;
1870 }
1871 }
1872 while (true) {
1873 /* Handle window */
1874 /* TODO: It appears that WX checks if the window begins *next* pixel, not *this* pixel. For this reason,
1875 WX=167 has no effect at all (It checks if the PPU X position is 161, which never happens) and WX=166
1876 has weird artifacts (It appears to activate the window during HBlank, as PPU X is temporarily 160 at
1877 that point. The code should be updated to represent this, and this will fix the time travel hack in
1878 WX's access conflict code. */
1879 gb->wx_166_interrupt_glitch = false;
1880 if (unlikely(gb->wy_just_checked)) {
1881 gb->wy_just_checked = false;
1882 }
1883 else if (!gb->wx_triggered && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) {
1884 bool should_activate_window = false;
1885 if (unlikely(gb->io_registers[GB_IO_WX] == 0)) {
1886 if (gb->position_in_line == (uint8_t)-7) {
1887 should_activate_window = true;
1888 }
1889 else if (gb->position_in_line == (uint8_t)-16 && (gb->io_registers[GB_IO_SCX] & 7)) {
1890 should_activate_window = true;
1891 }
1892 else if (gb->position_in_line >= (uint8_t)-15 && gb->position_in_line <= (uint8_t)-8) {
1893 should_activate_window = true;
1894 }
1895 }
1896 else if (gb->io_registers[GB_IO_WX] < 166 + GB_is_cgb(gb)) { // TODO: 166 on the CGB behaves a bit weird
1897 if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
1898 should_activate_window = true;
1899 }
1900 else if (!GB_is_cgb(gb) && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 6) && !gb->wx_just_changed) {
1901 should_activate_window = true;
1902 /* LCD-PPU horizontal desync! It only appears to happen on DMGs, but not all of them.
1903 This doesn't seem to be CPU revision dependent, but most revisions */
1904 if ((gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_DMG_FAMILY && !GB_is_sgb(gb)) {
1905 if (gb->lcd_x > 0) {
1906 gb->lcd_x--;
1907 }
1908 }
1909 }
1910 }
1911
1912 if (should_activate_window) {
1913 gb->window_y++;
1914 gb->window_tile_x = 0;
1915 fifo_clear(&gb->bg_fifo);
1916 /* TODO: Verify fetcher access timings in this case */
1917 if (gb->io_registers[GB_IO_WX] == 0 && (gb->io_registers[GB_IO_SCX] & 7) && !GB_is_cgb(gb)) {
1918 gb->cycles_for_line += 1;
1919 GB_SLEEP(gb, display, 42, 1);
1920 }
1921 else if (gb->io_registers[GB_IO_WX] == 166) {
1922 gb->wx_166_interrupt_glitch = true;
1923 }
1924 gb->wx_triggered = true;
1925 gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
1926 gb->window_is_being_fetched = true;
1927 }
1928 else if (!GB_is_cgb(gb) && gb->io_registers[GB_IO_WX] == 166 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
1929 gb->window_y++;
1930 }
1931 }
1932
1933 if (unlikely(gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7) &&
1934 (!GB_is_cgb(gb) || gb->io_registers[GB_IO_WX] == 0) &&
1935 gb->wx_triggered && !gb->window_is_being_fetched &&
1936 gb->fetcher_state == GB_FETCHER_GET_TILE_T1 &&
1937 gb->bg_fifo.size == 8)) {
1938 // Insert a pixel right at the FIFO's end
1939 gb->insert_bg_pixel = true;
1940 }
1941
1942 /* Handle objects */
1943 /* When the object enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB.
1944 On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */
1945
1946 while (gb->n_visible_objs != 0 &&
1947 gb->objects_x[gb->n_visible_objs - 1] < x_for_object_match(gb)) {
1948 gb->n_visible_objs--;
1949 }
1950
1951 gb->during_object_fetch = true;
1952 while (gb->n_visible_objs != 0 &&
1953 (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN || GB_is_cgb(gb)) &&
1954 gb->objects_x[gb->n_visible_objs - 1] == x_for_object_match(gb)) {
1955
1956 while (gb->fetcher_state < GB_FETCHER_GET_TILE_DATA_HIGH_T2 || fifo_size(&gb->bg_fifo) == 0) {
1957 advance_fetcher_state_machine(gb, &cycles);
1958 gb->cycles_for_line++;
1959 GB_SLEEP(gb, display, 27, 1);
1960 if (gb->object_fetch_aborted) {
1961 goto abort_fetching_object;
1962 }
1963 }
1964
1965 /* TODO: Can this be deleted? { */
1966 advance_fetcher_state_machine(gb, &cycles);
1967 gb->cycles_for_line++;
1968 GB_SLEEP(gb, display, 41, 1);
1969 if (gb->object_fetch_aborted) {
1970 goto abort_fetching_object;
1971 }
1972 /* } */
1973
1974 advance_fetcher_state_machine(gb, &cycles);
1975 dma_sync(gb, &cycles);
1976 gb->mode2_y_bus = oam_read(gb, gb->visible_objs[gb->n_visible_objs - 1] * 4 + 2);
1977 gb->object_flags = oam_read(gb, gb->visible_objs[gb->n_visible_objs - 1] * 4 + 3);
1978
1979 gb->cycles_for_line += 2;
1980 GB_SLEEP(gb, display, 20, 2);
1981 if (gb->object_fetch_aborted) {
1982 goto abort_fetching_object;
1983 }
1984
1985 /* TODO: timing not verified */
1986 dma_sync(gb, &cycles);
1987 gb->object_low_line_address = get_object_line_address(gb,
1988 gb->objects_y[gb->n_visible_objs - 1],
1989 gb->mode2_y_bus,
1990 gb->object_flags);
1991 gb->object_tile_data[0] = vram_read(gb, gb->object_low_line_address);
1992
1993
1994 gb->cycles_for_line += 2;
1995 GB_SLEEP(gb, display, 39, 2);
1996 if (gb->object_fetch_aborted) {
1997 goto abort_fetching_object;
1998 }
1999
2000 gb->during_object_fetch = false;
2001 gb->cycles_for_line++;
2002 gb->object_low_line_address = get_object_line_address(gb,
2003 gb->objects_y[gb->n_visible_objs - 1],
2004 gb->mode2_y_bus,
2005 gb->object_flags);
2006
2007 dma_sync(gb, &cycles);
2008 gb->object_tile_data[1] = vram_read(gb, gb->object_low_line_address + 1);
2009 GB_SLEEP(gb, display, 40, 1);
2010
2011
2012 uint8_t palette = (gb->object_flags & 0x10) ? 1 : 0;
2013 if (gb->cgb_mode) {
2014 palette = gb->object_flags & 0x7;
2015 }
2016 fifo_overlay_object_row(&gb->oam_fifo,
2017 gb->object_tile_data[0],
2018 gb->object_tile_data[1],
2019 palette,
2020 gb->object_flags & 0x80,
2021 gb->object_priority == GB_OBJECT_PRIORITY_INDEX? gb->visible_objs[gb->n_visible_objs - 1] : 0,
2022 gb->object_flags & 0x20);
2023
2024 gb->data_for_sel_glitch = gb->vram_ppu_blocked? 0xFF : gb->vram[gb->object_low_line_address + 1];
2025 gb->n_visible_objs--;
2026 }
2027
2028 abort_fetching_object:
2029 gb->object_fetch_aborted = false;
2030 gb->during_object_fetch = false;
2031
2032 render_pixel_if_possible(gb);
2033 advance_fetcher_state_machine(gb, &cycles);
2034 if (gb->position_in_line == 160) break;
2035
2036 gb->cycles_for_line++;
2037 GB_SLEEP(gb, display, 21, 1);
2038 if (unlikely(gb->wx_166_interrupt_glitch)) {
2039 gb->mode_for_interrupt = 0;
2040 GB_STAT_update(gb);
2041 }
2042 }
2043 skip_slow_mode_3:
2044 gb->position_in_line = -16;
2045 gb->line_has_fractional_scrolling = false;
2046
2047
2048 /* TODO: Commented code seems incorrect (glitches Tesserae), verify further */
2049
2050 if (gb->fetcher_state == GB_FETCHER_GET_TILE_DATA_HIGH_T1 ||
2051 gb->fetcher_state == GB_FETCHER_GET_TILE_DATA_HIGH_T2) {
2052 // Make sure current_tile_data[1] holds the last tile data byte read
2053 gb->current_tile_data[1] = gb->current_tile_data[0];
2054
2055 //gb->data_for_sel_glitch = gb->current_tile_data[0];
2056 }
2057 /*
2058 else {
2059 gb->data_for_sel_glitch = gb->current_tile_data[1];
2060 }
2061 */
2062 while (gb->lcd_x != 160 && !gb->disable_rendering && gb->screen && !gb->sgb) {
2063 /* Oh no! The PPU and LCD desynced! Fill the rest of the line with the last color. */
2064 uint32_t *dest = NULL;
2065 if (gb->border_mode != GB_BORDER_ALWAYS) {
2066 dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH;
2067 }
2068 else {
2069 dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH;
2070 }
2071 *dest = (gb->lcd_x == 0)? gb->background_palettes_rgb[0] : dest[-1];
2072 gb->lcd_x++;
2073
2074 }
2075
2076 /* TODO: Verify timing { */
2077 if (gb->current_line == 143) {
2078 gb->window_y = -1;
2079 }
2080 if (!GB_is_cgb(gb) && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) && gb->io_registers[GB_IO_WX] == 166) {
2081 gb->wx_triggered = true;
2082 gb->window_tile_x = 1;
2083 gb->window_y++;
2084 }
2085 else {
2086 gb->wx_triggered = false;
2087 }
2088 /* } */
2089
2090 if (!gb->cgb_double_speed) {
2091 gb->io_registers[GB_IO_STAT] &= ~3;
2092 gb->mode_for_interrupt = 0;
2093 gb->oam_read_blocked = gb->model >= GB_MODEL_CGB_D;
2094 gb->vram_read_blocked = false;
2095 gb->oam_write_blocked = false;
2096 gb->vram_write_blocked = false;
2097 }
2098
2099 gb->cycles_for_line++;
2100 GB_SLEEP(gb, display, 22, 1);
2101
2102 gb->io_registers[GB_IO_STAT] &= ~3;
2103 gb->mode_for_interrupt = 0;
2104 gb->oam_read_blocked = false;
2105 gb->vram_read_blocked = false;
2106 gb->oam_write_blocked = false;
2107 gb->vram_write_blocked = false;
2108 GB_STAT_update(gb);
2109
2110 /* Todo: Measure this value */
2111 gb->cycles_for_line += 2;
2112 GB_SLEEP(gb, display, 33, 2);
2113 gb->cgb_palettes_blocked = !gb->cgb_double_speed;
2114
2115 if (gb->hdma_on_hblank && !gb->halted && !gb->stopped) {
2116 gb->hdma_on = true;
2117 }
2118
2119 gb->cycles_for_line += 2;
2120 GB_SLEEP(gb, display, 36, 2);
2121 gb->cgb_palettes_blocked = false;
2122
2123 if (gb->cycles_for_line > LINE_LENGTH - 2) {
2124 gb->cycles_for_line = 0;
2125 GB_SLEEP(gb, display, 43, LINE_LENGTH - gb->cycles_for_line);
2126 goto display9;
2127 }
2128
2129 {
2130 uint16_t cycles_for_line = gb->cycles_for_line;
2131 gb->cycles_for_line = 0;
2132 GB_SLEEP(gb, display, 11, LINE_LENGTH - cycles_for_line - 2);
2133 }
2134
2135 gb->cycles_for_line = 0;
2136 GB_SLEEP(gb, display, 31, 2);
2137 if (gb->current_line != LINES - 1) {
2138 gb->mode_for_interrupt = 2;
2139 }
2140
2141 // Todo: unverified timing
2142 gb->current_lcd_line++;
2143 if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
2144 GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME);
2145 }
2146
2147 if (gb->icd_hreset_callback) {
2148 gb->icd_hreset_callback(gb);
2149 }
2150 }
2151 /* Lines 144 - 152 */
2152 for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) {
2153 gb->ly_for_comparison = -1;
2154 if (unlikely(gb->lcd_line_callback)) {
2155 gb->lcd_line_callback(gb, gb->current_line);
2156 }
2157 GB_STAT_update(gb);
2158 GB_SLEEP(gb, display, 26, 2);
2159 gb->io_registers[GB_IO_LY] = gb->current_line;
2160 if (gb->current_line == LINES && !gb->stat_interrupt_line && (gb->io_registers[GB_IO_STAT] & 0x20)) {
2161 gb->io_registers[GB_IO_IF] |= 2;
2162 }
2163 GB_SLEEP(gb, display, 12, 2);
2164 if (gb->delayed_glitch_hblank_interrupt) {
2165 gb->delayed_glitch_hblank_interrupt = false;
2166 gb->mode_for_interrupt = 0;
2167 }
2168 gb->ly_for_comparison = gb->current_line;
2169 GB_STAT_update(gb);
2170 GB_SLEEP(gb, display, 24, 1);
2171
2172 if (gb->current_line == LINES) {
2173 /* Entering VBlank state triggers the OAM interrupt */
2174 gb->io_registers[GB_IO_STAT] &= ~3;
2175 gb->io_registers[GB_IO_STAT] |= 1;
2176 gb->io_registers[GB_IO_IF] |= 1;
2177 if (!gb->stat_interrupt_line && (gb->io_registers[GB_IO_STAT] & 0x20)) {
2178 gb->io_registers[GB_IO_IF] |= 2;
2179 }
2180 gb->mode_for_interrupt = 1;
2181 GB_STAT_update(gb);
2182
2183 if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) {
2184 if (GB_is_cgb(gb)) {
2185 GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME);
2186 gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_RENDERED;
2187 }
2188 else {
2189 if (!GB_is_sgb(gb)) {
2190 update_frame_parity(gb); // TODO: test actual timing
2191 GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME);
2192 }
2193 gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_RENDERED;
2194 }
2195 }
2196 else {
2197 if (!GB_is_sgb(gb)) {
2198 update_frame_parity(gb); // TODO: test actual timing
2199 GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME);
2200 }
2201 }
2202 }
2203
2204 /* 3640 is just a few cycles less than 4 lines, no clue where the
2205 AGB constant comes from (These are measured and confirmed) */
2206 gb->frame_repeat_countdown = LINES * LINE_LENGTH * 2 + (gb->model > GB_MODEL_CGB_E? 5982 : 3640); // 8MHz units
2207 if (gb->display_cycles < gb->frame_repeat_countdown) {
2208 gb->frame_repeat_countdown -= gb->display_cycles;
2209 }
2210 else {
2211 gb->frame_repeat_countdown = 0;
2212 }
2213
2214 GB_SLEEP(gb, display, 13, LINE_LENGTH - 5);
2215 }
2216
2217 /* Lines 153 */
2218 gb->ly_for_comparison = -1;
2219 GB_STAT_update(gb);
2220 GB_SLEEP(gb, display, 19, 2);
2221 gb->io_registers[GB_IO_LY] = 153;
2222 GB_SLEEP(gb, display, 14, (gb->model > GB_MODEL_CGB_C)? 2: 4);
2223
2224 if (gb->model <= GB_MODEL_CGB_C && !gb->cgb_double_speed) {
2225 gb->io_registers[GB_IO_LY] = 0;
2226 }
2227 gb->ly_for_comparison = 153;
2228 GB_STAT_update(gb);
2229 GB_SLEEP(gb, display, 15, (gb->model > GB_MODEL_CGB_C)? 4: 2);
2230
2231 gb->io_registers[GB_IO_LY] = 0;
2232 gb->ly_for_comparison = (gb->model > GB_MODEL_CGB_C || gb->cgb_double_speed)? 153 : -1;
2233 GB_STAT_update(gb);
2234 GB_SLEEP(gb, display, 16, 4);
2235
2236 gb->ly_for_comparison = 0;
2237 GB_STAT_update(gb);
2238 GB_SLEEP(gb, display, 29, 12); /* Writing to LYC during this period on a CGB has side effects */
2239 GB_SLEEP(gb, display, 17, LINE_LENGTH - 24);
2240
2241
2242 gb->current_line = 0;
2243 gb->wy_triggered = false;
2244
2245 // TODO: not the correct timing
2246 gb->current_lcd_line = 0;
2247 if (gb->icd_vreset_callback) {
2248 gb->icd_vreset_callback(gb);
2249 }
2250 }
2251 }
2252
2253 void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index)
2254 {
2255 uint32_t none_palette[4];
2256 uint32_t *palette = NULL;
2257
2258 switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) {
2259 default:
2260 case GB_PALETTE_NONE:
2261 none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
2262 none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
2263 none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
2264 none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
2265 palette = none_palette;
2266 break;
2267 case GB_PALETTE_BACKGROUND:
2268 palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
2269 break;
2270 case GB_PALETTE_OAM:
2271 palette = gb->object_palettes_rgb + (4 * (palette_index & 7));
2272 break;
2273 }
2274
2275 for (unsigned y = 0; y < 192; y++) {
2276 for (unsigned x = 0; x < 256; x++) {
2277 if (x >= 128 && !GB_is_cgb(gb)) {
2278 *(dest++) = gb->background_palettes_rgb[0];
2279 continue;
2280 }
2281 uint16_t tile = (x % 128) / 8 + y / 8 * 16;
2282 uint16_t tile_address = tile * 0x10 + (x >= 128? 0x2000 : 0);
2283 uint8_t pixel = (((gb->vram[tile_address + (y & 7) * 2 ] >> ((~x)&7)) & 1 ) |
2284 ((gb->vram[tile_address + (y & 7) * 2 + 1] >> ((~x)&7)) & 1) << 1);
2285
2286 if (!gb->cgb_mode) {
2287 if (palette_type == GB_PALETTE_BACKGROUND) {
2288 pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
2289 }
2290 else if (!gb->cgb_mode) {
2291 if (palette_type == GB_PALETTE_OAM) {
2292 pixel = ((gb->io_registers[palette_index == 0? GB_IO_OBP0 : GB_IO_OBP1] >> (pixel << 1)) & 3);
2293 }
2294 }
2295 }
2296
2297
2298 *(dest++) = palette[pixel];
2299 }
2300 }
2301 }
2302
2303 void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type)
2304 {
2305 uint32_t none_palette[4];
2306 uint32_t *palette = NULL;
2307 uint16_t map = 0x1800;
2308
2309 switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) {
2310 case GB_PALETTE_NONE:
2311 none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
2312 none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
2313 none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
2314 none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
2315 palette = none_palette;
2316 break;
2317 case GB_PALETTE_BACKGROUND:
2318 palette = gb->background_palettes_rgb + (4 * (palette_index & 7));
2319 break;
2320 case GB_PALETTE_OAM:
2321 palette = gb->object_palettes_rgb + (4 * (palette_index & 7));
2322 break;
2323 case GB_PALETTE_AUTO:
2324 break;
2325 }
2326
2327 if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_MAP)) {
2328 map = 0x1C00;
2329 }
2330
2331 if (tileset_type == GB_TILESET_AUTO) {
2332 tileset_type = (gb->io_registers[GB_IO_LCDC] & GB_LCDC_TILE_SEL)? GB_TILESET_8800 : GB_TILESET_8000;
2333 }
2334
2335 for (unsigned y = 0; y < 256; y++) {
2336 for (unsigned x = 0; x < 256; x++) {
2337 uint8_t tile = gb->vram[map + x/8 + y/8 * 32];
2338 uint16_t tile_address;
2339 uint8_t attributes = 0;
2340
2341 if (tileset_type == GB_TILESET_8800) {
2342 tile_address = tile * 0x10;
2343 }
2344 else {
2345 tile_address = (int8_t) tile * 0x10 + 0x1000;
2346 }
2347
2348 if (gb->cgb_mode) {
2349 attributes = gb->vram[map + x/8 + y/8 * 32 + 0x2000];
2350 }
2351
2352 if (attributes & 0x8) {
2353 tile_address += 0x2000;
2354 }
2355
2356 uint8_t pixel = (((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 ] >> (((attributes & 0x20)? x : ~x)&7)) & 1 ) |
2357 ((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 + 1] >> (((attributes & 0x20)? x : ~x)&7)) & 1) << 1);
2358
2359 if (!gb->cgb_mode && (palette_type == GB_PALETTE_BACKGROUND || palette_type == GB_PALETTE_AUTO)) {
2360 pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
2361 }
2362
2363 if (palette) {
2364 *(dest++) = palette[pixel];
2365 }
2366 else {
2367 *(dest++) = gb->background_palettes_rgb[(attributes & 7) * 4 + pixel];
2368 }
2369 }
2370 }
2371 }
2372
2373 uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *object_height)
2374 {
2375 uint8_t count = 0;
2376 *object_height = (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_SIZE) ? 16:8;
2377 uint8_t oam_to_dest_index[40] = {0,};
2378 for (signed y = 0; y < LINES; y++) {
2379 object_t *object = (object_t *) &gb->oam;
2380 uint8_t objects_in_line = 0;
2381 bool obscured = false;
2382 for (uint8_t i = 0; i < 40; i++, object++) {
2383 signed object_y = object->y - 16;
2384 // Is object not in this line?
2385 if (object_y > y || object_y + *object_height <= y) continue;
2386 if (++objects_in_line == 11) obscured = true;
2387
2388 GB_oam_info_t *info = NULL;
2389 if (!oam_to_dest_index[i]) {
2390 info = dest + count;
2391 oam_to_dest_index[i] = ++count;
2392 info->x = object->x;
2393 info->y = object->y;
2394 info->tile = *object_height == 16? object->tile & 0xFE : object->tile;
2395 info->flags = object->flags;
2396 info->obscured_by_line_limit = false;
2397 info->oam_addr = 0xFE00 + i * sizeof(*object);
2398 }
2399 else {
2400 info = dest + oam_to_dest_index[i] - 1;
2401 }
2402 info->obscured_by_line_limit |= obscured;
2403 }
2404 }
2405
2406 for (unsigned i = 0; i < count; i++) {
2407 uint16_t vram_address = dest[i].tile * 0x10;
2408 uint8_t flags = dest[i].flags;
2409 uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0);
2410 if (GB_is_cgb(gb) && (flags & 0x8)) {
2411 vram_address += 0x2000;
2412 }
2413
2414 uint8_t dmg_palette = gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0];
2415 if (dmg_palette == 0xFF) {
2416 dmg_palette = 0xFC;
2417 }
2418 else if (dmg_palette == 0x00) {
2419 dmg_palette = 0x03;
2420 }
2421
2422 for (unsigned y = 0; y < *object_height; y++) {
2423 unrolled for (unsigned x = 0; x < 8; x++) {
2424 uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) |
2425 ((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 );
2426
2427 if (!gb->cgb_mode) {
2428 color = (dmg_palette >> (color << 1)) & 3;
2429 }
2430 dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?*object_height - 1 -y:y) * 8] = gb->object_palettes_rgb[palette * 4 + color];
2431 }
2432 vram_address += 2;
2433 }
2434 }
2435 return count;
2436 }
2437
2438
2439 bool GB_is_odd_frame(GB_gameboy_t *gb)
2440 {
2441 return gb->is_odd_frame;
2442 }
2443
2444 void GB_set_object_rendering_disabled(GB_gameboy_t *gb, bool disabled)
2445 {
2446 gb->objects_disabled = disabled;
2447 }
2448
2449 void GB_set_background_rendering_disabled(GB_gameboy_t *gb, bool disabled)
2450 {
2451 gb->background_disabled = disabled;
2452 }
2453
2454 bool GB_is_object_rendering_disabled(GB_gameboy_t *gb)
2455 {
2456 return gb->objects_disabled;
2457 }
2458
2459 bool GB_is_background_rendering_disabled(GB_gameboy_t *gb)
2460 {
2461 return gb->background_disabled;
2462 }
2463
2464 unsigned GB_get_screen_width(GB_gameboy_t *gb)
2465 {
2466 switch (gb->border_mode) {
2467 default:
2468 case GB_BORDER_SGB:
2469 return GB_is_hle_sgb(gb)? 256 : 160;
2470 case GB_BORDER_NEVER:
2471 return 160;
2472 case GB_BORDER_ALWAYS:
2473 return 256;
2474 }
2475 }
2476
2477 unsigned GB_get_screen_height(GB_gameboy_t *gb)
2478 {
2479 switch (gb->border_mode) {
2480 default:
2481 case GB_BORDER_SGB:
2482 return GB_is_hle_sgb(gb)? 224 : 144;
2483 case GB_BORDER_NEVER:
2484 return 144;
2485 case GB_BORDER_ALWAYS:
2486 return 224;
2487 }
2488 }
2489
2490 double GB_get_usual_frame_rate(GB_gameboy_t *gb)
2491 {
2492 return GB_get_clock_rate(gb) / (double)LCDC_PERIOD;
2493 }
2494
2495 void GB_set_enable_skipped_frame_vblank_callbacks(GB_gameboy_t *gb, bool enable)
2496 {
2497 gb->enable_skipped_frame_vblank_callbacks = enable;
2498 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.