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:
Tim Allen 2010-09-01 23:22:05 +10:00
parent 0bf6c40d1f
commit f1009ec634
13 changed files with 407 additions and 226 deletions

View File

@ -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_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)); 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); signed x = 0 - (hscroll & 7);
while(x < width) { while(x < width) {
hoffset = x + hscroll; hoffset = x + hscroll;
@ -90,9 +93,10 @@ void PPU::Background::render() {
for(unsigned n = 0; n < 8; n++) { for(unsigned n = 0; n < 8; n++) {
unsigned col = *tiledata++; unsigned col = *tiledata++;
if(col) { if(col && !(plot_x & 256)) {
unsigned color = self.screen.get_palette(pal_index + col); 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; plot_x += step;
} }

View File

@ -23,6 +23,8 @@ class Background {
bool sub_enable; bool sub_enable;
} regs; } regs;
LayerWindow window;
void render(); void render();
const unsigned id; const unsigned id;

View File

@ -6,9 +6,9 @@ void PPU::latch_counters() {
regs.counters_latched = true; regs.counters_latched = true;
} }
bool PPU::interlace() const { return false; } bool PPU::interlace() const { return regs.interlace; }
bool PPU::overscan() const { return false; } bool PPU::overscan() const { return regs.overscan; }
bool PPU::hires() const { return false; } bool PPU::hires() const { return regs.pseudo_hires || (regs.bgmode == 5 || regs.bgmode == 6); }
uint16 PPU::get_vram_addr() { uint16 PPU::get_vram_addr() {
uint16 addr = regs.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(); if(regs.display_disable && vcounter() == display.height) oam.address_reset();
regs.display_disable = data & 0x80; regs.display_disable = data & 0x80;
regs.display_brightness = data & 0x0f; regs.display_brightness = data & 0x0f;
screen.light_table = screen.light_tables[regs.display_brightness];
return; return;
} }
@ -294,6 +293,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
oam.regs.base_size = (data >> 5) & 7; oam.regs.base_size = (data >> 5) & 7;
oam.regs.nameselect = (data >> 3) & 3; oam.regs.nameselect = (data >> 3) & 3;
oam.regs.tiledata_addr = (data & 3) << 14; oam.regs.tiledata_addr = (data & 3) << 14;
oam.list_valid = false;
return; return;
} }
@ -534,72 +534,72 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
} }
case 0x2123: { //W12SEL case 0x2123: { //W12SEL
window.regs.bg2_two_enable = data & 0x80; bg2.window.two_enable = data & 0x80;
window.regs.bg2_two_invert = data & 0x40; bg2.window.two_invert = data & 0x40;
window.regs.bg2_one_enable = data & 0x20; bg2.window.one_enable = data & 0x20;
window.regs.bg2_one_invert = data & 0x10; bg2.window.one_invert = data & 0x10;
window.regs.bg1_two_enable = data & 0x08; bg1.window.two_enable = data & 0x08;
window.regs.bg1_two_invert = data & 0x04; bg1.window.two_invert = data & 0x04;
window.regs.bg1_one_enable = data & 0x02; bg1.window.one_enable = data & 0x02;
window.regs.bg1_one_invert = data & 0x01; bg1.window.one_invert = data & 0x01;
return; return;
} }
case 0x2124: { //W34SEL case 0x2124: { //W34SEL
window.regs.bg4_two_enable = data & 0x80; bg4.window.two_enable = data & 0x80;
window.regs.bg4_two_invert = data & 0x40; bg4.window.two_invert = data & 0x40;
window.regs.bg4_one_enable = data & 0x20; bg4.window.one_enable = data & 0x20;
window.regs.bg4_one_invert = data & 0x10; bg4.window.one_invert = data & 0x10;
window.regs.bg3_two_enable = data & 0x08; bg3.window.two_enable = data & 0x08;
window.regs.bg3_two_invert = data & 0x04; bg3.window.two_invert = data & 0x04;
window.regs.bg3_one_enable = data & 0x02; bg3.window.one_enable = data & 0x02;
window.regs.bg3_one_invert = data & 0x01; bg3.window.one_invert = data & 0x01;
return; return;
} }
case 0x2125: { //WOBJSEL case 0x2125: { //WOBJSEL
window.regs.col_two_enable = data & 0x80; screen.window.two_enable = data & 0x80;
window.regs.col_two_invert = data & 0x40; screen.window.two_invert = data & 0x40;
window.regs.col_one_enable = data & 0x20; screen.window.one_enable = data & 0x20;
window.regs.col_one_invert = data & 0x10; screen.window.one_invert = data & 0x10;
window.regs.oam_two_enable = data & 0x08; oam.window.two_enable = data & 0x08;
window.regs.oam_two_invert = data & 0x04; oam.window.two_invert = data & 0x04;
window.regs.oam_one_enable = data & 0x02; oam.window.one_enable = data & 0x02;
window.regs.oam_one_invert = data & 0x01; oam.window.one_invert = data & 0x01;
return; return;
} }
case 0x2126: { //WH0 case 0x2126: { //WH0
window.regs.one_left = data; regs.window_one_left = data;
return; return;
} }
case 0x2127: { //WH1 case 0x2127: { //WH1
window.regs.one_right = data; regs.window_one_right = data;
return; return;
} }
case 0x2128: { //WH2 case 0x2128: { //WH2
window.regs.two_left = data; regs.window_two_left = data;
return; return;
} }
case 0x2129: { //WH3 case 0x2129: { //WH3
window.regs.two_right = data; regs.window_two_right = data;
return; return;
} }
case 0x212a: { //WBGLOG case 0x212a: { //WBGLOG
window.regs.bg4_mask = (data >> 6) & 3; bg4.window.mask = (data >> 6) & 3;
window.regs.bg3_mask = (data >> 4) & 3; bg3.window.mask = (data >> 4) & 3;
window.regs.bg2_mask = (data >> 2) & 3; bg2.window.mask = (data >> 2) & 3;
window.regs.bg1_mask = (data >> 0) & 3; bg1.window.mask = (data >> 0) & 3;
return; return;
} }
case 0x212b: { //WOBJLOG case 0x212b: { //WOBJLOG
window.regs.col_mask = (data >> 2) & 3; screen.window.mask = (data >> 2) & 3;
window.regs.oam_mask = (data >> 0) & 3; oam.window.mask = (data >> 0) & 3;
return; return;
} }
@ -622,26 +622,26 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
} }
case 0x212e: { //TMW case 0x212e: { //TMW
window.regs.oam_main_enable = data & 0x10; oam.window.main_enable = data & 0x10;
window.regs.bg4_main_enable = data & 0x08; bg4.window.main_enable = data & 0x08;
window.regs.bg3_main_enable = data & 0x04; bg3.window.main_enable = data & 0x04;
window.regs.bg2_main_enable = data & 0x02; bg2.window.main_enable = data & 0x02;
window.regs.bg1_main_enable = data & 0x01; bg1.window.main_enable = data & 0x01;
return; return;
} }
case 0x212f: { //TSW case 0x212f: { //TSW
window.regs.oam_sub_enable = data & 0x10; oam.window.sub_enable = data & 0x10;
window.regs.bg4_sub_enable = data & 0x08; bg4.window.sub_enable = data & 0x08;
window.regs.bg3_sub_enable = data & 0x04; bg3.window.sub_enable = data & 0x04;
window.regs.bg2_sub_enable = data & 0x02; bg2.window.sub_enable = data & 0x02;
window.regs.bg1_sub_enable = data & 0x01; bg1.window.sub_enable = data & 0x01;
return; return;
} }
case 0x2130: { //CGWSEL case 0x2130: { //CGWSEL
window.regs.col_main_mask = (data >> 6) & 3; screen.window.main_mask = (data >> 6) & 3;
window.regs.col_sub_mask = (data >> 4) & 3; screen.window.sub_mask = (data >> 4) & 3;
screen.regs.addsub_mode = data & 0x02; screen.regs.addsub_mode = data & 0x02;
screen.regs.direct_color = data & 0x01; screen.regs.direct_color = data & 0x01;
return; return;
@ -650,12 +650,12 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
case 0x2131: { //CGADDSUB case 0x2131: { //CGADDSUB
screen.regs.color_mode = data & 0x80; screen.regs.color_mode = data & 0x80;
screen.regs.color_halve = data & 0x40; screen.regs.color_halve = data & 0x40;
screen.regs.back_color_enable = data & 0x20; screen.regs.color_enable[5] = data & 0x20;
screen.regs.oam_color_enable = data & 0x10; screen.regs.color_enable[4] = data & 0x10;
screen.regs.bg4_color_enable = data & 0x08; screen.regs.color_enable[3] = data & 0x08;
screen.regs.bg3_color_enable = data & 0x04; screen.regs.color_enable[2] = data & 0x04;
screen.regs.bg2_color_enable = data & 0x02; screen.regs.color_enable[1] = data & 0x02;
screen.regs.bg1_color_enable = data & 0x01; screen.regs.color_enable[0] = data & 0x01;
return; return;
} }
@ -663,6 +663,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
if(data & 0x80) screen.regs.color_b = data & 0x1f; if(data & 0x80) screen.regs.color_b = data & 0x1f;
if(data & 0x40) screen.regs.color_g = data & 0x1f; if(data & 0x40) screen.regs.color_g = data & 0x1f;
if(data & 0x20) screen.regs.color_r = 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; return;
} }
@ -673,6 +674,7 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
oam.regs.interlace = data & 0x02; oam.regs.interlace = data & 0x02;
regs.interlace = data & 0x01; regs.interlace = data & 0x01;
mmio_update_video_mode(); mmio_update_video_mode();
oam.list_valid = false;
return; return;
} }
} }
@ -694,11 +696,11 @@ void PPU::mmio_reset() {
regs.latch_vcounter = 0; regs.latch_vcounter = 0;
oam.regs.first_sprite = 0; oam.regs.first_sprite = 0;
oam.list_valid = false;
//$2100 //$2100
regs.display_disable = true; regs.display_disable = true;
regs.display_brightness = 0; regs.display_brightness = 0;
screen.light_table = screen.light_tables[regs.display_brightness];
//$2101 //$2101
oam.regs.base_size = 0; oam.regs.base_size = 0;
@ -777,49 +779,49 @@ void PPU::mmio_reset() {
regs.cgram_addr = 0; regs.cgram_addr = 0;
//$2123-$2125 //$2123-$2125
window.regs.bg1_one_enable = 0; bg1.window.one_enable = 0;
window.regs.bg1_one_invert = 0; bg1.window.one_invert = 0;
window.regs.bg1_two_enable = 0; bg1.window.two_enable = 0;
window.regs.bg1_two_invert = 0; bg1.window.two_invert = 0;
window.regs.bg2_one_enable = 0; bg2.window.one_enable = 0;
window.regs.bg2_one_invert = 0; bg2.window.one_invert = 0;
window.regs.bg2_two_enable = 0; bg2.window.two_enable = 0;
window.regs.bg2_two_invert = 0; bg2.window.two_invert = 0;
window.regs.bg3_one_enable = 0; bg3.window.one_enable = 0;
window.regs.bg3_one_invert = 0; bg3.window.one_invert = 0;
window.regs.bg3_two_enable = 0; bg3.window.two_enable = 0;
window.regs.bg3_two_invert = 0; bg3.window.two_invert = 0;
window.regs.bg4_one_enable = 0; bg4.window.one_enable = 0;
window.regs.bg4_one_invert = 0; bg4.window.one_invert = 0;
window.regs.bg4_two_enable = 0; bg4.window.two_enable = 0;
window.regs.bg4_two_invert = 0; bg4.window.two_invert = 0;
window.regs.oam_one_enable = 0; oam.window.one_enable = 0;
window.regs.oam_one_invert = 0; oam.window.one_invert = 0;
window.regs.oam_two_enable = 0; oam.window.two_enable = 0;
window.regs.oam_two_invert = 0; oam.window.two_invert = 0;
window.regs.col_one_enable = 0; screen.window.one_enable = 0;
window.regs.col_one_invert = 0; screen.window.one_invert = 0;
window.regs.col_two_enable = 0; screen.window.two_enable = 0;
window.regs.col_two_invert = 0; screen.window.two_invert = 0;
//$2126-$2129 //$2126-$2129
window.regs.one_left = 0; regs.window_one_left = 0;
window.regs.one_right = 0; regs.window_one_right = 0;
window.regs.two_left = 0; regs.window_two_left = 0;
window.regs.two_right = 0; regs.window_two_right = 0;
//$212a-$212b //$212a-$212b
window.regs.bg1_mask = 0; bg1.window.mask = 0;
window.regs.bg2_mask = 0; bg2.window.mask = 0;
window.regs.bg3_mask = 0; bg3.window.mask = 0;
window.regs.bg4_mask = 0; bg4.window.mask = 0;
window.regs.oam_mask = 0; oam.window.mask = 0;
window.regs.col_mask = 0; screen.window.mask = 0;
//$212c //$212c
bg1.regs.main_enable = 0; bg1.regs.main_enable = 0;
@ -836,39 +838,40 @@ void PPU::mmio_reset() {
oam.regs.sub_enable = 0; oam.regs.sub_enable = 0;
//$212e //$212e
window.regs.bg1_main_enable = 0; bg1.window.main_enable = 0;
window.regs.bg2_main_enable = 0; bg2.window.main_enable = 0;
window.regs.bg3_main_enable = 0; bg3.window.main_enable = 0;
window.regs.bg4_main_enable = 0; bg4.window.main_enable = 0;
window.regs.oam_main_enable = 0; oam.window.main_enable = 0;
//$212f //$212f
window.regs.bg1_sub_enable = 0; bg1.window.sub_enable = 0;
window.regs.bg2_sub_enable = 0; bg2.window.sub_enable = 0;
window.regs.bg3_sub_enable = 0; bg3.window.sub_enable = 0;
window.regs.bg4_sub_enable = 0; bg4.window.sub_enable = 0;
window.regs.oam_sub_enable = 0; oam.window.sub_enable = 0;
//$2130 //$2130
window.regs.col_main_mask = 0; screen.window.main_mask = 0;
window.regs.col_sub_mask = 0; screen.window.sub_mask = 0;
screen.regs.addsub_mode = 0; screen.regs.addsub_mode = 0;
screen.regs.direct_color = 0; screen.regs.direct_color = 0;
//$2131 //$2131
screen.regs.color_mode = 0; screen.regs.color_mode = 0;
screen.regs.color_halve = 0; screen.regs.color_halve = 0;
screen.regs.back_color_enable = 0; screen.regs.color_enable[5] = 0;
screen.regs.oam_color_enable = 0; screen.regs.color_enable[4] = 0;
screen.regs.bg4_color_enable = 0; screen.regs.color_enable[3] = 0;
screen.regs.bg3_color_enable = 0; screen.regs.color_enable[2] = 0;
screen.regs.bg2_color_enable = 0; screen.regs.color_enable[1] = 0;
screen.regs.bg1_color_enable = 0; screen.regs.color_enable[0] = 0;
//$2132 //$2132
screen.regs.color_b = 0; screen.regs.color_b = 0;
screen.regs.color_g = 0; screen.regs.color_g = 0;
screen.regs.color_r = 0; screen.regs.color_r = 0;
screen.regs.color = 0;
//$2133 //$2133
regs.mode7_extbg = 0; regs.mode7_extbg = 0;

View File

@ -56,6 +56,12 @@ struct Regs {
//$2121 //$2121
uint16 cgram_addr; uint16 cgram_addr;
//$2126-$212a
unsigned window_one_left;
unsigned window_one_right;
unsigned window_two_left;
unsigned window_two_right;
//$2133 //$2133
bool mode7_extbg; bool mode7_extbg;
bool pseudo_hires; bool pseudo_hires;

View File

@ -6,10 +6,10 @@ namespace SNES {
PPU ppu; PPU ppu;
#include "mmio/mmio.cpp" #include "mmio/mmio.cpp"
#include "window/window.cpp"
#include "cache/cache.cpp" #include "cache/cache.cpp"
#include "background/background.cpp" #include "background/background.cpp"
#include "sprite/sprite.cpp" #include "sprite/sprite.cpp"
#include "window/window.cpp"
#include "screen/screen.cpp" #include "screen/screen.cpp"
#include "serialization.cpp" #include "serialization.cpp"
@ -34,7 +34,7 @@ void PPU::enter() {
} }
scanline(); scanline();
if(vcounter() < display.height) { if(vcounter() < display.height && vcounter()) {
add_clocks(512); add_clocks(512);
render_scanline(); render_scanline();
add_clocks(lineclocks() - 512); add_clocks(lineclocks() - 512);
@ -62,12 +62,14 @@ void PPU::render_scanline() {
} }
void PPU::scanline() { void PPU::scanline() {
if(vcounter() == 0) frame();
display.width = !hires() ? 256 : 512; display.width = !hires() ? 256 : 512;
display.height = !overscan() ? 225 : 240; display.height = !overscan() ? 225 : 240;
if(vcounter() == 0) frame();
if(vcounter() == display.height && regs.display_disable == false) oam.address_reset();
} }
void PPU::frame() { void PPU::frame() {
oam.frame();
system.frame(); system.frame();
} }
@ -75,7 +77,6 @@ void PPU::power() {
foreach(n, memory::vram) n = 0; foreach(n, memory::vram) n = 0;
foreach(n, memory::oam) n = 0; foreach(n, memory::oam) n = 0;
foreach(n, memory::cgram) n = 0; foreach(n, memory::cgram) n = 0;
reset(); reset();
} }
@ -83,7 +84,6 @@ void PPU::reset() {
create(Enter, system.cpu_frequency()); create(Enter, system.cpu_frequency());
PPUcounter::reset(); PPUcounter::reset();
memset(surface, 0, 512 * 512 * sizeof(uint16)); memset(surface, 0, 512 * 512 * sizeof(uint16));
mmio_reset(); mmio_reset();
} }
@ -94,7 +94,6 @@ bg2(*this, Background::ID::BG2),
bg3(*this, Background::ID::BG3), bg3(*this, Background::ID::BG3),
bg4(*this, Background::ID::BG4), bg4(*this, Background::ID::BG4),
oam(*this), oam(*this),
window(*this),
screen(*this) { screen(*this) {
surface = new uint16[512 * 512]; surface = new uint16[512 * 512];
output = surface + 16 * 512; output = surface + 16 * 512;

View File

@ -24,10 +24,10 @@ private:
uint16 *output; uint16 *output;
#include "mmio/mmio.hpp" #include "mmio/mmio.hpp"
#include "window/window.hpp"
#include "cache/cache.hpp" #include "cache/cache.hpp"
#include "background/background.hpp" #include "background/background.hpp"
#include "sprite/sprite.hpp" #include "sprite/sprite.hpp"
#include "window/window.hpp"
#include "screen/screen.hpp" #include "screen/screen.hpp"
Cache cache; Cache cache;
@ -36,7 +36,6 @@ private:
Background bg3; Background bg3;
Background bg4; Background bg4;
Sprite oam; Sprite oam;
Window window;
Screen screen; Screen screen;
struct Display { struct Display {

View File

@ -1,17 +1,50 @@
#ifdef PPU_CPP #ifdef PPU_CPP
unsigned PPU::Screen::get_palette(unsigned color) { unsigned PPU::Screen::get_palette(unsigned color) {
#if defined(ARCH_LSB)
static uint16 *cgram = (uint16*)memory::cgram.data();
return cgram[color];
#else
color <<= 1; color <<= 1;
return (memory::cgram[color + 0] << 0) + (memory::cgram[color + 1] << 8); 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() { void PPU::Screen::scanline() {
unsigned color = get_palette(0);
for(unsigned x = 0; x < 256; x++) { for(unsigned x = 0; x < 256; x++) {
output.main[x].color = color;
output.main[x].priority = 0; 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].priority = 0;
output.sub[x].color = 0; output.sub[x].source = 5;
} }
window.render(0);
window.render(1);
} }
void PPU::Screen::render_black() { void PPU::Screen::render_black() {
@ -19,17 +52,45 @@ void PPU::Screen::render_black() {
memset(data, 0, self.display.width << 1); 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() { void PPU::Screen::render() {
uint16 *data = self.output + self.vcounter() * 1024; uint16 *data = self.output + self.vcounter() * 1024;
uint16 *light = light_table[self.regs.display_brightness];
for(unsigned i = 0; i < 256; i++) { 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) { PPU::Screen::Screen(PPU &self) : self(self) {
light_tables = new uint16*[16]; light_table = new uint16*[16];
for(unsigned l = 0; l < 16; l++) { 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 r = 0; r < 32; r++) {
for(unsigned g = 0; g < 32; g++) { for(unsigned g = 0; g < 32; g++) {
for(unsigned b = 0; b < 32; b++) { 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 ar = (luma * r + 0.5);
unsigned ag = (luma * g + 0.5); unsigned ag = (luma * g + 0.5);
unsigned ab = (luma * b + 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() { PPU::Screen::~Screen() {
for(unsigned l = 0; l < 16; l++) delete[] light_tables[l]; for(unsigned l = 0; l < 16; l++) delete[] light_table[l];
delete[] light_tables; delete[] light_table;
} }
void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority) { void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority, unsigned source) {
if(x & 256) return;
if(priority > main[x].priority) { if(priority > main[x].priority) {
main[x].priority = priority;
main[x].color = color; main[x].color = color;
main[x].priority = priority;
main[x].source = source;
} }
} }
void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority) { void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source) {
if(x & 256) return;
if(priority > sub[x].priority) { if(priority > sub[x].priority) {
sub[x].priority = priority;
sub[x].color = color; sub[x].color = color;
sub[x].priority = priority;
sub[x].source = source;
} }
} }

View File

@ -5,35 +5,33 @@ class Screen {
bool color_mode; bool color_mode;
bool color_halve; bool color_halve;
bool back_color_enable; bool color_enable[6];
bool oam_color_enable;
bool bg4_color_enable;
bool bg3_color_enable;
bool bg2_color_enable;
bool bg1_color_enable;
unsigned color_b; unsigned color_b;
unsigned color_g; unsigned color_g;
unsigned color_r; unsigned color_r;
unsigned color;
} regs; } regs;
struct Output { struct Output {
struct { struct Pixel {
unsigned color; unsigned color;
unsigned priority; unsigned priority;
unsigned source;
} main[256], sub[256]; } main[256], sub[256];
void plot_main(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); void plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source);
} output; } output;
uint16 **light_tables; ColorWindow window;
uint16 *light_table; uint16 **light_table;
unsigned get_palette(unsigned color); unsigned get_palette(unsigned color);
uint16 addsub(unsigned x, unsigned y, bool halve);
void scanline(); void scanline();
void render_black(); void render_black();
uint16 get_pixel_main(unsigned x);
void render(); void render();
Screen(PPU &self); Screen(PPU &self);
~Screen(); ~Screen();

View File

@ -1,5 +1,10 @@
#ifdef PPU_CPP #ifdef PPU_CPP
void PPU::Sprite::frame() {
regs.time_over = false;
regs.range_over = false;
}
void PPU::Sprite::update_list(unsigned addr, uint8 data) { void PPU::Sprite::update_list(unsigned addr, uint8 data) {
if(addr < 0x0200) { if(addr < 0x0200) {
unsigned i = addr >> 2; unsigned i = addr >> 2;
@ -18,12 +23,13 @@ void PPU::Sprite::update_list(unsigned addr, uint8 data) {
unsigned i = (addr & 0x1f) << 2; unsigned i = (addr & 0x1f) << 2;
list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff); list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff);
list[i + 0].size = data & 0x02; 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 + 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 + 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[i + 3].size = data & 0x80;
list_valid = false;
} }
} }
@ -46,18 +52,21 @@ bool PPU::Sprite::on_scanline(unsigned sprite) {
} }
void PPU::Sprite::render() { void PPU::Sprite::render() {
for(unsigned i = 0; i < 128; i++) { if(list_valid == false) {
if(list[i].size == 0) { list_valid = true;
static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 }; for(unsigned i = 0; i < 128; i++) {
static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 }; if(list[i].size == 0) {
list[i].width = width[regs.base_size]; static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 };
list[i].height = height[regs.base_size]; static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 };
} else { list[i].width = width[regs.base_size];
static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 }; list[i].height = height[regs.base_size];
static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 }; } else {
list[i].width = width[regs.base_size]; static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 };
list[i].height = height[regs.base_size]; static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 };
if(regs.interlace && regs.base_size >= 6) list[i].height = 16; 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; y &= 255;
uint16 tdaddr = regs.tiledata_addr; uint16 tdaddr = regs.tiledata_addr;
uint16 chrx = (s.character >> 0) & 15; uint16 chrx = (s.character >> 0) & 15;
uint16 chry = (s.character >> 4) & 15; uint16 chry = (s.character >> 4) & 15;
if(s.use_nameselect) { if(s.use_nameselect) {
tdaddr += (256 * 32) + (regs.nameselect << 13); 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); 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) { PPU::Sprite::Sprite(PPU &self) : self(self) {

View File

@ -32,6 +32,7 @@ class Sprite {
unsigned priority; unsigned priority;
bool size; bool size;
} list[128]; } list[128];
bool list_valid;
uint8 itemlist[32]; uint8 itemlist[32];
struct TileList { struct TileList {
@ -48,6 +49,9 @@ class Sprite {
uint8 priority[256]; uint8 priority[256];
} output; } output;
LayerWindow window;
void frame();
void update_list(unsigned addr, uint8 data); void update_list(unsigned addr, uint8 data);
void address_reset(); void address_reset();
void set_first(); void set_first();

View File

@ -1,6 +1,98 @@
#ifdef PPU_CPP #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 #endif

View File

@ -1,66 +1,35 @@
class Window { class LayerWindow {
struct Regs { public:
bool bg1_one_enable; bool one_enable;
bool bg1_one_invert; bool one_invert;
bool bg1_two_enable; bool two_enable;
bool bg1_two_invert; bool two_invert;
bool bg2_one_enable; unsigned mask;
bool bg2_one_invert;
bool bg2_two_enable;
bool bg2_two_invert;
bool bg3_one_enable; bool main_enable;
bool bg3_one_invert; bool sub_enable;
bool bg3_two_enable;
bool bg3_two_invert;
bool bg4_one_enable; uint8 main[256];
bool bg4_one_invert; uint8 sub[256];
bool bg4_two_enable;
bool bg4_two_invert;
bool oam_one_enable; void render(bool screen);
bool oam_one_invert; };
bool oam_two_enable;
bool oam_two_invert; class ColorWindow {
public:
bool col_one_enable; bool one_enable;
bool col_one_invert; bool one_invert;
bool col_two_enable; bool two_enable;
bool col_two_invert; bool two_invert;
unsigned one_left; unsigned mask;
unsigned one_right;
unsigned main_mask;
unsigned two_left; unsigned sub_mask;
unsigned two_right;
uint8 main[256];
unsigned bg1_mask; uint8 sub[256];
unsigned bg2_mask;
unsigned bg3_mask; void render(bool screen);
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;
}; };

View File

@ -1,7 +1,7 @@
namespace SNES { namespace SNES {
namespace Info { namespace Info {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "068.06"; static const char Version[] = "068.08";
static const unsigned SerializerVersion = 13; static const unsigned SerializerVersion = 13;
} }
} }