Update to v084 release.

byuu says:

This release adds preliminary Game Boy Color emulation. Due to lack of
technical information, this is undoubtedly the least stable module
I provide at this time; but improvements should continue as it is
developed.

This release also polishes the NES emulation and user interface code.

Changelog (since v083):
- added preliminary Game Boy Color emulation
- NES: added MMC6, VRC1, VRC2, VRC3 emulation
- NES: fixed MMC5 banking and added split-screen support [Cydrak]
- NES: pass all of blargg's ppu_vbl_nmi tests, pass more sprite tests
- NES: palette is now generated algorithmically [Bisqwit]
- SNES: fixed SA-1 IRQ regression caused by code refactoring
- Game Boy: rewrote audio channel mixing code; sound output is greatly
  improved as a result
- Game Boy: uses DMG boot ROM instead of SGB boot ROM
- Game Boy: fixed potential bug when loading save states
- phoenix: fixed ListView focus issue [X-Fi6]
- phoenix: fixed dialog message parsing [X-Fi6]
- ui: video output is truly 24-bit now; SNES luma=0 edge case emulated
- ui: audio frequency, latency, resampler are now user configurable
- ui: gamma ramp is dynamically adjustable
- ui: all filters ported to 24-bit mode (speed hit to HQ2x)
- ui: added turbo button mappings for all generic controllers
- ui: fixed audio volume on unmute via menu [Ver Greeneyes]
- ui: shrink window option does nothing when no cartridge is loaded
- ui: re-added compositor disable, driver verification from v082
This commit is contained in:
Tim Allen 2011-11-08 00:04:58 +11:00
parent 891f1ab7af
commit 01750e9c83
10 changed files with 312 additions and 50 deletions

View File

