Update to v068r12 release.

(there was no r11 release posted to the WIP thread)

byuu says:

This took ten hours of mind boggling insanity to pull off.

It upgrades the S-PPU dot-based renderer to fetch one tile, and then
output all of its pixels before fetching again. It sounds easy enough,
but it's insanely difficult. I ended up taking one small shortcut, in
that rather than fetch at -7, I fetch at the first instance where a tile
is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it
fetches at -3. That won't work so well on hardware, if two BGs fetch at
the same X offset, they won't have time.

I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While
I can shift and fetch just fine, what happens is that when a new tile is
fetched in, that gives a new palette, priority, etc; and this ends up
happening between two tiles which results in the right-most edges of the
screen ending up with the wrong colors and such.

Offset-per-tile is cheap as always. Although looking at it, I'm not sure
how BG3 could pre-fetch, especially with the way one or two OPT modes
can fetch two tiles.

There's no magic in Hoffset caching yet, so the SMW1 pixel issue is
still there.

Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic
code. After re-designing the BG mosaic, I ended up needing a separate
mosaic for Mode7, and in the process I fixed that bug. The obvious
change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause
the pendulum to jump anymore.

Windows were simplified just a tad. The range testing is shared for all
modes now. Ironically, it's a bit slower, but I'll take less code over
more speed for the accuracy core.

Speaking of speed, because there's so much less calculations per pixel
for BGs, performance for the entire emulator has gone up by 30% in the
accuracy core. Pretty neat overall, I can maintain 60fps in all but,
yeah you can guess can't you?
This commit is contained in:
Tim Allen 2010-09-04 13:36:03 +10:00
parent 7df9157abd
commit 5b4702afc4
16 changed files with 335 additions and 274 deletions

View File

@ -1,6 +1,6 @@
include nall/Makefile include nall/Makefile
snes := snes snes := snes
profile := performance profile := accuracy
ui := qt ui := qt
# compiler # compiler

View File

