#ifdef PPU_CPP void PPU::cgb_render() { for(unsigned n = 0; n < 160; n++) { line[n] = 0x7fff; origin[n] = Origin::None; } if(status.display_enable) { cgb_render_bg(); if(status.window_display_enable) cgb_render_window(); if(status.ob_enable) cgb_render_ob(); } uint32* output = screen + status.ly * 160; for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]]; interface->lcdScanline(); } //Attributes: //0x80: 0 = OAM priority, 1 = BG priority //0x40: vertical flip //0x20: horizontal flip //0x08: VRAM bank# //0x07: palette# void PPU::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& tile, unsigned& attr, unsigned& data) { unsigned tmaddr = 0x1800 + (select << 10); tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff; tile = vram[0x0000 + tmaddr]; attr = vram[0x2000 + tmaddr]; unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000; if(status.bg_tiledata_select == 0) { tdaddr += 0x1000 + ((int8)tile << 4); } else { tdaddr += 0x0000 + (tile << 4); } y &= 7; if(attr & 0x40) y ^= 7; tdaddr += y << 1; data = vram[tdaddr++] << 0; data |= vram[tdaddr++] << 8; if(attr & 0x20) data = hflip(data); } void PPU::cgb_render_bg() { unsigned iy = (status.ly + status.scy) & 255; unsigned ix = status.scx, tx = ix & 7; unsigned tile, attr, data; cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data); for(unsigned ox = 0; ox < 160; ox++) { unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) | ((data & (0x8000 >> tx)) ? 2 : 0); unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); unsigned palette = 0; palette |= bgpd[palette_index++] << 0; palette |= bgpd[palette_index++] << 8; palette &= 0x7fff; line[ox] = palette; origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG); ix = (ix + 1) & 255; tx = (tx + 1) & 7; if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data); } } void PPU::cgb_render_window() { if(status.ly - status.wy >= 144u) return; if(status.wx >= 167u) return; unsigned iy = status.wyc++; unsigned ix = (7 - status.wx) & 255, tx = ix & 7; unsigned tile, attr, data; cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data); for(unsigned ox = 0; ox < 160; ox++) { unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) | ((data & (0x8000 >> tx)) ? 2 : 0); unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); unsigned palette = 0; palette |= bgpd[palette_index++] << 0; palette |= bgpd[palette_index++] << 8; palette &= 0x7fff; if(ox - (status.wx - 7) < 160u) { line[ox] = palette; origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG); } ix = (ix + 1) & 255; tx = (tx + 1) & 7; if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data); } } //Attributes: //0x80: 0 = OBJ above BG, 1 = BG above OBJ //0x40: vertical flip //0x20: horizontal flip //0x08: VRAM bank# //0x07: palette# void PPU::cgb_render_ob() { const unsigned Height = (status.ob_size == 0 ? 8 : 16); unsigned sprite[10], sprites = 0; //find first ten sprites on this scanline for(unsigned s = 0; s < 40; s++) { unsigned sy = oam[(s << 2) + 0] - 16; unsigned sx = oam[(s << 2) + 1] - 8; sy = status.ly - sy; if(sy >= Height) continue; sprite[sprites++] = s; if(sprites == 10) break; } //sort by X-coordinate, when equal, lower address comes first for(unsigned x = 0; x < sprites; x++) { for(unsigned y = x + 1; y < sprites; y++) { signed sx = oam[(sprite[x] << 2) + 1] - 8; signed sy = oam[(sprite[y] << 2) + 1] - 8; if(sy < sx) { sprite[x] ^= sprite[y]; sprite[y] ^= sprite[x]; sprite[x] ^= sprite[y]; } } } //render backwards, so that first sprite has highest priority for(signed s = sprites - 1; s >= 0; s--) { unsigned n = sprite[s] << 2; unsigned sy = oam[n + 0] - 16; unsigned sx = oam[n + 1] - 8; unsigned tile = oam[n + 2] & ~status.ob_size; unsigned attr = oam[n + 3]; sy = status.ly - sy; if(sy >= Height) continue; if(attr & 0x40) sy ^= (Height - 1); unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0; data |= vram[tdaddr++] << 0; data |= vram[tdaddr++] << 8; if(attr & 0x20) data = hflip(data); for(unsigned tx = 0; tx < 8; tx++) { unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) | ((data & (0x8000 >> tx)) ? 2 : 0); if(index == 0) continue; unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); unsigned palette = 0; palette |= obpd[palette_index++] << 0; palette |= obpd[palette_index++] << 8; palette &= 0x7fff; unsigned ox = sx + tx; if(ox < 160) { //When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window if(status.bg_enable) { if(origin[ox] == Origin::BGP) continue; if(attr & 0x80) { if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) { if(line[ox] > 0) continue; } } } line[ox] = palette; origin[ox] = Origin::OB; } } } } #endif