git.y1.nz

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.