mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
7df9157abd
commit
5b4702afc4
|
@ -1,6 +1,6 @@
|
|||
include nall/Makefile
|
||||
snes := snes
|
||||
profile := performance
|
||||
profile := accuracy
|
||||
ui := qt
|
||||
|
||||
# compiler
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,7 +15,7 @@ struct {
|
|||
uint16 icgramaddr;
|
||||
|
||||
//$2100 INIDISP
|
||||
bool display_disabled;
|
||||
bool display_disable;
|
||||
unsigned display_brightness;
|
||||
|
||||
//$2102 OAMADDL
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue