mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-18 12:01:28 +02:00
Update to v068r14 release.
byuu says: Holy hell, that was a total brain twister. After hours of crazy bit twiddling and debug printf's, I finally figured out how to allow both lores and hires scrolling in the accurate PPU renderer. In the process, I modified the main loop to run from -7 to 255, regardless of the hires setting, and perform X adjustment inside the tile fetching. This fixed a strange main/subscreen misalignment issue, so I was able to restore the proper sub-then-main rendering for the final screen output stage. Code looks a good bit cleaner this way overall. I also added load state and save state menus to the tools menu, so you can use the menubar to load and save to ten slots. I am thinking that I should nuke the icons. As pretty as they are, it's getting tiresome trying to find icons for everything, I have no pictures to represent loading or saving a slot, nor to represent individual slots. I'll just stick to radios and checkboxes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
include nall/Makefile
|
include nall/Makefile
|
||||||
snes := snes
|
snes := snes
|
||||||
profile := performance
|
profile := accuracy
|
||||||
ui := qt
|
ui := qt
|
||||||
|
|
||||||
# compiler
|
# compiler
|
||||||
|
@@ -174,6 +174,24 @@ MainWindow::MainWindow() {
|
|||||||
|
|
||||||
tools->addSeparator();
|
tools->addSeparator();
|
||||||
|
|
||||||
|
tools_loadState = tools->addMenu("Load Quick State");
|
||||||
|
for(unsigned i = 0; i < 10; i++) {
|
||||||
|
QAction *loadAction = new QAction(string("Slot ", i + 1), 0);
|
||||||
|
loadAction->setData(i);
|
||||||
|
connect(loadAction, SIGNAL(triggered()), this, SLOT(loadState()));
|
||||||
|
tools_loadState->addAction(loadAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
tools_saveState = tools->addMenu("Save Quick State");
|
||||||
|
for(unsigned i = 0; i < 10; i++) {
|
||||||
|
QAction *saveAction = new QAction(string("Slot ", i + 1), 0);
|
||||||
|
saveAction->setData(i);
|
||||||
|
connect(saveAction, SIGNAL(triggered()), this, SLOT(saveState()));
|
||||||
|
tools_saveState->addAction(saveAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
tools->addSeparator();
|
||||||
|
|
||||||
tools_cheatEditor = tools->addAction("Cheat &Editor ...");
|
tools_cheatEditor = tools->addAction("Cheat &Editor ...");
|
||||||
tools_cheatEditor->setIcon(QIcon(":/16x16/accessories-text-editor.png"));
|
tools_cheatEditor->setIcon(QIcon(":/16x16/accessories-text-editor.png"));
|
||||||
|
|
||||||
@@ -581,6 +599,20 @@ void MainWindow::saveScreenshot() {
|
|||||||
interface.saveScreenshot = true;
|
interface.saveScreenshot = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::loadState() {
|
||||||
|
QAction *action = dynamic_cast<QAction*>(sender());
|
||||||
|
if(action == 0) return;
|
||||||
|
unsigned slot = action->data().toUInt();
|
||||||
|
state.load(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::saveState() {
|
||||||
|
QAction *action = dynamic_cast<QAction*>(sender());
|
||||||
|
if(action == 0) return;
|
||||||
|
unsigned slot = action->data().toUInt();
|
||||||
|
state.save(slot);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::showCheatEditor() { toolsWindow->tab->setCurrentIndex(0); toolsWindow->show(); }
|
void MainWindow::showCheatEditor() { toolsWindow->tab->setCurrentIndex(0); toolsWindow->show(); }
|
||||||
void MainWindow::showCheatFinder() { toolsWindow->tab->setCurrentIndex(1); toolsWindow->show(); }
|
void MainWindow::showCheatFinder() { toolsWindow->tab->setCurrentIndex(1); toolsWindow->show(); }
|
||||||
void MainWindow::showStateManager() { toolsWindow->tab->setCurrentIndex(2); toolsWindow->show(); }
|
void MainWindow::showStateManager() { toolsWindow->tab->setCurrentIndex(2); toolsWindow->show(); }
|
||||||
|
@@ -80,6 +80,8 @@ public:
|
|||||||
QAction *tools_movies_recordFromPowerOn;
|
QAction *tools_movies_recordFromPowerOn;
|
||||||
QAction *tools_movies_recordFromHere;
|
QAction *tools_movies_recordFromHere;
|
||||||
QAction *tools_captureScreenshot;
|
QAction *tools_captureScreenshot;
|
||||||
|
QMenu *tools_loadState;
|
||||||
|
QMenu *tools_saveState;
|
||||||
QAction *tools_cheatEditor;
|
QAction *tools_cheatEditor;
|
||||||
QAction *tools_cheatFinder;
|
QAction *tools_cheatFinder;
|
||||||
QAction *tools_stateManager;
|
QAction *tools_stateManager;
|
||||||
@@ -150,6 +152,8 @@ public slots:
|
|||||||
void recordMovieFromPowerOn();
|
void recordMovieFromPowerOn();
|
||||||
void recordMovieFromHere();
|
void recordMovieFromHere();
|
||||||
void saveScreenshot();
|
void saveScreenshot();
|
||||||
|
void loadState();
|
||||||
|
void saveState();
|
||||||
void showCheatEditor();
|
void showCheatEditor();
|
||||||
void showCheatFinder();
|
void showCheatFinder();
|
||||||
void showStateManager();
|
void showStateManager();
|
||||||
|
@@ -7,9 +7,9 @@ void PPU::Background::frame() {
|
|||||||
|
|
||||||
void PPU::Background::scanline() {
|
void PPU::Background::scanline() {
|
||||||
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
|
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
|
||||||
x = -8 << hires;
|
x = -7;
|
||||||
y = self.vcounter();
|
y = self.vcounter();
|
||||||
edge = 7 - (regs.hoffset & 7);
|
tile_counter = (7 - (regs.hoffset & 7)) << hires;
|
||||||
for(unsigned n = 0; n < 8; n++) data[n] = 0;
|
for(unsigned n = 0; n < 8; n++) data[n] = 0;
|
||||||
|
|
||||||
if(self.vcounter() == 1) {
|
if(self.vcounter() == 1) {
|
||||||
@@ -36,7 +36,7 @@ void PPU::Background::get_tile() {
|
|||||||
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 = 256 << hires;
|
||||||
|
|
||||||
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;
|
||||||
@@ -45,7 +45,7 @@ void PPU::Background::get_tile() {
|
|||||||
mask_x--;
|
mask_x--;
|
||||||
mask_y--;
|
mask_y--;
|
||||||
|
|
||||||
unsigned px = x;
|
unsigned px = x << hires;
|
||||||
unsigned py = mosaic_voffset;
|
unsigned py = mosaic_voffset;
|
||||||
|
|
||||||
unsigned hscroll = regs.hoffset;
|
unsigned hscroll = regs.hoffset;
|
||||||
@@ -59,24 +59,24 @@ void PPU::Background::get_tile() {
|
|||||||
unsigned voffset = vscroll + py;
|
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 offset_x = (x + (hscroll & 7));
|
||||||
|
|
||||||
if(opt_x >= 8) {
|
if(offset_x >= 8) {
|
||||||
unsigned hval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0);
|
unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0);
|
||||||
unsigned vval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8);
|
unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8);
|
||||||
unsigned opt_valid_bit = (id == ID::BG1 ? 0x2000 : 0x4000);
|
unsigned valid_mask = (id == ID::BG1 ? 0x2000 : 0x4000);
|
||||||
|
|
||||||
if(self.regs.bgmode == 4) {
|
if(self.regs.bgmode == 4) {
|
||||||
if(hval & opt_valid_bit) {
|
if(hval & valid_mask) {
|
||||||
if(!(hval & 0x8000)) {
|
if((hval & 0x8000) == 0) {
|
||||||
hoffset = opt_x + (hval & ~7);
|
hoffset = offset_x + (hval & ~7);
|
||||||
} else {
|
} else {
|
||||||
voffset = y + hval;
|
voffset = y + hval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(hval & opt_valid_bit) hoffset = opt_x + (hval & ~7);
|
if(hval & valid_mask) hoffset = offset_x + (hval & ~7);
|
||||||
if(vval & opt_valid_bit) voffset = y + vval;
|
if(vval & valid_mask) voffset = y + vval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,8 @@ void PPU::Background::get_tile() {
|
|||||||
hoffset &= mask_x;
|
hoffset &= mask_x;
|
||||||
voffset &= mask_y;
|
voffset &= mask_y;
|
||||||
|
|
||||||
unsigned screen_x = (regs.screen_size & 1 ? (32 << 5) : 0);
|
unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0);
|
||||||
unsigned screen_y = (regs.screen_size & 2 ? (32 << 5) : 0);
|
unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0);
|
||||||
if(regs.screen_size == 3) screen_y <<= 1;
|
if(regs.screen_size == 3) screen_y <<= 1;
|
||||||
|
|
||||||
unsigned tx = hoffset >> tile_width;
|
unsigned tx = hoffset >> tile_width;
|
||||||
@@ -126,21 +126,21 @@ void PPU::Background::get_tile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(mirror_x) for(unsigned n = 0; n < 8; n++) {
|
if(mirror_x) for(unsigned n = 0; n < 8; n++) {
|
||||||
|
//reverse data bits in data[n]: 01234567 -> 76543210
|
||||||
data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0);
|
data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0);
|
||||||
data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc);
|
data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc);
|
||||||
data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa);
|
data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::Background::run() {
|
void PPU::Background::run(bool screen) {
|
||||||
if(self.vcounter() == 0) return;
|
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) {
|
if(screen == Screen::Sub) {
|
||||||
output.main.priority = 0;
|
output.main.priority = 0;
|
||||||
output.sub.priority = 0;
|
output.sub.priority = 0;
|
||||||
} else if(hires == false) {
|
if(hires == false) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(regs.mode == Mode::Inactive) return;
|
if(regs.mode == Mode::Inactive) return;
|
||||||
@@ -148,7 +148,11 @@ void PPU::Background::run() {
|
|||||||
|
|
||||||
if(regs.mode == Mode::Mode7) return run_mode7();
|
if(regs.mode == Mode::Mode7) return run_mode7();
|
||||||
|
|
||||||
if((x++ & 7) == edge) get_tile();
|
if(tile_counter-- == 0) {
|
||||||
|
tile_counter = 7;
|
||||||
|
get_tile();
|
||||||
|
}
|
||||||
|
if(screen == Screen::Main) x++;
|
||||||
|
|
||||||
uint8 palette = get_tile_color();
|
uint8 palette = get_tile_color();
|
||||||
if(x >= 0 && mosaic_hcounter++ >= regs.mosaic) {
|
if(x >= 0 && mosaic_hcounter++ >= regs.mosaic) {
|
||||||
@@ -169,19 +173,17 @@ void PPU::Background::run() {
|
|||||||
output.sub.palette = palette_index + mosaic_palette;
|
output.sub.palette = palette_index + mosaic_palette;
|
||||||
output.sub.tile = tile;
|
output.sub.tile = tile;
|
||||||
}
|
}
|
||||||
} else {
|
} else if(screen == Screen::Main) {
|
||||||
if(x & 1) {
|
if(regs.main_enable) {
|
||||||
if(regs.main_enable) {
|
output.main.priority = priority;
|
||||||
output.main.priority = priority;
|
output.main.palette = palette_index + mosaic_palette;
|
||||||
output.main.palette = palette_index + mosaic_palette;
|
output.main.tile = tile;
|
||||||
output.main.tile = tile;
|
}
|
||||||
}
|
} else if(screen == Screen::Sub) {
|
||||||
} else {
|
if(regs.sub_enable) {
|
||||||
if(regs.sub_enable) {
|
output.sub.priority = priority;
|
||||||
output.sub.priority = priority;
|
output.sub.palette = palette_index + mosaic_palette;
|
||||||
output.sub.palette = palette_index + mosaic_palette;
|
output.sub.tile = tile;
|
||||||
output.sub.tile = tile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +228,6 @@ void PPU::Background::reset() {
|
|||||||
|
|
||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
edge = 0;
|
|
||||||
|
|
||||||
mosaic_vcounter = 0;
|
mosaic_vcounter = 0;
|
||||||
mosaic_voffset = 0;
|
mosaic_voffset = 0;
|
||||||
@@ -234,6 +235,7 @@ void PPU::Background::reset() {
|
|||||||
mosaic_hoffset = 0;
|
mosaic_hoffset = 0;
|
||||||
mosaic_palette = 0;
|
mosaic_palette = 0;
|
||||||
|
|
||||||
|
tile_counter = 0;
|
||||||
tile = 0;
|
tile = 0;
|
||||||
priority = 0;
|
priority = 0;
|
||||||
palette_number = 0;
|
palette_number = 0;
|
||||||
|
@@ -5,6 +5,7 @@ class Background {
|
|||||||
struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
||||||
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 Screen { enum { Main, Sub }; };
|
||||||
|
|
||||||
struct Regs {
|
struct Regs {
|
||||||
unsigned tiledata_addr;
|
unsigned tiledata_addr;
|
||||||
@@ -35,7 +36,6 @@ class Background {
|
|||||||
struct {
|
struct {
|
||||||
signed x;
|
signed x;
|
||||||
signed y;
|
signed y;
|
||||||
signed edge;
|
|
||||||
|
|
||||||
unsigned mosaic_vcounter;
|
unsigned mosaic_vcounter;
|
||||||
unsigned mosaic_voffset;
|
unsigned mosaic_voffset;
|
||||||
@@ -43,6 +43,7 @@ class Background {
|
|||||||
unsigned mosaic_hoffset;
|
unsigned mosaic_hoffset;
|
||||||
unsigned mosaic_palette;
|
unsigned mosaic_palette;
|
||||||
|
|
||||||
|
unsigned tile_counter;
|
||||||
unsigned tile;
|
unsigned tile;
|
||||||
unsigned priority;
|
unsigned priority;
|
||||||
unsigned palette_number;
|
unsigned palette_number;
|
||||||
@@ -52,7 +53,7 @@ class Background {
|
|||||||
|
|
||||||
void frame();
|
void frame();
|
||||||
void scanline();
|
void scanline();
|
||||||
void run();
|
void run(bool screen);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void get_tile();
|
void get_tile();
|
||||||
|
@@ -16,7 +16,7 @@ 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);
|
||||||
|
|
||||||
if(++Background::x & ~255) return;
|
if(Background::x++ & ~255) return;
|
||||||
unsigned x = mosaic_hoffset;
|
unsigned x = mosaic_hoffset;
|
||||||
unsigned y = self.bg1.mosaic_voffset; //BG2 vertical mosaic uses BG1 mosaic size
|
unsigned y = self.bg1.mosaic_voffset; //BG2 vertical mosaic uses BG1 mosaic size
|
||||||
|
|
||||||
|
@@ -43,16 +43,16 @@ void PPU::enter() {
|
|||||||
if(vcounter() <= (!regs.overscan ? 224 : 239)) {
|
if(vcounter() <= (!regs.overscan ? 224 : 239)) {
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
for(unsigned pixel = 1; pixel < 8 + 256; pixel++) {
|
for(unsigned pixel = 1; pixel < 8 + 256; pixel++) {
|
||||||
bg1.run();
|
bg1.run(1);
|
||||||
bg2.run();
|
bg2.run(1);
|
||||||
bg3.run();
|
bg3.run(1);
|
||||||
bg4.run();
|
bg4.run(1);
|
||||||
add_clocks(2);
|
add_clocks(2);
|
||||||
|
|
||||||
bg1.run();
|
bg1.run(0);
|
||||||
bg2.run();
|
bg2.run(0);
|
||||||
bg3.run();
|
bg3.run(0);
|
||||||
bg4.run();
|
bg4.run(0);
|
||||||
if(pixel >= 8) {
|
if(pixel >= 8) {
|
||||||
oam.run();
|
oam.run();
|
||||||
window.run();
|
window.run();
|
||||||
|
@@ -8,13 +8,13 @@ void PPU::Screen::scanline() {
|
|||||||
void PPU::Screen::run() {
|
void PPU::Screen::run() {
|
||||||
uint16 color;
|
uint16 color;
|
||||||
if(self.regs.pseudo_hires == false && self.regs.bgmode != 5 && self.regs.bgmode != 6) {
|
if(self.regs.pseudo_hires == false && self.regs.bgmode != 5 && self.regs.bgmode != 6) {
|
||||||
color = get_pixel(false);
|
color = get_pixel(0);
|
||||||
*output++ = color;
|
*output++ = color;
|
||||||
*output++ = color;
|
*output++ = color;
|
||||||
} else {
|
} else {
|
||||||
color = get_pixel(false);
|
color = get_pixel(1);
|
||||||
*output++ = color;
|
*output++ = color;
|
||||||
color = get_pixel(true);
|
color = get_pixel(0);
|
||||||
*output++ = color;
|
*output++ = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -115,7 +115,6 @@ void PPU::Background::serialize(serializer &s) {
|
|||||||
|
|
||||||
s.integer(x);
|
s.integer(x);
|
||||||
s.integer(y);
|
s.integer(y);
|
||||||
s.integer(edge);
|
|
||||||
|
|
||||||
s.integer(mosaic_vcounter);
|
s.integer(mosaic_vcounter);
|
||||||
s.integer(mosaic_voffset);
|
s.integer(mosaic_voffset);
|
||||||
@@ -123,6 +122,7 @@ void PPU::Background::serialize(serializer &s) {
|
|||||||
s.integer(mosaic_hoffset);
|
s.integer(mosaic_hoffset);
|
||||||
s.integer(mosaic_palette);
|
s.integer(mosaic_palette);
|
||||||
|
|
||||||
|
s.integer(tile_counter);
|
||||||
s.integer(tile);
|
s.integer(tile);
|
||||||
s.integer(priority);
|
s.integer(priority);
|
||||||
s.integer(palette_number);
|
s.integer(palette_number);
|
||||||
|
@@ -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.13";
|
static const char Version[] = "068.14";
|
||||||
static const unsigned SerializerVersion = 13;
|
static const unsigned SerializerVersion = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user