@ -6,40 +6,27 @@ void PPU::Background::frame() {
} }
void PPU::Background::scanline() { void PPU::Background::scanline() {
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
x = -8 << hires;
y = self.vcounter();
edge = 7 - (regs.hoffset & 7);
for(unsigned n = 0; n < 8; n++) data[n] = 0;
if(self.vcounter() == 1) { if(self.vcounter() == 1) {
t.mosaic_vcounter = regs.mosaic + 1; mosaic_vcounter = regs.mosaic + 1;
t.mosaic_y = 1; mosaic_voffset = 1;
} else if(--t.mosaic_vcounter == 0) { } else if(--mosaic_vcounter == 0) {
t.mosaic_vcounter = regs.mosaic + 1; mosaic_vcounter = regs.mosaic + 1;
t.mosaic_y += regs.mosaic + 1; mosaic_voffset += regs.mosaic + 1;
} }
t.mosaic_x = 0; mosaic_hcounter = 0;
t.mosaic_hcounter = regs.mosaic + 1; mosaic_hoffset = 0;
} }
void PPU::Background::run() { void PPU::Background::get_tile() {
if(self.vcounter() == 0) return;
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
if((self.hcounter() & 2) == 0) {
output.main.priority = 0;
output.sub.priority = 0;
} else if(hires == false) {
return;
}
if(regs.mode == Mode::Inactive) return;
if(regs.main_enabled == false && regs.sub_enabled == false) return;
unsigned x = t.mosaic_x;
unsigned y = t.mosaic_y;
if(--t.mosaic_hcounter == 0) {
t.mosaic_hcounter = regs.mosaic + 1;
t.mosaic_x += regs.mosaic + 1;
}
if(regs.mode == Mode::Mode7) return run_mode7();
unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2); unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2);
unsigned palette_offset = (self.regs.bgmode == 0 ? (id << 5) : 0); unsigned palette_offset = (self.regs.bgmode == 0 ? (id << 5) : 0);
unsigned palette_size = 2 << color_depth; unsigned palette_size = 2 << color_depth;
@ -47,9 +34,10 @@ void PPU::Background::run() {
unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth); unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth);
unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4);
unsigned tile_width = (!hires ? tile_height : 4); unsigned tile_width = (!hires ? tile_height : 4);
unsigned width = (!hires ? 256 : 512); unsigned width = (!hires ? 256 : 512);
unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_x = (tile_height == 3 ? width : (width << 1));
unsigned mask_y = mask_x; unsigned mask_y = mask_x;
if(regs.screen_size & 1) mask_x <<= 1; if(regs.screen_size & 1) mask_x <<= 1;
@ -57,15 +45,18 @@ void PPU::Background::run() {
mask_x--; mask_x--;
mask_y--; mask_y--;
unsigned px = x;
unsigned py = mosaic_voffset;
unsigned hscroll = regs.hoffset; unsigned hscroll = regs.hoffset;
unsigned vscroll = regs.voffset; unsigned vscroll = regs.voffset;
if(hires) { if(hires) {
hscroll <<= 1; hscroll <<= 1;
if(self.regs.interlace) y = (y << 1) + self.field(); if(self.regs.interlace) py = (py << 1) + self.field();
} }
unsigned hoffset = hscroll + x; unsigned hoffset = hscroll + px;
unsigned voffset = vscroll + y; unsigned voffset = vscroll + py;
if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) {
uint16 opt_x = (x + (hscroll & 7)); uint16 opt_x = (x + (hscroll & 7));
@ -93,60 +84,167 @@ void PPU::Background::run() {
hoffset &= mask_x; hoffset &= mask_x;
voffset &= mask_y; voffset &= mask_y;
unsigned tile_number = get_tile(hoffset, voffset); unsigned screen_x = (regs.screen_size & 1 ? (32 << 5) : 0);
bool mirror_y = tile_number & 0x8000; unsigned screen_y = (regs.screen_size & 2 ? (32 << 5) : 0);
bool mirror_x = tile_number & 0x4000; if(regs.screen_size == 3) screen_y <<= 1;
unsigned priority = (tile_number & 0x2000 ? regs.priority1 : regs.priority0);
unsigned palette_number = (tile_number >> 10) & 7;
unsigned palette_index = palette_offset + (palette_number << palette_size);
if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_number += 1; unsigned tx = hoffset >> tile_width;
if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_number += 16; unsigned ty = voffset >> tile_height;
tile_number &= 0x03ff;
tile_number += tiledata_index; uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f);
tile_number &= tile_mask; if(tx & 0x20) offset += screen_x;
if(ty & 0x20) offset += screen_y;
uint16 addr = regs.screen_addr + (offset << 1);
tile = (memory::vram[addr + 0] << 0) + (memory::vram[addr + 1] << 8);
bool mirror_y = tile & 0x8000;
bool mirror_x = tile & 0x4000;
priority = (tile & 0x2000 ? regs.priority1 : regs.priority0);
palette_number = (tile >> 10) & 7;
palette_index = palette_offset + (palette_number << palette_size);
if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile += 1;
if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile += 16;
uint16 character = ((tile & 0x03ff) + tiledata_index) & tile_mask;
if(mirror_x) hoffset ^= 7;
if(mirror_y) voffset ^= 7; if(mirror_y) voffset ^= 7;
offset = (character << (4 + color_depth)) + ((voffset & 7) << 1);
uint8 color = get_color(hoffset, voffset, tile_number); if(regs.mode >= Mode::BPP2) {
if(color == 0) return; data[0] = memory::vram[offset + 0];
data[1] = memory::vram[offset + 1];
}
if(regs.mode >= Mode::BPP4) {
data[2] = memory::vram[offset + 16];
data[3] = memory::vram[offset + 17];
}
if(regs.mode >= Mode::BPP8) {
data[4] = memory::vram[offset + 32];
data[5] = memory::vram[offset + 33];
data[6] = memory::vram[offset + 48];
data[7] = memory::vram[offset + 49];
}
color += palette_index; if(mirror_x) for(unsigned n = 0; n < 8; n++) {
data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0);
data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc);
data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa);
}
}
void PPU::Background::run() {
if(self.vcounter() == 0) return;
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
if((self.hcounter() & 2) == 0) {
output.main.priority = 0;
output.sub.priority = 0;
} else if(hires == false) {
return;
}
if(regs.mode == Mode::Inactive) return;
if(regs.main_enable == false && regs.sub_enable == false) return;
if(regs.mode == Mode::Mode7) return run_mode7();
if((x++ & 7) == edge) get_tile();
uint8 palette = get_tile_color();
if(x >= 0 && mosaic_hcounter++ >= regs.mosaic) {
mosaic_hcounter = 0;
mosaic_palette = palette;
}
if(mosaic_palette == 0) return;
if(hires == false) { if(hires == false) {
if(regs.main_enabled) { if(regs.main_enable) {
output.main.priority = priority; output.main.priority = priority;
output.main.palette = color; output.main.palette = palette_index + mosaic_palette;
output.main.tile = tile_number; output.main.tile = tile;
} }
if(regs.sub_enabled) { if(regs.sub_enable) {
output.sub.priority = priority; output.sub.priority = priority;
output.sub.palette = color; output.sub.palette = palette_index + mosaic_palette;
output.sub.tile = tile_number; output.sub.tile = tile;
} }
} else { } else {
if(x & 1) { if(x & 1) {
if(regs.main_enabled) { if(regs.main_enable) {
output.main.priority = priority; output.main.priority = priority;
output.main.palette = color; output.main.palette = palette_index + mosaic_palette;
output.main.tile = tile_number; output.main.tile = tile;
} }
} else { } else {
if(regs.sub_enabled) { if(regs.sub_enable) {
output.sub.priority = priority; output.sub.priority = priority;
output.sub.palette = color; output.sub.palette = palette_index + mosaic_palette;
output.sub.tile = tile_number; output.sub.tile = tile;
} }
} }
} }
} }
unsigned PPU::Background::get_tile_color() {
unsigned color = 0;
if(regs.mode >= Mode::BPP2) {
color += (data[0] & 0x80) ? 0x01 : 0; data[0] <<= 1;
color += (data[1] & 0x80) ? 0x02 : 0; data[1] <<= 1;
}
if(regs.mode >= Mode::BPP4) {
color += (data[2] & 0x80) ? 0x04 : 0; data[2] <<= 1;
color += (data[3] & 0x80) ? 0x08 : 0; data[3] <<= 1;
}
if(regs.mode >= Mode::BPP8) {
color += (data[4] & 0x80) ? 0x10 : 0; data[4] <<= 1;
color += (data[5] & 0x80) ? 0x20 : 0; data[5] <<= 1;
color += (data[6] & 0x80) ? 0x40 : 0; data[6] <<= 1;
color += (data[7] & 0x80) ? 0x80 : 0; data[7] <<= 1;
}
return color;
}
void PPU::Background::reset() {
regs.tiledata_addr = 0;
regs.screen_addr = 0;
regs.screen_size = 0;
regs.mosaic = 0;
regs.tile_size = 0;
regs.mode = 0;
regs.priority0 = 0;
regs.priority1 = 0;
regs.main_enable = 0;
regs.sub_enable = 0;
regs.hoffset = 0;
regs.voffset = 0;
output.main.palette = 0;
output.main.priority = 0;
output.sub.palette = 0;
output.sub.priority = 0;
x = 0;
y = 0;
edge = 0;
mosaic_vcounter = 0;
mosaic_voffset = 0;
mosaic_hcounter = 0;
mosaic_hoffset = 0;
mosaic_palette = 0;
tile = 0;
priority = 0;
palette_number = 0;
palette_index = 0;
for(unsigned n = 0; n < 8; n++) data[n] = 0;
}
unsigned PPU::Background::get_tile(unsigned x, unsigned y) { unsigned PPU::Background::get_tile(unsigned x, unsigned y) {
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4);
unsigned tile_width = (!hires ? tile_height : 4); unsigned tile_width = (!hires ? tile_height : 4);
unsigned width = (!hires ? 256 : 512); unsigned width = (!hires ? 256 : 512);
unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_x = (tile_height == 3 ? width : (width << 1));
unsigned mask_y = mask_x; unsigned mask_y = mask_x;
@ -162,89 +260,12 @@ unsigned PPU::Background::get_tile(unsigned x, unsigned y) {
x = (x & mask_x) >> tile_width; x = (x & mask_x) >> tile_width;
y = (y & mask_y) >> tile_height; y = (y & mask_y) >> tile_height;
uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f);
if(x & 0x20) pos += screen_x; if(x & 0x20) offset += screen_x;
if(y & 0x20) pos += screen_y; if(y & 0x20) offset += screen_y;
uint16 addr = regs.screen_addr + (pos << 1); uint16 addr = regs.screen_addr + (offset << 1);
return memory::vram[addr + 0] + (memory::vram[addr + 1] << 8); return (memory::vram[addr + 0] << 0) + (memory::vram[addr + 1] << 8);
}
unsigned PPU::Background::get_color(unsigned x, unsigned y, uint16 offset) {
unsigned mask = 0x80 >> (x & 7);
switch(regs.mode) {
case Background::Mode::BPP2: {
offset = (offset * 16) + ((y & 7) * 2);
unsigned d0 = memory::vram[offset + 0];
unsigned d1 = memory::vram[offset + 1];
return (((bool)(d0 & mask)) << 0)
+ (((bool)(d1 & mask)) << 1);
}
case Background::Mode::BPP4: {
offset = (offset * 32) + ((y & 7) * 2);
unsigned d0 = memory::vram[offset + 0];
unsigned d1 = memory::vram[offset + 1];
unsigned d2 = memory::vram[offset + 16];
unsigned d3 = memory::vram[offset + 17];
return (((bool)(d0 & mask)) << 0)
+ (((bool)(d1 & mask)) << 1)
+ (((bool)(d2 & mask)) << 2)
+ (((bool)(d3 & mask)) << 3);
}
case Background::Mode::BPP8: {
offset = (offset * 64) + ((y & 7) * 2);
unsigned d0 = memory::vram[offset + 0];
unsigned d1 = memory::vram[offset + 1];
unsigned d2 = memory::vram[offset + 16];
unsigned d3 = memory::vram[offset + 17];
unsigned d4 = memory::vram[offset + 32];
unsigned d5 = memory::vram[offset + 33];
unsigned d6 = memory::vram[offset + 48];
unsigned d7 = memory::vram[offset + 49];
return (((bool)(d0 & mask)) << 0)
+ (((bool)(d1 & mask)) << 1)
+ (((bool)(d2 & mask)) << 2)
+ (((bool)(d3 & mask)) << 3)
+ (((bool)(d4 & mask)) << 4)
+ (((bool)(d5 & mask)) << 5)
+ (((bool)(d6 & mask)) << 6)
+ (((bool)(d7 & mask)) << 7);
}
};
}
void PPU::Background::reset() {
t.mosaic_x = 0;
t.mosaic_y = 0;
t.mosaic_hcounter = 0;
t.mosaic_vcounter = 0;
regs.tiledata_addr = 0;
regs.screen_addr = 0;
regs.screen_size = 0;
regs.mosaic = 0;
regs.tile_size = 0;
regs.mode = 0;
regs.priority0 = 0;
regs.priority1 = 0;
regs.main_enabled = 0;
regs.sub_enabled = 0;
regs.hoffset = 0;
regs.voffset = 0;
output.main.palette = 0;
output.main.priority = 0;
output.sub.palette = 0;
output.sub.priority = 0;
} }
PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) {

View File

@ -1,6 +1,4 @@
class Background { class Background {
public:
PPU &self;
struct ID { enum { BG1, BG2, BG3, BG4 }; }; struct ID { enum { BG1, BG2, BG3, BG4 }; };
unsigned id; unsigned id;
@ -8,14 +6,7 @@ public:
struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; }; struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; };
struct TileSize { enum { Size8x8, Size16x16 }; }; struct TileSize { enum { Size8x8, Size16x16 }; };
struct { struct Regs {
unsigned mosaic_x;
unsigned mosaic_y;
unsigned mosaic_hcounter;
unsigned mosaic_vcounter;
} t;
struct {
unsigned tiledata_addr; unsigned tiledata_addr;
unsigned screen_addr; unsigned screen_addr;
unsigned screen_size; unsigned screen_size;
@ -26,33 +17,53 @@ public:
unsigned priority0; unsigned priority0;
unsigned priority1; unsigned priority1;
bool main_enabled; bool main_enable;
bool sub_enabled; bool sub_enable;
unsigned hoffset; unsigned hoffset;
unsigned voffset; unsigned voffset;
} regs; } regs;
struct { struct Output {
struct { struct Pixel {
unsigned priority; //0 = none (transparent) unsigned priority; //0 = none (transparent)
unsigned palette; unsigned palette;
unsigned tile; unsigned tile;
} main, sub; } main, sub;
} output; } output;
struct {
signed x;
signed y;
signed edge;
unsigned mosaic_vcounter;
unsigned mosaic_voffset;
unsigned mosaic_hcounter;
unsigned mosaic_hoffset;
unsigned mosaic_palette;
unsigned tile;
unsigned priority;
unsigned palette_number;
unsigned palette_index;
uint8 data[8];
};
void frame(); void frame();
void scanline(); void scanline();
void run(); void run();
unsigned get_tile(unsigned x, unsigned y);
unsigned get_color(unsigned x, unsigned y, uint16 offset);
void reset(); void reset();
void get_tile();
unsigned get_tile_color();
unsigned get_tile(unsigned x, unsigned y);
signed clip(signed n);
void run_mode7();
void serialize(serializer&); void serialize(serializer&);
Background(PPU &self, unsigned id); Background(PPU &self, unsigned id);
private: PPU &self;
//mode7.cpp friend class PPU;
signed clip(signed n);
void run_mode7();
}; };

