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_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;
}

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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) {

View File

@ -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();

View File

@ -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

View File

@ -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);
};

View File

@ -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;
}
}