#ifdef PPU_CPP #include "mode7.cpp" void PPU::Background::scanline() { if(self.vcounter() == 1) { t.mosaic_y = 1; t.mosaic_countdown = 0; } else { if(!regs.mosaic || !t.mosaic_countdown) t.mosaic_y = self.vcounter(); if(!t.mosaic_countdown) t.mosaic_countdown = regs.mosaic + 1; t.mosaic_countdown--; } t.x = 0; } void PPU::Background::run() { bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); if((self.hcounter() & 2) == 0) { output.main.priority = 0; output.sub.priority = 0; } else if(hires == false) { return; } if(regs.mode == Mode::Inactive) return; if(regs.main_enabled == false && regs.sub_enabled == false) return; unsigned x = t.x++; unsigned y = t.mosaic_y; if(regs.mode == Mode::Mode7) return run_mode7(x, y); unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2); unsigned palette_offset = (self.regs.bgmode == 0 ? (id << 5) : 0); unsigned palette_size = 2 << color_depth; unsigned tile_mask = 0x0fff >> color_depth; unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); unsigned tile_width = (!hires ? tile_height : 4); unsigned width = (!hires ? 256 : 512); unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_y = mask_x; if(regs.screen_size & 1) mask_x <<= 1; if(regs.screen_size & 2) mask_y <<= 1; mask_x--; mask_y--; unsigned hscroll = regs.hoffset; unsigned vscroll = regs.voffset; if(hires) { hscroll <<= 1; if(self.regs.interlace) y = (y << 1) + self.field(); } unsigned hoffset = hscroll + mosaic_table[regs.mosaic][x]; unsigned voffset = vscroll + y; if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { uint16 opt_x = (x + (hscroll & 7)); if(opt_x >= 8) { unsigned hval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0); unsigned vval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8); unsigned opt_valid_bit = (id == ID::BG1 ? 0x2000 : 0x4000); if(self.regs.bgmode == 4) { if(hval & opt_valid_bit) { if(!(hval & 0x8000)) { hoffset = opt_x + (hval & ~7); } else { voffset = y + hval; } } } else { if(hval & opt_valid_bit) hoffset = opt_x + (hval & ~7); if(vval & opt_valid_bit) voffset = y + vval; } } } hoffset &= mask_x; voffset &= mask_y; unsigned tile_number = get_tile(hoffset, voffset); bool mirror_y = tile_number & 0x8000; bool mirror_x = tile_number & 0x4000; unsigned priority = (tile_number & 0x2000 ? regs.priority1 : regs.priority0); unsigned palette_number = (tile_number >> 10) & 7; unsigned palette_index = palette_offset + (palette_number << palette_size); if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_number += 1; if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_number += 16; tile_number &= 0x03ff; tile_number += tiledata_index; tile_number &= tile_mask; if(mirror_x) hoffset ^= 7; if(mirror_y) voffset ^= 7; uint8 color = get_color(hoffset, voffset, tile_number); if(color == 0) return; color += palette_index; if(hires == false) { if(regs.main_enabled) { output.main.priority = priority; output.main.palette = color; output.main.tile = tile_number; } if(regs.sub_enabled) { output.sub.priority = priority; output.sub.palette = color; output.sub.tile = tile_number; } } else { if(x & 1) { if(regs.main_enabled) { output.main.priority = priority; output.main.palette = color; output.main.tile = tile_number; } } else { if(regs.sub_enabled) { output.sub.priority = priority; output.sub.palette = color; output.sub.tile = tile_number; } } } } unsigned PPU::Background::get_tile(unsigned x, unsigned y) { bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); unsigned tile_width = (!hires ? tile_height : 4); unsigned width = (!hires ? 256 : 512); unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_y = mask_x; if(regs.screen_size & 1) mask_x <<= 1; if(regs.screen_size & 2) mask_y <<= 1; mask_x--; mask_y--; unsigned screen_x = (regs.screen_size & 1 ? (32 << 5) : 0); unsigned screen_y = (regs.screen_size & 2 ? (32 << 5) : 0); if(regs.screen_size == 3) screen_y <<= 1; x = (x & mask_x) >> tile_width; y = (y & mask_y) >> tile_height; uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); if(x & 0x20) pos += screen_x; if(y & 0x20) pos += screen_y; uint16 addr = regs.screen_addr + (pos << 1); return memory::vram[addr + 0] + (memory::vram[addr + 1] << 8); } unsigned PPU::Background::get_color(unsigned x, unsigned y, uint16 offset) { unsigned mask = 0x80 >> (x & 7); switch(regs.mode) { case Background::Mode::BPP2: { offset = (offset * 16) + ((y & 7) * 2); unsigned d0 = memory::vram[offset + 0]; unsigned d1 = memory::vram[offset + 1]; return (((bool)(d0 & mask)) << 0) + (((bool)(d1 & mask)) << 1); } case Background::Mode::BPP4: { offset = (offset * 32) + ((y & 7) * 2); unsigned d0 = memory::vram[offset + 0]; unsigned d1 = memory::vram[offset + 1]; unsigned d2 = memory::vram[offset + 16]; unsigned d3 = memory::vram[offset + 17]; return (((bool)(d0 & mask)) << 0) + (((bool)(d1 & mask)) << 1) + (((bool)(d2 & mask)) << 2) + (((bool)(d3 & mask)) << 3); } case Background::Mode::BPP8: { offset = (offset * 64) + ((y & 7) * 2); unsigned d0 = memory::vram[offset + 0]; unsigned d1 = memory::vram[offset + 1]; unsigned d2 = memory::vram[offset + 16]; unsigned d3 = memory::vram[offset + 17]; unsigned d4 = memory::vram[offset + 32]; unsigned d5 = memory::vram[offset + 33]; unsigned d6 = memory::vram[offset + 48]; unsigned d7 = memory::vram[offset + 49]; return (((bool)(d0 & mask)) << 0) + (((bool)(d1 & mask)) << 1) + (((bool)(d2 & mask)) << 2) + (((bool)(d3 & mask)) << 3) + (((bool)(d4 & mask)) << 4) + (((bool)(d5 & mask)) << 5) + (((bool)(d6 & mask)) << 6) + (((bool)(d7 & mask)) << 7); } }; } void PPU::Background::reset() { t.x = 0; t.mosaic_y = 0; t.mosaic_countdown = 0; regs.tiledata_addr = 0; regs.screen_addr = 0; regs.screen_size = 0; regs.mosaic = 0; regs.tile_size = 0; regs.mode = 0; regs.priority0 = 0; regs.priority1 = 0; regs.main_enabled = 0; regs.sub_enabled = 0; regs.hoffset = 0; regs.voffset = 0; output.main.palette = 0; output.main.priority = 0; output.sub.palette = 0; output.sub.priority = 0; } PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { for(unsigned m = 0; m < 16; m++) { for(unsigned x = 0; x < 4096; x++) { mosaic_table[m][x] = (x / (m + 1)) * (m + 1); } } } uint16 PPU::Background::mosaic_table[16][4096]; #endif