@ -1,17 +1,29 @@
struct KonamiVRC2 : Board {
struct Settings {
struct Pinout {
unsigned a0;
unsigned a1;
} pinout;
} settings;
VRC2 vrc2;
bool latch;
uint8 prg_read(unsigned addr) {
if(addr == 0x6000) return latch;
if(addr & 0x8000) return prgrom.read(vrc2.prg_addr(addr));
return cpu.mdr();
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return vrc2.ram_read(addr);
return prgrom.read(vrc2.prg_addr(addr));
}
void prg_write(unsigned addr, uint8 data) {
if(addr == 0x6000) latch = data & 0x01;
if(addr & 0x8000) return vrc2.reg_write(addr, data);
if(addr < 0x6000) return;
if(addr < 0x8000) return vrc2.ram_write(addr, data);
bool a0 = (addr & settings.pinout.a0);
bool a1 = (addr & settings.pinout.a1);
addr &= 0xfff0;
addr |= (a0 << 0) | (a1 << 1);
return vrc2.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
@ -30,16 +42,16 @@ void power() {
void reset() {
vrc2.reset();
latch = 0;
}
void serialize(serializer &s) {
Board::serialize(s);
vrc2.serialize(s);
s.integer(latch);
}
KonamiVRC2(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc2(*this) {
settings.pinout.a0 = 1 << decimal(board["chip"]["pinout"]["a0"].value);
settings.pinout.a1 = 1 << decimal(board["chip"]["pinout"]["a1"].value);
}
};

View File

@ -1,16 +1,17 @@
struct VRC2 : Chip {
uint4 prg_bank[2];
uint5 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
bool latch;
unsigned prg_addr(unsigned addr) const {
unsigned bank;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = 0x0e; break;
case 0xe000: bank = 0x0f; break;
case 0xc000: bank = 0x1e; break;
case 0xe000: bank = 0x1f; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
@ -30,10 +31,26 @@ unsigned ciram_addr(unsigned addr) const {
throw;
}
uint8 ram_read(unsigned addr) {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
return cpu.mdr();
}
return board.prgram.read(addr & 0x1fff);
}
void ram_write(unsigned addr, uint8 data) {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
return;
}
return board.prgram.write(addr & 0x1fff, data);
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x0f;
prg_bank[0] = data & 0x1f;
break;
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
@ -41,7 +58,7 @@ void reg_write(unsigned addr, uint8 data) {
break;
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
prg_bank[1] = data & 0x0f;
prg_bank[1] = data & 0x1f;
break;
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
@ -77,12 +94,14 @@ void reset() {
for(auto &n : prg_bank) n = 0;
for(auto &n : chr_bank) n = 0;
mirror = 0;
latch = 0;
}
void serialize(serializer &s) {
for(auto &n : prg_bank) s.integer(n);
for(auto &n : chr_bank) s.integer(n);
s.integer(mirror);
s.integer(latch);
}
VRC2(Board &board) : Chip(board) {

View File

@ -49,6 +49,32 @@ void main() {
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
switch(addr & 0xe000) {
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 0xe000: bank = banks - 1; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
@ -117,32 +143,6 @@ void reg_write(unsigned addr, uint8 data) {
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
switch((addr / 0x2000) & 3) {
case 0: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 1: bank = prg_bank[1]; break;
case 2: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 3: bank = banks - 1; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
throw;
}
void power() {
}

View File

@ -14,7 +14,7 @@ static string iNES(const uint8_t *data, unsigned size) {
unsigned prgram = 0;
unsigned chrram = chrrom == 0 ? 8192 : 0;
print("iNES mapper: ", mapper, "\n");
//print("iNES mapper: ", mapper, "\n");
output.append("cartridge\n");
@ -81,9 +81,6 @@ static string iNES(const uint8_t *data, unsigned size) {
case 21:
case 23:
case 25:
//VRC2
//output.append("\tboard type:KONAMI-VRC-2\n");
//output.append("\t\tchip type:VRC2\n");
//VRC4
output.append("\tboard type:KONAMI-VRC-4\n");
output.append("\t\tchip type:VRC4\n");
@ -91,6 +88,13 @@ static string iNES(const uint8_t *data, unsigned size) {
prgram = 8192;
break;
case 22:
//VRC2
output.append("\tboard type:KONAMI-VRC-2\n");
output.append("\t\tchip type:VRC2\n");
output.append("\t\t\tpinout a0=0 a1=1\n");
break;
case 24:
output.append("\tboard type:KONAMI-VRC-6\n");
output.append("\t\tchip type:VRC6\n");
@ -140,7 +144,7 @@ static string iNES(const uint8_t *data, unsigned size) {
output.append("\t\tprg rom=", prgrom, " ram=", prgram, "\n");
output.append("\t\tchr rom=", chrrom, " ram=", chrram, "\n");
print(output, "\n");
//print(output, "\n");
return output;
}

View File

@ -39,6 +39,7 @@ FileBrowser::FileBrowser() {
setPath(path);
};
fileList.onChange = { &FileBrowser::synchronize, this };
fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this };
filterModes.append({ "Default", "", { "*" } });
@ -53,6 +54,11 @@ FileBrowser::FileBrowser() {
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
config.load(application->path("paths.cfg"));
config.save(application->path("paths.cfg"));
synchronize();
}
void FileBrowser::synchronize() {
openButton.setEnabled(fileList.selected());
}
FileBrowser::~FileBrowser() {
@ -103,6 +109,7 @@ void FileBrowser::setPath(const string &path) {
for(auto &fileName : fileNameList) fileList.append(fileName);
fileList.setSelection(0);
fileList.setFocused();
synchronize();
}
void FileBrowser::fileListActivate() {

View File

@ -27,6 +27,7 @@ private:
lstring fileNameList;
function<void (string)> callback;
void synchronize();
void setPath(const string &path);
void fileListActivate();
bool loadFolder(const string &path);

View File

@ -297,7 +297,7 @@ MainWindow::MainWindow() {
settingsMuteAudio.onTick = [&] {
config->audio.mute = settingsMuteAudio.checked();
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
};
settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); };

View File

@ -27,7 +27,7 @@ void Application::run() {
}
Application::Application(int argc, char **argv) {
title = "bsnes v083.10";
title = "bsnes v084";
application = this;
quit = false;
@ -76,7 +76,11 @@ Application::Application(int argc, char **argv) {
video.driver(config->video.driver);
video.set(Video::Handle, mainWindow->viewport.handle());
video.set(Video::Synchronize, config->video.synchronize);
video.init();
if(video.init() == false) {
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->video.driver, " video driver." });
video.driver("None");
video.init();
}
utility->bindVideoFilter();
utility->bindVideoShader();
@ -85,17 +89,25 @@ Application::Application(int argc, char **argv) {
audio.set(Audio::Synchronize, config->audio.synchronize);
audio.set(Audio::Latency, config->audio.latency);
audio.set(Audio::Frequency, config->audio.frequency);
audio.init();
if(audio.init() == false) {
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->audio.driver, " audio driver." });
audio.driver("None");
audio.init();
}
dspaudio.setPrecision(16);
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
dspaudio.setBalance(0.0);
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
dspaudio.setResamplerFrequency(config->audio.frequency);
input.driver(config->input.driver);
input.set(Input::Handle, mainWindow->viewport.handle());
input.init();
if(input.init() == false) {
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->input.driver, " input driver." });
input.driver("None");
input.init();
}
if(config->video.startFullScreen) utility->toggleFullScreen();
if(argc == 2) interface->loadCartridge(argv[1]);

View File

@ -8,6 +8,7 @@ void Utility::setMode(Interface::Mode mode) {
mainWindow->nesMenu.setVisible(false);
mainWindow->snesMenu.setVisible(false);
mainWindow->gameBoyMenu.setVisible(false);
mainWindow->viewport.setVisible(mode != Interface::Mode::None);
if(mode == Interface::Mode::None) {
mainWindow->setTitle(application->title);

206
snesfilter/HQ2x/HQ2x-RGB555.cpp Executable file
View File

@ -0,0 +1,206 @@
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
using namespace nall;
extern "C" {
void filter_size(unsigned&, unsigned&);
void filter_render(uint16_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned);
};
enum {
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407,
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0,
};
uint32_t *yuvTable;
uint8_t rotate[256];
const uint8_t hqTable[256] = {
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
};
static void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
yuvTable = new uint32_t[32768];
for(unsigned i = 0; i < 32768; i++) {
uint8_t R = (i >> 0) & 31;
uint8_t G = (i >> 5) & 31;
uint8_t B = (i >> 10) & 31;
//bgr555->bgr888
double r = (R << 3) | (R >> 2);
double g = (G << 3) | (G >> 2);
double b = (B << 3) | (B >> 2);
//bgr888->yuv
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
}
//counter-clockwise rotation table; one revolution:
//123 369 12346789
//4.6 -> 2.8 =
//789 147 36928147
for(unsigned n = 0; n < 256; n++) {
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
}
}
static void terminate() {
delete[] yuvTable;
}
static bool same(uint16_t x, uint16_t y) {
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
}
static bool diff(uint32_t x, uint16_t y) {
return ((x - yuvTable[y]) & diff_mask);
}
static void grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
static uint16_t pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
static uint16_t blend1(uint32_t A, uint32_t B) {
grow(A); grow(B);
return pack((A * 3 + B) >> 2);
}
static uint16_t blend2(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 2 + B + C) >> 2);
}
static uint16_t blend3(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 5 + B * 2 + C) >> 3);
}
static uint16_t blend4(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 6 + B + C) >> 3);
}
static uint16_t blend5(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 2 + (B + C) * 3) >> 3);
}
static uint16_t blend6(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 14 + B + C) >> 4);
}
static uint16_t blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
switch(rule) { default:
case 0: return E;
case 1: return blend1(E, A);
case 2: return blend1(E, D);
case 3: return blend1(E, B);
case 4: return blend2(E, D, B);
case 5: return blend2(E, A, B);
case 6: return blend2(E, A, D);
case 7: return blend3(E, B, D);
case 8: return blend3(E, D, B);
case 9: return blend4(E, D, B);
case 10: return blend5(E, D, B);
case 11: return blend6(E, D, B);
case 12: return same(B, D) ? blend2(E, D, B) : E;
case 13: return same(B, D) ? blend5(E, D, B) : E;
case 14: return same(B, D) ? blend6(E, D, B) : E;
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
}
}
dllexport void filter_size(unsigned &width, unsigned &height) {
initialize();
width *= 2;
height *= 2;
}
dllexport void filter_render(
uint16_t *output, unsigned outputPitch,
const uint16_t *input, unsigned inputPitch,
unsigned width, unsigned height
) {
initialize();
outputPitch >>= 1, inputPitch >>= 1;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
const uint16_t *in = input + y * inputPitch;
uint16_t *out0 = output + y * outputPitch * 2;
uint16_t *out1 = output + y * outputPitch * 2 + outputPitch;
int prevline = (y == 0 ? 0 : inputPitch);
int nextline = (y == height - 1 ? 0 : inputPitch);
in++;
*out0++ = 0; *out0++ = 0;
*out1++ = 0; *out1++ = 0;
for(unsigned x = 1; x < width - 1; x++) {
uint16_t A = *(in - prevline - 1);
uint16_t B = *(in - prevline + 0);
uint16_t C = *(in - prevline + 1);
uint16_t D = *(in - 1);
uint16_t E = *(in + 0);
uint16_t F = *(in + 1);
uint16_t G = *(in + nextline - 1);
uint16_t H = *(in + nextline + 0);
uint16_t I = *(in + nextline + 1);
uint32_t e = yuvTable[E] + diff_offset;
uint8_t pattern;
pattern = diff(e, A) << 0;
pattern |= diff(e, B) << 1;
pattern |= diff(e, C) << 2;
pattern |= diff(e, D) << 3;
pattern |= diff(e, F) << 4;
pattern |= diff(e, G) << 5;
pattern |= diff(e, H) << 6;
pattern |= diff(e, I) << 7;
*(out0 + 0) = blend(hqTable[pattern], E, A, B, D, F, H); pattern = rotate[pattern];
*(out0 + 1) = blend(hqTable[pattern], E, C, F, B, H, D); pattern = rotate[pattern];
*(out1 + 1) = blend(hqTable[pattern], E, I, H, F, D, B); pattern = rotate[pattern];
*(out1 + 0) = blend(hqTable[pattern], E, G, D, H, B, F);
in++;
out0 += 2;
out1 += 2;
}
in++;
*out0++ = 0; *out0++ = 0;
*out1++ = 0; *out1++ = 0;
}
}