mirror of https://github.com/bsnes-emu/bsnes.git
Update to v068r08 release.
byuu says: This gets the basic new PPU skeleton up and running, still missing a lot: - Mode7 - direct color mode - OAM color exemption (this one will impact performance negatively) - vertical mosaic - horizontal mosaic (this one may impact performance negatively) - offset per tile - interlace - hires - pseudo-hires But it's correct enough to play most games okay. So far, the new PPU is about 11% faster on my Atom, and 17% faster on my E8400. I was hoping for more, but the window masking and sprite calculation is just kicking my ass. The 11/17 figure is total emulator overhead, so that means raw PPU vs PPU, the new one is at least 22-34% faster than the old one. I don't really have any ideas for additional optimizations. I'm even using little-endian word reads where applicable. But at any rate, I need to get all the above implemented correctly before trying to push optimizations even further.
This commit is contained in:
parent
0bf6c40d1f
commit
f1009ec634
|
@ -47,6 +47,9 @@ void PPU::Background::render() {
|
|||
const bool is_opt_mode = (self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6);
|
||||
const bool is_direct_color_mode = (self.screen.regs.direct_color == true && id == ID::BG1 && (self.regs.bgmode == 3 || self.regs.bgmode == 4));
|
||||
|
||||
window.render(0);
|
||||
window.render(1);
|
||||
|
||||
signed x = 0 - (hscroll & 7);
|
||||
while(x < width) {
|
||||
hoffset = x + hscroll;
|
||||
|
@ -90,9 +93,10 @@ void PPU::Background::render() {
|
|||
|
||||
for(unsigned n = 0; n < 8; n++) {
|
||||
unsigned col = *tiledata++;
|
||||
if(col) {
|
||||
if(col && !(plot_x & 256)) {
|
||||
unsigned color = self.screen.get_palette(pal_index + col);
|
||||
self.screen.output.plot_main(plot_x, color, tile_pri);
|
||||
if(regs.main_enable && !window.main[plot_x]) self.screen.output.plot_main(plot_x, color, tile_pri, id);
|
||||
if(regs.sub_enable && !window.sub[plot_x]) self.screen.output.plot_sub(plot_x, color, tile_pri, id);
|
||||
}
|
||||
plot_x += step;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ class Background {
|
|||
bool sub_enable;
|
||||
} regs;
|
||||
|
||||
LayerWindow window;
|
||||
|
||||
void render();
|
||||
|
||||
const unsigned id;
|
||||
|
|
|
@ -6,9 +6,9 @@ void PPU::latch_counters() {
|
|||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
bool PPU::interlace() const { return false; }
|
||||
bool PPU::overscan() const { return false; }
|
||||
bool PPU::hires() const { return false; }
|
||||
bool PPU::interlace() const { return regs.interlace; }
|
||||
bool PPU::overscan() const { return regs.overscan; }
|
||||
bool PPU::hires() const { return regs.pseudo_hires || (regs.bgmode == 5 || regs.bgmode == 6); }
|
||||
|
||||
uint16 PPU::get_vram_addr() {
|
||||
uint16 addr = regs.vram_addr;
|
||||
|
@ -286,7 +286,6 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
if(regs.display_disable && vcounter() == display.height) oam.address_reset();
|
||||
regs.display_disable = data & 0x80;
|
||||
regs.display_brightness = data & 0x0f;
|
||||
screen.light_table = screen.light_tables[regs.display_brightness];
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -294,6 +293,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
oam.regs.base_size = (data >> 5) & 7;
|
||||
oam.regs.nameselect = (data >> 3) & 3;
|
||||
oam.regs.tiledata_addr = (data & 3) << 14;
|
||||
oam.list_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -534,72 +534,72 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
|
||||
case 0x2123: { //W12SEL
|
||||
window.regs.bg2_two_enable = data & 0x80;
|
||||
window.regs.bg2_two_invert = data & 0x40;
|
||||
window.regs.bg2_one_enable = data & 0x20;
|
||||
window.regs.bg2_one_invert = data & 0x10;
|
||||
window.regs.bg1_two_enable = data & 0x08;
|
||||
window.regs.bg1_two_invert = data & 0x04;
|
||||
window.regs.bg1_one_enable = data & 0x02;
|
||||
window.regs.bg1_one_invert = data & 0x01;
|
||||
bg2.window.two_enable = data & 0x80;
|
||||
bg2.window.two_invert = data & 0x40;
|
||||
bg2.window.one_enable = data & 0x20;
|
||||
bg2.window.one_invert = data & 0x10;
|
||||
bg1.window.two_enable = data & 0x08;
|
||||
bg1.window.two_invert = data & 0x04;
|
||||
bg1.window.one_enable = data & 0x02;
|
||||
bg1.window.one_invert = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2124: { //W34SEL
|
||||
window.regs.bg4_two_enable = data & 0x80;
|
||||
window.regs.bg4_two_invert = data & 0x40;
|
||||
window.regs.bg4_one_enable = data & 0x20;
|
||||
window.regs.bg4_one_invert = data & 0x10;
|
||||
window.regs.bg3_two_enable = data & 0x08;
|
||||
window.regs.bg3_two_invert = data & 0x04;
|
||||
window.regs.bg3_one_enable = data & 0x02;
|
||||
window.regs.bg3_one_invert = data & 0x01;
|
||||
bg4.window.two_enable = data & 0x80;
|
||||
bg4.window.two_invert = data & 0x40;
|
||||
bg4.window.one_enable = data & 0x20;
|
||||
bg4.window.one_invert = data & 0x10;
|
||||
bg3.window.two_enable = data & 0x08;
|
||||
bg3.window.two_invert = data & 0x04;
|
||||
bg3.window.one_enable = data & 0x02;
|
||||
bg3.window.one_invert = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2125: { //WOBJSEL
|
||||
window.regs.col_two_enable = data & 0x80;
|
||||
window.regs.col_two_invert = data & 0x40;
|
||||
window.regs.col_one_enable = data & 0x20;
|
||||
window.regs.col_one_invert = data & 0x10;
|
||||
window.regs.oam_two_enable = data & 0x08;
|
||||
window.regs.oam_two_invert = data & 0x04;
|
||||
window.regs.oam_one_enable = data & 0x02;
|
||||
window.regs.oam_one_invert = data & 0x01;
|
||||
screen.window.two_enable = data & 0x80;
|
||||
screen.window.two_invert = data & 0x40;
|
||||
screen.window.one_enable = data & 0x20;
|
||||
screen.window.one_invert = data & 0x10;
|
||||
oam.window.two_enable = data & 0x08;
|
||||
oam.window.two_invert = data & 0x04;
|
||||
oam.window.one_enable = data & 0x02;
|
||||
oam.window.one_invert = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2126: { //WH0
|
||||
window.regs.one_left = data;
|
||||
regs.window_one_left = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2127: { //WH1
|
||||
window.regs.one_right = data;
|
||||
regs.window_one_right = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2128: { //WH2
|
||||
window.regs.two_left = data;
|
||||
regs.window_two_left = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2129: { //WH3
|
||||
window.regs.two_right = data;
|
||||
regs.window_two_right = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212a: { //WBGLOG
|
||||
window.regs.bg4_mask = (data >> 6) & 3;
|
||||
window.regs.bg3_mask = (data >> 4) & 3;
|
||||
window.regs.bg2_mask = (data >> 2) & 3;
|
||||
window.regs.bg1_mask = (data >> 0) & 3;
|
||||
bg4.window.mask = (data >> 6) & 3;
|
||||
bg3.window.mask = (data >> 4) & 3;
|
||||
bg2.window.mask = (data >> 2) & 3;
|
||||
bg1.window.mask = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212b: { //WOBJLOG
|
||||
window.regs.col_mask = (data >> 2) & 3;
|
||||
window.regs.oam_mask = (data >> 0) & 3;
|
||||
screen.window.mask = (data >> 2) & 3;
|
||||
oam.window.mask = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -622,26 +622,26 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
|
||||
case 0x212e: { //TMW
|
||||
window.regs.oam_main_enable = data & 0x10;
|
||||
window.regs.bg4_main_enable = data & 0x08;
|
||||
window.regs.bg3_main_enable = data & 0x04;
|
||||
window.regs.bg2_main_enable = data & 0x02;
|
||||
window.regs.bg1_main_enable = data & 0x01;
|
||||
oam.window.main_enable = data & 0x10;
|
||||
bg4.window.main_enable = data & 0x08;
|
||||
bg3.window.main_enable = data & 0x04;
|
||||
bg2.window.main_enable = data & 0x02;
|
||||
bg1.window.main_enable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212f: { //TSW
|
||||
window.regs.oam_sub_enable = data & 0x10;
|
||||
window.regs.bg4_sub_enable = data & 0x08;
|
||||
window.regs.bg3_sub_enable = data & 0x04;
|
||||
window.regs.bg2_sub_enable = data & 0x02;
|
||||
window.regs.bg1_sub_enable = data & 0x01;
|
||||
oam.window.sub_enable = data & 0x10;
|
||||
bg4.window.sub_enable = data & 0x08;
|
||||
bg3.window.sub_enable = data & 0x04;
|
||||
bg2.window.sub_enable = data & 0x02;
|
||||
bg1.window.sub_enable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2130: { //CGWSEL
|
||||
window.regs.col_main_mask = (data >> 6) & 3;
|
||||
window.regs.col_sub_mask = (data >> 4) & 3;
|
||||
screen.window.main_mask = (data >> 6) & 3;
|
||||
screen.window.sub_mask = (data >> 4) & 3;
|
||||
screen.regs.addsub_mode = data & 0x02;
|
||||
screen.regs.direct_color = data & 0x01;
|
||||
return;
|
||||
|
@ -650,12 +650,12 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
case 0x2131: { //CGADDSUB
|
||||
screen.regs.color_mode = data & 0x80;
|
||||
screen.regs.color_halve = data & 0x40;
|
||||
screen.regs.back_color_enable = data & 0x20;
|
||||
screen.regs.oam_color_enable = data & 0x10;
|
||||
screen.regs.bg4_color_enable = data & 0x08;
|
||||
screen.regs.bg3_color_enable = data & 0x04;
|
||||
screen.regs.bg2_color_enable = data & 0x02;
|
||||
screen.regs.bg1_color_enable = data & 0x01;
|
||||
screen.regs.color_enable[5] = data & 0x20;
|
||||
screen.regs.color_enable[4] = data & 0x10;
|
||||
screen.regs.color_enable[3] = data & 0x08;
|
||||
screen.regs.color_enable[2] = data & 0x04;
|
||||
screen.regs.color_enable[1] = data & 0x02;
|
||||
screen.regs.color_enable[0] = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -663,6 +663,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
if(data & 0x80) screen.regs.color_b = data & 0x1f;
|
||||
if(data & 0x40) screen.regs.color_g = data & 0x1f;
|
||||
if(data & 0x20) screen.regs.color_r = data & 0x1f;
|
||||
screen.regs.color = (screen.regs.color_b << 10) | (screen.regs.color_g << 5) | (screen.regs.color_r << 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -673,6 +674,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
oam.regs.interlace = data & 0x02;
|
||||
regs.interlace = data & 0x01;
|
||||
mmio_update_video_mode();
|
||||
oam.list_valid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -694,11 +696,11 @@ void PPU::mmio_reset() {
|
|||
regs.latch_vcounter = 0;
|
||||
|
||||
oam.regs.first_sprite = 0;
|
||||
oam.list_valid = false;
|
||||
|
||||
//$2100
|
||||
regs.display_disable = true;
|
||||
regs.display_brightness = 0;
|
||||
screen.light_table = screen.light_tables[regs.display_brightness];
|
||||
|
||||
//$2101
|
||||
oam.regs.base_size = 0;
|
||||
|
@ -777,49 +779,49 @@ void PPU::mmio_reset() {
|
|||
regs.cgram_addr = 0;
|
||||
|
||||
//$2123-$2125
|
||||
window.regs.bg1_one_enable = 0;
|
||||
window.regs.bg1_one_invert = 0;
|
||||
window.regs.bg1_two_enable = 0;
|
||||
window.regs.bg1_two_invert = 0;
|
||||
bg1.window.one_enable = 0;
|
||||
bg1.window.one_invert = 0;
|
||||
bg1.window.two_enable = 0;
|
||||
bg1.window.two_invert = 0;
|
||||
|
||||
window.regs.bg2_one_enable = 0;
|
||||
window.regs.bg2_one_invert = 0;
|
||||
window.regs.bg2_two_enable = 0;
|
||||
window.regs.bg2_two_invert = 0;
|
||||
bg2.window.one_enable = 0;
|
||||
bg2.window.one_invert = 0;
|
||||
bg2.window.two_enable = 0;
|
||||
bg2.window.two_invert = 0;
|
||||
|
||||
window.regs.bg3_one_enable = 0;
|
||||
window.regs.bg3_one_invert = 0;
|
||||
window.regs.bg3_two_enable = 0;
|
||||
window.regs.bg3_two_invert = 0;
|
||||
bg3.window.one_enable = 0;
|
||||
bg3.window.one_invert = 0;
|
||||
bg3.window.two_enable = 0;
|
||||
bg3.window.two_invert = 0;
|
||||
|
||||
window.regs.bg4_one_enable = 0;
|
||||
window.regs.bg4_one_invert = 0;
|
||||
window.regs.bg4_two_enable = 0;
|
||||
window.regs.bg4_two_invert = 0;
|
||||
bg4.window.one_enable = 0;
|
||||
bg4.window.one_invert = 0;
|
||||
bg4.window.two_enable = 0;
|
||||
bg4.window.two_invert = 0;
|
||||
|
||||
window.regs.oam_one_enable = 0;
|
||||
window.regs.oam_one_invert = 0;
|
||||
window.regs.oam_two_enable = 0;
|
||||
window.regs.oam_two_invert = 0;
|
||||
oam.window.one_enable = 0;
|
||||
oam.window.one_invert = 0;
|
||||
oam.window.two_enable = 0;
|
||||
oam.window.two_invert = 0;
|
||||
|
||||
window.regs.col_one_enable = 0;
|
||||
window.regs.col_one_invert = 0;
|
||||
window.regs.col_two_enable = 0;
|
||||
window.regs.col_two_invert = 0;
|
||||
screen.window.one_enable = 0;
|
||||
screen.window.one_invert = 0;
|
||||
screen.window.two_enable = 0;
|
||||
screen.window.two_invert = 0;
|
||||
|
||||
//$2126-$2129
|
||||
window.regs.one_left = 0;
|
||||
window.regs.one_right = 0;
|
||||
window.regs.two_left = 0;
|
||||
window.regs.two_right = 0;
|
||||
regs.window_one_left = 0;
|
||||
regs.window_one_right = 0;
|
||||
regs.window_two_left = 0;
|
||||
regs.window_two_right = 0;
|
||||
|
||||
//$212a-$212b
|
||||
window.regs.bg1_mask = 0;
|
||||
window.regs.bg2_mask = 0;
|
||||
window.regs.bg3_mask = 0;
|
||||
window.regs.bg4_mask = 0;
|
||||
window.regs.oam_mask = 0;
|
||||
window.regs.col_mask = 0;
|
||||
bg1.window.mask = 0;
|
||||
bg2.window.mask = 0;
|
||||
bg3.window.mask = 0;
|
||||
bg4.window.mask = 0;
|
||||
oam.window.mask = 0;
|
||||
screen.window.mask = 0;
|
||||
|
||||
//$212c
|
||||
bg1.regs.main_enable = 0;
|
||||
|
@ -836,39 +838,40 @@ void PPU::mmio_reset() {
|
|||
oam.regs.sub_enable = 0;
|
||||
|
||||
//$212e
|
||||
window.regs.bg1_main_enable = 0;
|
||||
window.regs.bg2_main_enable = 0;
|
||||
window.regs.bg3_main_enable = 0;
|
||||
window.regs.bg4_main_enable = 0;
|
||||
window.regs.oam_main_enable = 0;
|
||||
bg1.window.main_enable = 0;
|
||||
bg2.window.main_enable = 0;
|
||||
bg3.window.main_enable = 0;
|
||||
bg4.window.main_enable = 0;
|
||||
oam.window.main_enable = 0;
|
||||
|
||||
//$212f
|
||||
window.regs.bg1_sub_enable = 0;
|
||||
window.regs.bg2_sub_enable = 0;
|
||||
window.regs.bg3_sub_enable = 0;
|
||||
window.regs.bg4_sub_enable = 0;
|
||||
window.regs.oam_sub_enable = 0;
|
||||
bg1.window.sub_enable = 0;
|
||||
bg2.window.sub_enable = 0;
|
||||
bg3.window.sub_enable = 0;
|
||||
bg4.window.sub_enable = 0;
|
||||
oam.window.sub_enable = 0;
|
||||
|
||||
//$2130
|
||||
window.regs.col_main_mask = 0;
|
||||
window.regs.col_sub_mask = 0;
|
||||
screen.window.main_mask = 0;
|
||||
screen.window.sub_mask = 0;
|
||||
screen.regs.addsub_mode = 0;
|
||||
screen.regs.direct_color = 0;
|
||||
|
||||
//$2131
|
||||
screen.regs.color_mode = 0;
|
||||
screen.regs.color_halve = 0;
|
||||
screen.regs.back_color_enable = 0;
|
||||
screen.regs.oam_color_enable = 0;
|
||||
screen.regs.bg4_color_enable = 0;
|
||||
screen.regs.bg3_color_enable = 0;
|
||||
screen.regs.bg2_color_enable = 0;
|
||||
screen.regs.bg1_color_enable = 0;
|
||||
screen.regs.color_enable[5] = 0;
|
||||
screen.regs.color_enable[4] = 0;
|
||||
screen.regs.color_enable[3] = 0;
|
||||
screen.regs.color_enable[2] = 0;
|
||||
screen.regs.color_enable[1] = 0;
|
||||
screen.regs.color_enable[0] = 0;
|
||||
|
||||
//$2132
|
||||
screen.regs.color_b = 0;
|
||||
screen.regs.color_g = 0;
|
||||
screen.regs.color_r = 0;
|
||||
screen.regs.color = 0;
|
||||
|
||||
//$2133
|
||||
regs.mode7_extbg = 0;
|
||||
|
|
|
@ -56,6 +56,12 @@ struct Regs {
|
|||
//$2121
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2126-$212a
|
||||
unsigned window_one_left;
|
||||
unsigned window_one_right;
|
||||
unsigned window_two_left;
|
||||
unsigned window_two_right;
|
||||
|
||||
//$2133
|
||||
bool mode7_extbg;
|
||||
bool pseudo_hires;
|
||||
|
|
|
@ -6,10 +6,10 @@ namespace SNES {
|
|||
PPU ppu;
|
||||
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "window/window.cpp"
|
||||
#include "cache/cache.cpp"
|
||||
#include "background/background.cpp"
|
||||
#include "sprite/sprite.cpp"
|
||||
#include "window/window.cpp"
|
||||
#include "screen/screen.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
|
@ -34,7 +34,7 @@ void PPU::enter() {
|
|||
}
|
||||
|
||||
scanline();
|
||||
if(vcounter() < display.height) {
|
||||
if(vcounter() < display.height && vcounter()) {
|
||||
add_clocks(512);
|
||||
render_scanline();
|
||||
add_clocks(lineclocks() - 512);
|
||||
|
@ -62,12 +62,14 @@ void PPU::render_scanline() {
|
|||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
if(vcounter() == 0) frame();
|
||||
display.width = !hires() ? 256 : 512;
|
||||
display.height = !overscan() ? 225 : 240;
|
||||
if(vcounter() == 0) frame();
|
||||
if(vcounter() == display.height && regs.display_disable == false) oam.address_reset();
|
||||
}
|
||||
|
||||
void PPU::frame() {
|
||||
oam.frame();
|
||||
system.frame();
|
||||
}
|
||||
|
||||
|
@ -75,7 +77,6 @@ void PPU::power() {
|
|||
foreach(n, memory::vram) n = 0;
|
||||
foreach(n, memory::oam) n = 0;
|
||||
foreach(n, memory::cgram) n = 0;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -83,7 +84,6 @@ void PPU::reset() {
|
|||
create(Enter, system.cpu_frequency());
|
||||
PPUcounter::reset();
|
||||
memset(surface, 0, 512 * 512 * sizeof(uint16));
|
||||
|
||||
mmio_reset();
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,6 @@ bg2(*this, Background::ID::BG2),
|
|||
bg3(*this, Background::ID::BG3),
|
||||
bg4(*this, Background::ID::BG4),
|
||||
oam(*this),
|
||||
window(*this),
|
||||
screen(*this) {
|
||||
surface = new uint16[512 * 512];
|
||||
output = surface + 16 * 512;
|
||||
|
|
|
@ -24,10 +24,10 @@ private:
|
|||
uint16 *output;
|
||||
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "cache/cache.hpp"
|
||||
#include "background/background.hpp"
|
||||
#include "sprite/sprite.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "screen/screen.hpp"
|
||||
|
||||
Cache cache;
|
||||
|
@ -36,7 +36,6 @@ private:
|
|||
Background bg3;
|
||||
Background bg4;
|
||||
Sprite oam;
|
||||
Window window;
|
||||
Screen screen;
|
||||
|
||||
struct Display {
|
||||
|
|
|
@ -1,17 +1,50 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
unsigned PPU::Screen::get_palette(unsigned color) {
|
||||
#if defined(ARCH_LSB)
|
||||
static uint16 *cgram = (uint16*)memory::cgram.data();
|
||||
return cgram[color];
|
||||
#else
|
||||
color <<= 1;
|
||||
return (memory::cgram[color + 0] << 0) + (memory::cgram[color + 1] << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16 PPU::Screen::addsub(unsigned x, unsigned y, bool halve) {
|
||||
if(!regs.color_mode) {
|
||||
if(!halve) {
|
||||
unsigned sum = x + y;
|
||||
unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
|
||||
return (sum - carry) | (carry - (carry >> 5));
|
||||
} else {
|
||||
return (x + y - ((x ^ y) & 0x0421)) >> 1;
|
||||
}
|
||||
} else {
|
||||
unsigned diff = x - y + 0x8420;
|
||||
unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
|
||||
if(!halve) {
|
||||
return (diff - borrow) & (borrow - (borrow >> 5));
|
||||
} else {
|
||||
return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Screen::scanline() {
|
||||
unsigned color = get_palette(0);
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output.main[x].color = color;
|
||||
output.main[x].priority = 0;
|
||||
output.main[x].color = 0;
|
||||
output.main[x].source = 5;
|
||||
|
||||
output.sub[x].color = regs.color;
|
||||
output.sub[x].priority = 0;
|
||||
output.sub[x].color = 0;
|
||||
output.sub[x].source = 5;
|
||||
}
|
||||
|
||||
window.render(0);
|
||||
window.render(1);
|
||||
}
|
||||
|
||||
void PPU::Screen::render_black() {
|
||||
|
@ -19,17 +52,45 @@ void PPU::Screen::render_black() {
|
|||
memset(data, 0, self.display.width << 1);
|
||||
}
|
||||
|
||||
uint16 PPU::Screen::get_pixel_main(unsigned x) {
|
||||
auto &main = output.main[x];
|
||||
auto &sub = output.sub[x];
|
||||
|
||||
if(!regs.addsub_mode) {
|
||||
sub.source = 5;
|
||||
sub.color = regs.color;
|
||||
}
|
||||
|
||||
if(!window.main[x]) {
|
||||
if(!window.sub[x]) {
|
||||
return 0x0000;
|
||||
}
|
||||
main.color = 0x0000;
|
||||
}
|
||||
|
||||
if(regs.color_enable[main.source] && window.sub[x]) {
|
||||
bool halve = false;
|
||||
if(regs.color_halve && window.main[x]) {
|
||||
if(!regs.addsub_mode || sub.source != 5) halve = true;
|
||||
}
|
||||
return addsub(main.color, sub.color, halve);
|
||||
}
|
||||
|
||||
return main.color;
|
||||
}
|
||||
|
||||
void PPU::Screen::render() {
|
||||
uint16 *data = self.output + self.vcounter() * 1024;
|
||||
uint16 *light = light_table[self.regs.display_brightness];
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
data[i] = light_table[output.main[i].color];
|
||||
data[i] = light[get_pixel_main(i)];
|
||||
}
|
||||
}
|
||||
|
||||
PPU::Screen::Screen(PPU &self) : self(self) {
|
||||
light_tables = new uint16*[16];
|
||||
light_table = new uint16*[16];
|
||||
for(unsigned l = 0; l < 16; l++) {
|
||||
light_tables[l] = new uint16[32768];
|
||||
light_table[l] = new uint16[32768];
|
||||
for(unsigned r = 0; r < 32; r++) {
|
||||
for(unsigned g = 0; g < 32; g++) {
|
||||
for(unsigned b = 0; b < 32; b++) {
|
||||
|
@ -37,7 +98,7 @@ PPU::Screen::Screen(PPU &self) : self(self) {
|
|||
unsigned ar = (luma * r + 0.5);
|
||||
unsigned ag = (luma * g + 0.5);
|
||||
unsigned ab = (luma * b + 0.5);
|
||||
light_tables[l][(r << 10) + (g << 5) + (b << 0)] = (ab << 10) + (ag << 5) + (ar << 0);
|
||||
light_table[l][(r << 10) + (g << 5) + (b << 0)] = (ab << 10) + (ag << 5) + (ar << 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,23 +106,23 @@ PPU::Screen::Screen(PPU &self) : self(self) {
|
|||
}
|
||||
|
||||
PPU::Screen::~Screen() {
|
||||
for(unsigned l = 0; l < 16; l++) delete[] light_tables[l];
|
||||
delete[] light_tables;
|
||||
for(unsigned l = 0; l < 16; l++) delete[] light_table[l];
|
||||
delete[] light_table;
|
||||
}
|
||||
|
||||
void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority) {
|
||||
if(x & 256) return;
|
||||
void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority, unsigned source) {
|
||||
if(priority > main[x].priority) {
|
||||
main[x].priority = priority;
|
||||
main[x].color = color;
|
||||
main[x].priority = priority;
|
||||
main[x].source = source;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority) {
|
||||
if(x & 256) return;
|
||||
void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source) {
|
||||
if(priority > sub[x].priority) {
|
||||
sub[x].priority = priority;
|
||||
sub[x].color = color;
|
||||
sub[x].priority = priority;
|
||||
sub[x].source = source;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,35 +5,33 @@ class Screen {
|
|||
|
||||
bool color_mode;
|
||||
bool color_halve;
|
||||
bool back_color_enable;
|
||||
bool oam_color_enable;
|
||||
bool bg4_color_enable;
|
||||
bool bg3_color_enable;
|
||||
bool bg2_color_enable;
|
||||
bool bg1_color_enable;
|
||||
bool color_enable[6];
|
||||
|
||||
unsigned color_b;
|
||||
unsigned color_g;
|
||||
unsigned color_r;
|
||||
unsigned color;
|
||||
} regs;
|
||||
|
||||
struct Output {
|
||||
struct {
|
||||
struct Pixel {
|
||||
unsigned color;
|
||||
unsigned priority;
|
||||
unsigned source;
|
||||
} main[256], sub[256];
|
||||
|
||||
void plot_main(unsigned x, unsigned color, unsigned priority);
|
||||
void plot_sub(unsigned x, unsigned color, unsigned priority);
|
||||
void plot_main(unsigned x, unsigned color, unsigned priority, unsigned source);
|
||||
void plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source);
|
||||
} output;
|
||||
|
||||
uint16 **light_tables;
|
||||
uint16 *light_table;
|
||||
ColorWindow window;
|
||||
uint16 **light_table;
|
||||
|
||||
unsigned get_palette(unsigned color);
|
||||
|
||||
uint16 addsub(unsigned x, unsigned y, bool halve);
|
||||
void scanline();
|
||||
void render_black();
|
||||
uint16 get_pixel_main(unsigned x);
|
||||
void render();
|
||||
Screen(PPU &self);
|
||||
~Screen();
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::Sprite::frame() {
|
||||
regs.time_over = false;
|
||||
regs.range_over = false;
|
||||
}
|
||||
|
||||
void PPU::Sprite::update_list(unsigned addr, uint8 data) {
|
||||
if(addr < 0x0200) {
|
||||
unsigned i = addr >> 2;
|
||||
|
@ -18,12 +23,13 @@ void PPU::Sprite::update_list(unsigned addr, uint8 data) {
|
|||
unsigned i = (addr & 0x1f) << 2;
|
||||
list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff);
|
||||
list[i + 0].size = data & 0x02;
|
||||
list[i + 1].x = ((data & 0x04) << 8) | (list[i + 1].x & 0xff);
|
||||
list[i + 1].x = ((data & 0x04) << 6) | (list[i + 1].x & 0xff);
|
||||
list[i + 1].size = data & 0x08;
|
||||
list[i + 2].x = ((data & 0x10) << 8) | (list[i + 2].x & 0xff);
|
||||
list[i + 2].x = ((data & 0x10) << 4) | (list[i + 2].x & 0xff);
|
||||
list[i + 2].size = data & 0x20;
|
||||
list[i + 3].x = ((data & 0x40) << 8) | (list[i + 3].x & 0xff);
|
||||
list[i + 3].x = ((data & 0x40) << 2) | (list[i + 3].x & 0xff);
|
||||
list[i + 3].size = data & 0x80;
|
||||
list_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,18 +52,21 @@ bool PPU::Sprite::on_scanline(unsigned sprite) {
|
|||
}
|
||||
|
||||
void PPU::Sprite::render() {
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
if(list[i].size == 0) {
|
||||
static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 };
|
||||
static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
} else {
|
||||
static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 };
|
||||
static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
if(regs.interlace && regs.base_size >= 6) list[i].height = 16;
|
||||
if(list_valid == false) {
|
||||
list_valid = true;
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
if(list[i].size == 0) {
|
||||
static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 };
|
||||
static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
} else {
|
||||
static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 };
|
||||
static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
if(regs.interlace && regs.base_size >= 6) list[i].height = 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,8 +107,8 @@ void PPU::Sprite::render() {
|
|||
y &= 255;
|
||||
|
||||
uint16 tdaddr = regs.tiledata_addr;
|
||||
uint16 chrx = (s.character >> 0) & 15;
|
||||
uint16 chry = (s.character >> 4) & 15;
|
||||
uint16 chrx = (s.character >> 0) & 15;
|
||||
uint16 chry = (s.character >> 4) & 15;
|
||||
if(s.use_nameselect) {
|
||||
tdaddr += (256 * 32) + (regs.nameselect << 13);
|
||||
}
|
||||
|
@ -125,8 +134,43 @@ void PPU::Sprite::render() {
|
|||
}
|
||||
}
|
||||
|
||||
regs.time_over |= (tilecount > 34);
|
||||
regs.time_over |= (tilecount > 34);
|
||||
regs.range_over |= (itemcount > 32);
|
||||
|
||||
if(regs.main_enable == false && regs.sub_enable == false) return;
|
||||
|
||||
for(unsigned i = 0; i < 34; i++) {
|
||||
if(tilelist[i].tile == 0xffff) continue;
|
||||
|
||||
auto &t = tilelist[i];
|
||||
uint8 *tiledata = self.cache.tile_4bpp(t.tile);
|
||||
tiledata += (t.y & 7) << 3;
|
||||
unsigned sx = t.x;
|
||||
for(unsigned x = 0; x < 8; x++) {
|
||||
sx &= 511;
|
||||
if(sx < 256) {
|
||||
unsigned color = *(tiledata + (t.hflip == false ? x : 7 - x));
|
||||
if(color) {
|
||||
color += t.palette;
|
||||
output.palette[sx] = color;
|
||||
output.priority[sx] = t.priority;
|
||||
}
|
||||
}
|
||||
sx++;
|
||||
}
|
||||
}
|
||||
|
||||
window.render(0);
|
||||
window.render(1);
|
||||
|
||||
const unsigned priority_table[] = { regs.priority0, regs.priority1, regs.priority2, regs.priority3 };
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
if(output.priority[x] == 0xff) continue;
|
||||
unsigned priority = priority_table[output.priority[x]];
|
||||
unsigned color = self.screen.get_palette(output.palette[x]);
|
||||
if(regs.main_enable && !window.main[x]) self.screen.output.plot_main(x, color, priority, 4);
|
||||
if(regs.sub_enable && !window.sub[x]) self.screen.output.plot_sub(x, color, priority, 4);
|
||||
}
|
||||
}
|
||||
|
||||
PPU::Sprite::Sprite(PPU &self) : self(self) {
|
||||
|
|
|
@ -32,6 +32,7 @@ class Sprite {
|
|||
unsigned priority;
|
||||
bool size;
|
||||
} list[128];
|
||||
bool list_valid;
|
||||
|
||||
uint8 itemlist[32];
|
||||
struct TileList {
|
||||
|
@ -48,6 +49,9 @@ class Sprite {
|
|||
uint8 priority[256];
|
||||
} output;
|
||||
|
||||
LayerWindow window;
|
||||
|
||||
void frame();
|
||||
void update_list(unsigned addr, uint8 data);
|
||||
void address_reset();
|
||||
void set_first();
|
||||
|
|
|
@ -1,6 +1,98 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
PPU::Window::Window(PPU &self) : self(self) {
|
||||
void PPU::LayerWindow::render(bool screen) {
|
||||
uint8 *output;
|
||||
if(screen == 0) {
|
||||
output = main;
|
||||
if(main_enable == false) {
|
||||
memset(output, 0, 256);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
output = sub;
|
||||
if(sub_enable == false) {
|
||||
memset(output, 0, 256);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(one_enable == false && two_enable == false) {
|
||||
memset(output, 0, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(one_enable == true && two_enable == false) {
|
||||
bool set = 1 ^ one_invert, clr = !set;
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output[x] = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(one_enable == false && two_enable == true) {
|
||||
bool set = 1 ^ two_invert, clr = !set;
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output[x] = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
bool one_mask = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ^ one_invert;
|
||||
bool two_mask = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ^ two_invert;
|
||||
switch(mask) {
|
||||
case 0: output[x] = one_mask | two_mask == 1; break;
|
||||
case 1: output[x] = one_mask & two_mask == 1; break;
|
||||
case 2: output[x] = one_mask ^ two_mask == 1; break;
|
||||
case 3: output[x] = one_mask ^ two_mask == 0; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void PPU::ColorWindow::render(bool screen) {
|
||||
uint8 *output = (screen == 0 ? main : sub);
|
||||
bool set = 1, clr = 0;
|
||||
|
||||
switch(screen == 0 ? main_mask : sub_mask) {
|
||||
case 0: memset(output, 1, 256); return; //always
|
||||
case 1: set = 1, clr = 0; break; //inside window only
|
||||
case 2: set = 0, clr = 1; break; //outside window only
|
||||
case 3: memset(output, 0, 256); return; //never
|
||||
}
|
||||
|
||||
if(one_enable == false && two_enable == false) {
|
||||
memset(output, clr, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(one_enable == true && two_enable == false) {
|
||||
if(one_invert) { set ^= 1; clr ^= 1; }
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output[x] = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(one_enable == false && two_enable == true) {
|
||||
if(two_invert) { set ^= 1; clr ^= 1; }
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output[x] = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
bool one_mask = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ^ one_invert;
|
||||
bool two_mask = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ^ two_invert;
|
||||
switch(mask) {
|
||||
case 0: output[x] = one_mask | two_mask == 1 ? set : clr; break;
|
||||
case 1: output[x] = one_mask & two_mask == 1 ? set : clr; break;
|
||||
case 2: output[x] = one_mask ^ two_mask == 1 ? set : clr; break;
|
||||
case 3: output[x] = one_mask ^ two_mask == 0 ? set : clr; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,66 +1,35 @@
|
|||
class Window {
|
||||
struct Regs {
|
||||
bool bg1_one_enable;
|
||||
bool bg1_one_invert;
|
||||
bool bg1_two_enable;
|
||||
bool bg1_two_invert;
|
||||
class LayerWindow {
|
||||
public:
|
||||
bool one_enable;
|
||||
bool one_invert;
|
||||
bool two_enable;
|
||||
bool two_invert;
|
||||
|
||||
bool bg2_one_enable;
|
||||
bool bg2_one_invert;
|
||||
bool bg2_two_enable;
|
||||
bool bg2_two_invert;
|
||||
unsigned mask;
|
||||
|
||||
bool bg3_one_enable;
|
||||
bool bg3_one_invert;
|
||||
bool bg3_two_enable;
|
||||
bool bg3_two_invert;
|
||||
bool main_enable;
|
||||
bool sub_enable;
|
||||
|
||||
bool bg4_one_enable;
|
||||
bool bg4_one_invert;
|
||||
bool bg4_two_enable;
|
||||
bool bg4_two_invert;
|
||||
uint8 main[256];
|
||||
uint8 sub[256];
|
||||
|
||||
bool oam_one_enable;
|
||||
bool oam_one_invert;
|
||||
bool oam_two_enable;
|
||||
bool oam_two_invert;
|
||||
|
||||
bool col_one_enable;
|
||||
bool col_one_invert;
|
||||
bool col_two_enable;
|
||||
bool col_two_invert;
|
||||
|
||||
unsigned one_left;
|
||||
unsigned one_right;
|
||||
|
||||
unsigned two_left;
|
||||
unsigned two_right;
|
||||
|
||||
unsigned bg1_mask;
|
||||
unsigned bg2_mask;
|
||||
unsigned bg3_mask;
|
||||
unsigned bg4_mask;
|
||||
unsigned oam_mask;
|
||||
unsigned col_mask;
|
||||
|
||||
bool bg1_main_enable;
|
||||
bool bg2_main_enable;
|
||||
bool bg3_main_enable;
|
||||
bool bg4_main_enable;
|
||||
bool oam_main_enable;
|
||||
|
||||
bool bg1_sub_enable;
|
||||
bool bg2_sub_enable;
|
||||
bool bg3_sub_enable;
|
||||
bool bg4_sub_enable;
|
||||
bool oam_sub_enable;
|
||||
|
||||
unsigned col_main_mask;
|
||||
unsigned col_sub_mask;
|
||||
} regs;
|
||||
|
||||
Window(PPU &self);
|
||||
|
||||
PPU &self;
|
||||
friend class PPU;
|
||||
void render(bool screen);
|
||||
};
|
||||
|
||||
class ColorWindow {
|
||||
public:
|
||||
bool one_enable;
|
||||
bool one_invert;
|
||||
bool two_enable;
|
||||
bool two_invert;
|
||||
|
||||
unsigned mask;
|
||||
|
||||
unsigned main_mask;
|
||||
unsigned sub_mask;
|
||||
|
||||
uint8 main[256];
|
||||
uint8 sub[256];
|
||||
|
||||
void render(bool screen);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "068.06";
|
||||
static const char Version[] = "068.08";
|
||||
static const unsigned SerializerVersion = 13;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue