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
snes := snes
profile := performance
profile := accuracy
ui := qt
# compiler

View File

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

View File

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

View File

@ -16,8 +16,15 @@ void PPU::Background::run_mode7() {
signed hoffset = sclip<13>(self.regs.mode7_hoffset);
signed voffset = sclip<13>(self.regs.mode7_voffset);
unsigned x = t.mosaic_x;
unsigned y = self.bg1.t.mosaic_y; //BG2 vertical mosaic uses BG1 mosaic size
if(++Background::x & ~255) return;
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_vflip) y = 255 - y;
@ -41,11 +48,12 @@ void PPU::Background::run_mode7() {
py &= 1023;
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} break;
break;
}
//palette color 0 outside of screen area
case 2: {
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
if((px | py) & ~1023) {
palette = 0;
} else {
px &= 1023;
@ -53,11 +61,12 @@ void PPU::Background::run_mode7() {
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
}
} break;
break;
}
//character 0 repetition outside of screen area
case 3: {
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
if((px & py) & ~1023) {
tile = 0;
} else {
px &= 1023;
@ -65,7 +74,8 @@ void PPU::Background::run_mode7() {
tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1];
}
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} break;
break;
}
}
unsigned priority;
@ -78,14 +88,16 @@ void PPU::Background::run_mode7() {
if(palette == 0) return;
if(regs.main_enabled) {
if(regs.main_enable) {
output.main.palette = palette;
output.main.priority = priority;
output.main.tile = 0;
}
if(regs.sub_enabled) {
if(regs.sub_enable) {
output.sub.palette = palette;
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) {
if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) {
if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) {
return memory::vram[addr];
}
return 0x00;
}
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;
}
}
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;
return memory::oam[addr];
}
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;
memory::oam[addr] = data;
oam.update(addr, data);
@ -164,8 +164,8 @@ void PPU::mmio_update_video_mode() {
//INIDISP
void PPU::mmio_w2100(uint8 data) {
if(regs.display_disabled && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset();
regs.display_disabled = data & 0x80;
if(regs.display_disable && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset();
regs.display_disable = data & 0x80;
regs.display_brightness = data & 0x0f;
}
@ -491,20 +491,20 @@ void PPU::mmio_w212b(uint8 data) {
//TM
void PPU::mmio_w212c(uint8 data) {
oam.regs.main_enabled = data & 0x10;
bg4.regs.main_enabled = data & 0x08;
bg3.regs.main_enabled = data & 0x04;
bg2.regs.main_enabled = data & 0x02;
bg1.regs.main_enabled = data & 0x01;
oam.regs.main_enable = data & 0x10;
bg4.regs.main_enable = data & 0x08;
bg3.regs.main_enable = data & 0x04;
bg2.regs.main_enable = data & 0x02;
bg1.regs.main_enable = data & 0x01;
}
//TS
void PPU::mmio_w212d(uint8 data) {
oam.regs.sub_enabled = data & 0x10;
bg4.regs.sub_enabled = data & 0x08;
bg3.regs.sub_enabled = data & 0x04;
bg2.regs.sub_enabled = data & 0x02;
bg1.regs.sub_enabled = data & 0x01;
oam.regs.sub_enable = data & 0x10;
bg4.regs.sub_enable = data & 0x08;
bg3.regs.sub_enable = data & 0x04;
bg2.regs.sub_enable = data & 0x02;
bg1.regs.sub_enable = data & 0x01;
}
//TMW
@ -703,7 +703,7 @@ void PPU::mmio_reset() {
regs.icgramaddr = 0;
//$2100 INIDISP
regs.display_disabled = true;
regs.display_disable = true;
regs.display_brightness = 0;
//$2102 OAMADDL

View File

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

View File

@ -38,10 +38,11 @@ void PPU::enter() {
}
scanline();
add_clocks(88);
add_clocks(56);
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();
bg2.run();
bg3.run();
@ -52,19 +53,21 @@ void PPU::enter() {
bg2.run();
bg3.run();
bg4.run();
oam.run();
window.run();
screen.run();
if(pixel >= 8) {
oam.run();
window.run();
screen.run();
}
add_clocks(2);
}
add_clocks(22);
oam.tilefetch();
} 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();
private:
uint16 *surface;
uint16 *output;
uint8 ppu1_version;
uint8 ppu2_version;
struct {
bool interlace;
bool overscan;
} display;
#include "background/background.hpp"
#include "mmio/mmio.hpp"
#include "screen/screen.hpp"
@ -32,17 +43,6 @@ private:
Window window;
Screen screen;
uint16 *surface;
uint16 *output;
uint8 ppu1_version;
uint8 ppu2_version;
struct {
bool interlace;
bool overscan;
} display;
static void Enter();
void add_clocks(unsigned);

View File

@ -12,10 +12,10 @@ void PPU::Screen::run() {
*output++ = color;
*output++ = color;
} else {
color = get_pixel(true);
*output++ = color;
color = get_pixel(false);
*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];
if(self.regs.display_disabled) output = 0x0000;
if(self.regs.display_disable) output = 0x0000;
return output;
}

View File

@ -1,9 +1,7 @@
class Screen {
public:
PPU &self;
uint16 *output;
struct {
struct Regs {
bool addsub_mode;
bool direct_color;
@ -25,13 +23,15 @@ public:
void run();
void reset();
void serialize(serializer&);
Screen(PPU &self);
private:
uint16 light_table[16][32768];
uint16 get_pixel(bool swap);
uint16 addsub(unsigned x, unsigned y, bool halve);
uint16 get_color(unsigned palette);
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.icgramaddr);
s.integer(regs.display_disabled);
s.integer(regs.display_disable);
s.integer(regs.display_brightness);
s.integer(regs.oam_baseaddr);
@ -89,11 +89,6 @@ void PPU::serialize(serializer &s) {
void PPU::Background::serialize(serializer &s) {
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.screen_addr);
s.integer(regs.screen_size);
@ -104,8 +99,8 @@ void PPU::Background::serialize(serializer &s) {
s.integer(regs.priority0);
s.integer(regs.priority1);
s.integer(regs.main_enabled);
s.integer(regs.sub_enabled);
s.integer(regs.main_enable);
s.integer(regs.sub_enable);
s.integer(regs.hoffset);
s.integer(regs.voffset);
@ -117,6 +112,22 @@ void PPU::Background::serialize(serializer &s) {
s.integer(output.sub.priority);
s.integer(output.sub.palette);
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) {
@ -153,8 +164,8 @@ void PPU::Sprite::serialize(serializer &s) {
}
}
s.integer(regs.main_enabled);
s.integer(regs.sub_enabled);
s.integer(regs.main_enable);
s.integer(regs.sub_enable);
s.integer(regs.interlace);
s.integer(regs.base_size);
@ -178,8 +189,6 @@ void PPU::Sprite::serialize(serializer &s) {
}
void PPU::Window::serialize(serializer &s) {
s.integer(t.x);
s.integer(regs.bg1_one_enable);
s.integer(regs.bg1_one_invert);
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.sub.color_enable);
s.integer(x);
s.integer(one);
s.integer(two);
}
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_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;
memset(oam_item, 0xff, 32); //default to invalid
@ -72,12 +72,12 @@ void PPU::Sprite::run() {
color |= ((bool)(tile.d3 & mask)) << 3;
if(color) {
if(regs.main_enabled) {
if(regs.main_enable) {
output.main.palette = tile.palette + color;
output.main.priority = priority_table[tile.priority];
}
if(regs.sub_enabled) {
if(regs.sub_enable) {
output.sub.palette = tile.palette + color;
output.sub.priority = priority_table[tile.priority];
}
@ -189,8 +189,8 @@ void PPU::Sprite::reset() {
}
}
regs.main_enabled = 0;
regs.sub_enabled = 0;
regs.main_enable = 0;
regs.sub_enable = 0;
regs.interlace = 0;
regs.base_size = 0;

View File

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

View File

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

View File

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