View File

@ -16,8 +16,15 @@ void PPU::Background::run_mode7() {
signed hoffset = sclip<13>(self.regs.mode7_hoffset); signed hoffset = sclip<13>(self.regs.mode7_hoffset);
signed voffset = sclip<13>(self.regs.mode7_voffset); signed voffset = sclip<13>(self.regs.mode7_voffset);
unsigned x = t.mosaic_x; if(++Background::x & ~255) return;
unsigned y = self.bg1.t.mosaic_y; //BG2 vertical mosaic uses BG1 mosaic size unsigned x = mosaic_hoffset;
unsigned y = self.bg1.mosaic_voffset; //BG2 vertical mosaic uses BG1 mosaic size
if(mosaic_hcounter++ == regs.mosaic) {
mosaic_hcounter = 0;
mosaic_hoffset += regs.mosaic + 1;
}
if(self.regs.mode7_hflip) x = 255 - x; if(self.regs.mode7_hflip) x = 255 - x;
if(self.regs.mode7_vflip) y = 255 - y; if(self.regs.mode7_vflip) y = 255 - y;
@ -41,11 +48,12 @@ void PPU::Background::run_mode7() {
py &= 1023; py &= 1023;
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} break; break;
}
//palette color 0 outside of screen area //palette color 0 outside of screen area
case 2: { case 2: {
if(px < 0 || px > 1023 || py < 0 || py > 1023) { if((px | py) & ~1023) {
palette = 0; palette = 0;
} else { } else {
px &= 1023; px &= 1023;
@ -53,11 +61,12 @@ void PPU::Background::run_mode7() {
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} }
} break; break;
}
//character 0 repetition outside of screen area //character 0 repetition outside of screen area
case 3: { case 3: {
if(px < 0 || px > 1023 || py < 0 || py > 1023) { if((px & py) & ~1023) {
tile = 0; tile = 0;
} else { } else {
px &= 1023; px &= 1023;
@ -65,7 +74,8 @@ void PPU::Background::run_mode7() {
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
} }
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} break; break;
}
} }
unsigned priority; unsigned priority;
@ -78,14 +88,16 @@ void PPU::Background::run_mode7() {
if(palette == 0) return; if(palette == 0) return;
if(regs.main_enabled) { if(regs.main_enable) {
output.main.palette = palette; output.main.palette = palette;
output.main.priority = priority; output.main.priority = priority;
output.main.tile = 0;
} }
if(regs.sub_enabled) { if(regs.sub_enable) {
output.sub.palette = palette; output.sub.palette = palette;
output.sub.priority = priority; output.sub.priority = priority;
output.sub.tile = 0;
} }
} }

