Path: blob/master/libsnes/bsnes/snes/ppu/background/background.cpp
2 views
#ifdef PPU_CPP12#include "mode7.cpp"34//V = 0, H = 05void PPU::Background::frame() {6}78//H = 09void PPU::Background::scanline() {10}1112//H = 6013void PPU::Background::begin() {14bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);15x = -7;16y = self.vcounter();1718if(y == 1) {19mosaic.vcounter = regs.mosaic + 1;20mosaic.voffset = 1;21cache.hoffset = regs.hoffset;22cache.voffset = regs.voffset;23} else if(--mosaic.vcounter == 0) {24mosaic.vcounter = regs.mosaic + 1;25mosaic.voffset += regs.mosaic + 1;26cache.hoffset = regs.hoffset;27cache.voffset = regs.voffset;28}2930tile_counter = (7 - (cache.hoffset & 7)) << hires;31for(unsigned n = 0; n < 8; n++) data[n] = 0;3233mosaic.hcounter = regs.mosaic + 1;34mosaic.hoffset = 0;3536if(regs.mode == Mode::Mode7) return begin_mode7();37if(regs.mosaic == 0) {38cache.hoffset = regs.hoffset;39cache.voffset = regs.voffset;40}41}4243void PPU::Background::get_tile() {44bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);4546unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2);47unsigned palette_offset = (self.regs.bgmode == 0 ? id << 5 : 0);48unsigned palette_size = 2 << color_depth;49unsigned tile_mask = 0x0fff >> color_depth;50unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth);5152unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4);53unsigned tile_width = (!hires ? tile_height : 4);5455unsigned width = 256 << hires;5657unsigned hmask = (tile_height == 3 ? width : width << 1);58unsigned vmask = hmask;59if(regs.screen_size & 1) hmask <<= 1;60if(regs.screen_size & 2) vmask <<= 1;61hmask--;62vmask--;6364unsigned px = x << hires;65unsigned py = (regs.mosaic == 0 ? y : mosaic.voffset);6667unsigned hscroll = cache.hoffset;68unsigned vscroll = cache.voffset;69if(hires) {70hscroll <<= 1;71if(self.regs.interlace) py = (py << 1) + self.field();72}7374unsigned hoffset = hscroll + px;75unsigned voffset = vscroll + py;7677if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) {78uint16 offset_x = (x + (hscroll & 7));7980if(offset_x >= 8) {81unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 0);82unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 8);83unsigned valid_mask = (id == ID::BG1 ? 0x2000 : 0x4000);8485if(self.regs.bgmode == 4) {86if(hval & valid_mask) {87if((hval & 0x8000) == 0) {88hoffset = offset_x + (hval & ~7);89} else {90voffset = y + hval;91}92}93} else {94if(hval & valid_mask) hoffset = offset_x + (hval & ~7);95if(vval & valid_mask) voffset = y + vval;96}97}98}99100hoffset &= hmask;101voffset &= vmask;102103unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0);104unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0);105if(regs.screen_size == 3) screen_y <<= 1;106107unsigned tx = hoffset >> tile_width;108unsigned ty = voffset >> tile_height;109110uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f);111if(tx & 0x20) offset += screen_x;112if(ty & 0x20) offset += screen_y;113114uint16 addr = regs.screen_addr + (offset << 1);115tile = (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8);116bool mirror_y = tile & 0x8000;117bool mirror_x = tile & 0x4000;118priority = (tile & 0x2000 ? regs.priority1 : regs.priority0);119palette_number = (tile >> 10) & 7;120palette_index = palette_offset + (palette_number << palette_size);121122if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile += 1;123if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile += 16;124uint16 character = ((tile & 0x03ff) + tiledata_index) & tile_mask;125126if(mirror_y) voffset ^= 7;127offset = (character << (4 + color_depth)) + ((voffset & 7) << 1);128129switch(regs.mode) {130case Mode::BPP8:131data[7] = ppu.vram[offset + 49];132data[6] = ppu.vram[offset + 48];133data[5] = ppu.vram[offset + 33];134data[4] = ppu.vram[offset + 32];135case Mode::BPP4:136data[3] = ppu.vram[offset + 17];137data[2] = ppu.vram[offset + 16];138case Mode::BPP2:139data[1] = ppu.vram[offset + 1];140data[0] = ppu.vram[offset + 0];141}142143if(mirror_x) for(unsigned n = 0; n < 8; n++) {144//reverse data bits in data[n]: 01234567 -> 76543210145data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0);146data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc);147data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa);148}149}150151void PPU::Background::run(bool screen) {152if(self.vcounter() == 0) return;153bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);154155if(screen == Screen::Sub) {156output.main.priority = 0;157output.sub.priority = 0;158if(hires == false) return;159}160161if(regs.mode == Mode::Inactive) return;162if(regs.mode == Mode::Mode7) return run_mode7();163164if(tile_counter-- == 0) {165tile_counter = 7;166get_tile();167}168169uint8 palette = get_tile_color();170if(x == 0) mosaic.hcounter = 1;171if(x >= 0 && --mosaic.hcounter == 0) {172mosaic.hcounter = regs.mosaic + 1;173mosaic.priority = priority;174mosaic.palette = palette ? palette_index + palette : 0;175mosaic.tile = tile;176}177if(screen == Screen::Main) x++;178if(mosaic.palette == 0) return;179180if(hires == false || screen == Screen::Main) if(regs.main_enable) output.main = mosaic;181if(hires == false || screen == Screen::Sub ) if(regs.sub_enable ) output.sub = mosaic;182}183184unsigned PPU::Background::get_tile_color() {185unsigned color = 0;186187switch(regs.mode) {188case Mode::BPP8:189color += (data[7] >> 0) & 0x80; data[7] <<= 1;190color += (data[6] >> 1) & 0x40; data[6] <<= 1;191color += (data[5] >> 2) & 0x20; data[5] <<= 1;192color += (data[4] >> 3) & 0x10; data[4] <<= 1;193case Mode::BPP4:194color += (data[3] >> 4) & 0x08; data[3] <<= 1;195color += (data[2] >> 5) & 0x04; data[2] <<= 1;196case Mode::BPP2:197color += (data[1] >> 6) & 0x02; data[1] <<= 1;198color += (data[0] >> 7) & 0x01; data[0] <<= 1;199}200201return color;202}203204void PPU::Background::reset() {205regs.tiledata_addr = (random(0x0000) & 0x07) << 13;206regs.screen_addr = (random(0x0000) & 0x7c) << 9;207regs.screen_size = random(0);208regs.mosaic = random(0);209regs.tile_size = random(0);210regs.mode = 0;211regs.priority0 = 0;212regs.priority1 = 0;213regs.main_enable = random(0);214regs.sub_enable = random(0);215regs.hoffset = random(0x0000);216regs.voffset = random(0x0000);217218cache.hoffset = 0;219cache.voffset = 0;220221output.main.palette = 0;222output.main.priority = 0;223output.sub.palette = 0;224output.sub.priority = 0;225226mosaic.priority = 0;227mosaic.palette = 0;228mosaic.tile = 0;229230mosaic.vcounter = 0;231mosaic.voffset = 0;232mosaic.hcounter = 0;233mosaic.hoffset = 0;234235x = 0;236y = 0;237238tile_counter = 0;239tile = 0;240priority = 0;241palette_number = 0;242palette_index = 0;243for(unsigned n = 0; n < 8; n++) data[n] = 0;244}245246unsigned PPU::Background::get_tile(unsigned x, unsigned y) {247bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);248unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4);249unsigned tile_width = (!hires ? tile_height : 4);250unsigned width = (!hires ? 256 : 512);251unsigned mask_x = (tile_height == 3 ? width : width << 1);252unsigned mask_y = mask_x;253if(regs.screen_size & 1) mask_x <<= 1;254if(regs.screen_size & 2) mask_y <<= 1;255mask_x--;256mask_y--;257258unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0);259unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0);260if(regs.screen_size == 3) screen_y <<= 1;261262x = (x & mask_x) >> tile_width;263y = (y & mask_y) >> tile_height;264265uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f);266if(x & 0x20) offset += screen_x;267if(y & 0x20) offset += screen_y;268269uint16 addr = regs.screen_addr + (offset << 1);270return (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8);271}272273PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) {274}275276#endif277278279