mirror of https://github.com/bsnes-emu/bsnes.git
250 lines
7.2 KiB
C++
250 lines
7.2 KiB
C++
|
#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
|