View File

@ -19,26 +19,26 @@ uint16 PPU::get_vram_address() {
} }
uint8 PPU::vram_read(unsigned addr) { uint8 PPU::vram_read(unsigned addr) {
if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) {
return memory::vram[addr]; return memory::vram[addr];
} }
return 0x00; return 0x00;
} }
void PPU::vram_write(unsigned addr, uint8 data) { void PPU::vram_write(unsigned addr, uint8 data) {
if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) {
memory::vram[addr] = data; memory::vram[addr] = data;
} }
} }
uint8 PPU::oam_read(unsigned addr) { uint8 PPU::oam_read(unsigned addr) {
if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; if(!regs.display_disable && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr;
if(addr & 0x0200) addr &= 0x021f; if(addr & 0x0200) addr &= 0x021f;
return memory::oam[addr]; return memory::oam[addr];
} }
void PPU::oam_write(unsigned addr, uint8 data) { void PPU::oam_write(unsigned addr, uint8 data) {
if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; if(!regs.display_disable && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr;
if(addr & 0x0200) addr &= 0x021f; if(addr & 0x0200) addr &= 0x021f;
memory::oam[addr] = data; memory::oam[addr] = data;
oam.update(addr, data); oam.update(addr, data);
@ -164,8 +164,8 @@ void PPU::mmio_update_video_mode() {
//INIDISP //INIDISP
void PPU::mmio_w2100(uint8 data) { void PPU::mmio_w2100(uint8 data) {
if(regs.display_disabled && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset(); if(regs.display_disable && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset();
regs.display_disabled = data & 0x80; regs.display_disable = data & 0x80;
regs.display_brightness = data & 0x0f; regs.display_brightness = data & 0x0f;
} }
@ -491,20 +491,20 @@ void PPU::mmio_w212b(uint8 data) {
//TM //TM
void PPU::mmio_w212c(uint8 data) { void PPU::mmio_w212c(uint8 data) {
oam.regs.main_enabled = data & 0x10; oam.regs.main_enable = data & 0x10;
bg4.regs.main_enabled = data & 0x08; bg4.regs.main_enable = data & 0x08;
bg3.regs.main_enabled = data & 0x04; bg3.regs.main_enable = data & 0x04;
bg2.regs.main_enabled = data & 0x02; bg2.regs.main_enable = data & 0x02;
bg1.regs.main_enabled = data & 0x01; bg1.regs.main_enable = data & 0x01;
} }
//TS //TS
void PPU::mmio_w212d(uint8 data) { void PPU::mmio_w212d(uint8 data) {
oam.regs.sub_enabled = data & 0x10; oam.regs.sub_enable = data & 0x10;
bg4.regs.sub_enabled = data & 0x08; bg4.regs.sub_enable = data & 0x08;
bg3.regs.sub_enabled = data & 0x04; bg3.regs.sub_enable = data & 0x04;
bg2.regs.sub_enabled = data & 0x02; bg2.regs.sub_enable = data & 0x02;
bg1.regs.sub_enabled = data & 0x01; bg1.regs.sub_enable = data & 0x01;
} }
//TMW //TMW
@ -703,7 +703,7 @@ void PPU::mmio_reset() {
regs.icgramaddr = 0; regs.icgramaddr = 0;
//$2100 INIDISP //$2100 INIDISP
regs.display_disabled = true; regs.display_disable = true;
regs.display_brightness = 0; regs.display_brightness = 0;
//$2102 OAMADDL //$2102 OAMADDL

View File

@ -15,7 +15,7 @@ struct {
uint16 icgramaddr; uint16 icgramaddr;
//$2100 INIDISP //$2100 INIDISP
bool display_disabled; bool display_disable;
unsigned display_brightness; unsigned display_brightness;
//$2102 OAMADDL //$2102 OAMADDL

View File

@ -38,10 +38,11 @@ void PPU::enter() {
} }
scanline(); scanline();
add_clocks(88); add_clocks(56);
if(vcounter() <= (!regs.overscan ? 224 : 239)) { if(vcounter() <= (!regs.overscan ? 224 : 239)) {
for(unsigned n = 0; n < 256; n++) { add_clocks(4);
for(unsigned pixel = 1; pixel < 8 + 256; pixel++) {
bg1.run(); bg1.run();
bg2.run(); bg2.run();
bg3.run(); bg3.run();
@ -52,19 +53,21 @@ void PPU::enter() {
bg2.run(); bg2.run();
bg3.run(); bg3.run();
bg4.run(); bg4.run();
oam.run(); if(pixel >= 8) {
window.run(); oam.run();
screen.run(); window.run();
screen.run();
}
add_clocks(2); add_clocks(2);
} }
add_clocks(22); add_clocks(22);
oam.tilefetch(); oam.tilefetch();
} else { } else {
add_clocks(1024 + 22 + 136); add_clocks(1056 + 22 + 136);
} }
add_clocks(lineclocks() - 88 - 1024 - 22 - 136); add_clocks(lineclocks() - 56 - 1056 - 22 - 136);
} }
} }

View File

@ -18,6 +18,17 @@ public:
~PPU(); ~PPU();
private: private:
uint16 *surface;
uint16 *output;
uint8 ppu1_version;
uint8 ppu2_version;
struct {
bool interlace;
bool overscan;
} display;
#include "background/background.hpp" #include "background/background.hpp"
#include "mmio/mmio.hpp" #include "mmio/mmio.hpp"
#include "screen/screen.hpp" #include "screen/screen.hpp"
@ -32,17 +43,6 @@ private:
Window window; Window window;
Screen screen; Screen screen;
uint16 *surface;
uint16 *output;
uint8 ppu1_version;
uint8 ppu2_version;
struct {
bool interlace;
bool overscan;
} display;
static void Enter(); static void Enter();
void add_clocks(unsigned); void add_clocks(unsigned);

View File

@ -12,10 +12,10 @@ void PPU::Screen::run() {
*output++ = color; *output++ = color;
*output++ = color; *output++ = color;
} else { } else {
color = get_pixel(true);
*output++ = color;
color = get_pixel(false); color = get_pixel(false);
*output++ = color; *output++ = color;
color = get_pixel(true);
*output++ = color;
} }
} }
@ -146,7 +146,7 @@ uint16 PPU::Screen::get_pixel(bool swap) {
//======== //========
output = light_table[self.regs.display_brightness][output]; output = light_table[self.regs.display_brightness][output];
if(self.regs.display_disabled) output = 0x0000; if(self.regs.display_disable) output = 0x0000;
return output; return output;
} }

View File

@ -1,9 +1,7 @@
class Screen { class Screen {
public:
PPU &self;
uint16 *output; uint16 *output;
struct { struct Regs {
bool addsub_mode; bool addsub_mode;
bool direct_color; bool direct_color;
@ -25,13 +23,15 @@ public:
void run(); void run();
void reset(); void reset();
void serialize(serializer&);
Screen(PPU &self);
private:
uint16 light_table[16][32768]; uint16 light_table[16][32768];
uint16 get_pixel(bool swap); uint16 get_pixel(bool swap);
uint16 addsub(unsigned x, unsigned y, bool halve); uint16 addsub(unsigned x, unsigned y, bool halve);
uint16 get_color(unsigned palette); uint16 get_color(unsigned palette);
uint16 get_direct_color(unsigned palette, unsigned tile); uint16 get_direct_color(unsigned palette, unsigned tile);
void serialize(serializer&);
Screen(PPU &self);
PPU &self;
friend class PPU;
}; };

View File

@ -37,7 +37,7 @@ void PPU::serialize(serializer &s) {
s.integer(regs.ioamaddr); s.integer(regs.ioamaddr);
s.integer(regs.icgramaddr); s.integer(regs.icgramaddr);
s.integer(regs.display_disabled); s.integer(regs.display_disable);
s.integer(regs.display_brightness); s.integer(regs.display_brightness);
s.integer(regs.oam_baseaddr); s.integer(regs.oam_baseaddr);
@ -89,11 +89,6 @@ void PPU::serialize(serializer &s) {
void PPU::Background::serialize(serializer &s) { void PPU::Background::serialize(serializer &s) {
s.integer(id); s.integer(id);
s.integer(t.mosaic_x);
s.integer(t.mosaic_y);
s.integer(t.mosaic_hcounter);
s.integer(t.mosaic_vcounter);
s.integer(regs.tiledata_addr); s.integer(regs.tiledata_addr);
s.integer(regs.screen_addr); s.integer(regs.screen_addr);
s.integer(regs.screen_size); s.integer(regs.screen_size);
@ -104,8 +99,8 @@ void PPU::Background::serialize(serializer &s) {
s.integer(regs.priority0); s.integer(regs.priority0);
s.integer(regs.priority1); s.integer(regs.priority1);
s.integer(regs.main_enabled); s.integer(regs.main_enable);
s.integer(regs.sub_enabled); s.integer(regs.sub_enable);
s.integer(regs.hoffset); s.integer(regs.hoffset);
s.integer(regs.voffset); s.integer(regs.voffset);
@ -117,6 +112,22 @@ void PPU::Background::serialize(serializer &s) {
s.integer(output.sub.priority); s.integer(output.sub.priority);
s.integer(output.sub.palette); s.integer(output.sub.palette);
s.integer(output.sub.tile); s.integer(output.sub.tile);
s.integer(x);
s.integer(y);
s.integer(edge);
s.integer(mosaic_vcounter);
s.integer(mosaic_voffset);
s.integer(mosaic_hcounter);
s.integer(mosaic_hoffset);
s.integer(mosaic_palette);
s.integer(tile);
s.integer(priority);
s.integer(palette_number);
s.integer(palette_index);
s.array(data);
} }
void PPU::Sprite::serialize(serializer &s) { void PPU::Sprite::serialize(serializer &s) {
@ -153,8 +164,8 @@ void PPU::Sprite::serialize(serializer &s) {
} }
} }
s.integer(regs.main_enabled); s.integer(regs.main_enable);
s.integer(regs.sub_enabled); s.integer(regs.sub_enable);
s.integer(regs.interlace); s.integer(regs.interlace);
s.integer(regs.base_size); s.integer(regs.base_size);
@ -178,8 +189,6 @@ void PPU::Sprite::serialize(serializer &s) {
} }
void PPU::Window::serialize(serializer &s) { void PPU::Window::serialize(serializer &s) {
s.integer(t.x);
s.integer(regs.bg1_one_enable); s.integer(regs.bg1_one_enable);
s.integer(regs.bg1_one_invert); s.integer(regs.bg1_one_invert);
s.integer(regs.bg1_two_enable); s.integer(regs.bg1_two_enable);
@ -239,6 +248,9 @@ void PPU::Window::serialize(serializer &s) {
s.integer(output.main.color_enable); s.integer(output.main.color_enable);
s.integer(output.sub.color_enable); s.integer(output.sub.color_enable);
s.integer(x);
s.integer(one);
s.integer(two);
} }
void PPU::Screen::serialize(serializer &s) { void PPU::Screen::serialize(serializer &s) {

View File

@ -23,7 +23,7 @@ void PPU::Sprite::scanline() {
auto oam_item = t.item[t.active]; auto oam_item = t.item[t.active];
auto oam_tile = t.tile[t.active]; auto oam_tile = t.tile[t.active];
if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disabled == false) address_reset(); if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disable == false) address_reset();
if(t.y >= (!self.regs.overscan ? 224 : 239)) return; if(t.y >= (!self.regs.overscan ? 224 : 239)) return;
memset(oam_item, 0xff, 32); //default to invalid memset(oam_item, 0xff, 32); //default to invalid
@ -72,12 +72,12 @@ void PPU::Sprite::run() {
color |= ((bool)(tile.d3 & mask)) << 3; color |= ((bool)(tile.d3 & mask)) << 3;
if(color) { if(color) {
if(regs.main_enabled) { if(regs.main_enable) {
output.main.palette = tile.palette + color; output.main.palette = tile.palette + color;
output.main.priority = priority_table[tile.priority]; output.main.priority = priority_table[tile.priority];
} }
if(regs.sub_enabled) { if(regs.sub_enable) {
output.sub.palette = tile.palette + color; output.sub.palette = tile.palette + color;
output.sub.priority = priority_table[tile.priority]; output.sub.priority = priority_table[tile.priority];
} }
@ -189,8 +189,8 @@ void PPU::Sprite::reset() {
} }
} }
regs.main_enabled = 0; regs.main_enable = 0;
regs.sub_enabled = 0; regs.sub_enable = 0;
regs.interlace = 0; regs.interlace = 0;
regs.base_size = 0; regs.base_size = 0;

View File

@ -1,7 +1,4 @@
class Sprite { class Sprite {
public:
PPU &self;
struct SpriteItem { struct SpriteItem {
uint16 x; uint16 x;
uint16 y; uint16 y;
@ -36,9 +33,9 @@ public:
TileItem tile[2][34]; TileItem tile[2][34];
} t; } t;
struct { struct Regs {
bool main_enabled; bool main_enable;
bool sub_enabled; bool sub_enable;
bool interlace; bool interlace;
uint8 base_size; uint8 base_size;
@ -55,8 +52,8 @@ public:
bool range_over; bool range_over;
} regs; } regs;
struct { struct Output {
struct { struct Pixel {
unsigned priority; //0 = none (transparent) unsigned priority; //0 = none (transparent)
unsigned palette; unsigned palette;
} main, sub; } main, sub;
@ -73,9 +70,11 @@ public:
void tilefetch(); void tilefetch();
void reset(); void reset();
bool on_scanline(SpriteItem&);
void serialize(serializer&); void serialize(serializer&);
Sprite(PPU &self); Sprite(PPU &self);
private: PPU &self;
bool on_scanline(SpriteItem&); friend class PPU;
}; };

View File

@ -1,11 +1,14 @@
#ifdef PPU_CPP #ifdef PPU_CPP
void PPU::Window::scanline() { void PPU::Window::scanline() {
t.x = 0; x = 0;
} }
void PPU::Window::run() { void PPU::Window::run() {
bool main, sub; bool main, sub;
one = (x >= regs.one_left && x <= regs.one_right);
two = (x >= regs.two_left && x <= regs.two_right);
x++;
test( test(
main, sub, main, sub,
@ -75,8 +78,6 @@ void PPU::Window::run() {
output.main.color_enable = main; output.main.color_enable = main;
output.sub.color_enable = sub; output.sub.color_enable = sub;
t.x++;
} }
void PPU::Window::test( void PPU::Window::test(
@ -85,24 +86,21 @@ void PPU::Window::test(
bool two_enable, bool two_invert, bool two_enable, bool two_invert,
uint8 mask, bool main_enable, bool sub_enable uint8 mask, bool main_enable, bool sub_enable
) { ) {
unsigned x = t.x; bool one = Window::one ^ one_invert;
bool two = Window::two ^ two_invert;
bool output; bool output;
if(one_enable == false && two_enable == false) { if(one_enable == false && two_enable == false) {
output = false; output = false;
} else if(one_enable == true && two_enable == false) { } else if(one_enable == true && two_enable == false) {
output = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; output = one;
} else if(one_enable == false && two_enable == true) { } else if(one_enable == false && two_enable == true) {
output = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; output = two;
} else { } else switch(mask) {
bool one = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; case 0: output = (one | two) == 1; break;
bool two = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; case 1: output = (one & two) == 1; break;
switch(mask) { case 2: output = (one ^ two) == 1; break;
case 0: output = (one | two) == 1; break; case 3: output = (one ^ two) == 0; break;
case 1: output = (one & two) == 1; break;
case 2: output = (one ^ two) == 1; break;
case 3: output = (one ^ two) == 0; break;
}
} }
main = main_enable ? output : false; main = main_enable ? output : false;
@ -110,7 +108,6 @@ void PPU::Window::test(
} }
void PPU::Window::reset() { void PPU::Window::reset() {
t.x = 0;
regs.bg1_one_enable = false; regs.bg1_one_enable = false;
regs.bg1_one_invert = false; regs.bg1_one_invert = false;
regs.bg1_two_enable = false; regs.bg1_two_enable = false;
@ -157,8 +154,13 @@ void PPU::Window::reset() {
regs.oam_sub_enable = 0; regs.oam_sub_enable = 0;
regs.col_main_mask = 0; regs.col_main_mask = 0;
regs.col_sub_mask = 0; regs.col_sub_mask = 0;
output.main.color_enable = 0; output.main.color_enable = 0;
output.sub.color_enable = 0; output.sub.color_enable = 0;
x = 0;
one = 0;
two = 0;
} }
PPU::Window::Window(PPU &self) : self(self) { PPU::Window::Window(PPU &self) : self(self) {

View File

@ -1,11 +1,4 @@
class Window { class Window {
public:
PPU &self;
struct {
unsigned x;
} t;
struct { struct {
bool bg1_one_enable; bool bg1_one_enable;
bool bg1_one_invert; bool bg1_one_invert;
@ -64,24 +57,32 @@ public:
uint8 col_sub_mask; uint8 col_sub_mask;
} regs; } regs;
struct { struct Output {
struct { struct Pixel {
bool color_enable; bool color_enable;
} main, sub; } main, sub;
} output; } output;
struct {
unsigned x;
bool one;
bool two;
};
void scanline(); void scanline();
void run(); void run();
void reset(); void reset();
void serialize(serializer&);
Window(PPU &self);
private:
void test( void test(
bool &main, bool &sub, bool &main, bool &sub,
bool one_enable, bool one_invert, bool one_enable, bool one_invert,
bool two_enable, bool two_invert, bool two_enable, bool two_invert,
uint8 mask, bool main_enable, bool sub_enable uint8 mask, bool main_enable, bool sub_enable
); );
void serialize(serializer&);
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.10"; static const char Version[] = "068.12";
static const unsigned SerializerVersion = 13; static const unsigned SerializerVersion = 13;
} }
} }