Update to v093r06 release.

byuu says:

Changelog:
- Windows port should compile out-of-the-box
- InputManager::scancode[] initialized at startup
- Library menu shows item for each bootable media type (notably Game Boy
  Color)
- Display Emulation menu selection fix
- LibraryManager load button works now
- Added hotkey to show library manager (defaults to L)
- Added color emulation to video settings (missing on GBA for now)
- SFC loading SGB without GB cartridge no longer segfaults
- GB/GBC system.load() after cartridge.load()
- GB/GBC BG-over-OAM fix
- GB/GBC disallow up+down and left+right
This commit is contained in:
Tim Allen 2013-12-07 20:12:37 +11:00
parent ed4e87f65e
commit 35f1605829
51 changed files with 308 additions and 172 deletions

View File

@ -6,14 +6,14 @@ flags := $(flags) -O3 -fomit-frame-pointer -I..
all:
$(compiler) $(cppflags) $(flags) -fPIC -o obj/ananke.o -c ananke.cpp
ifeq ($(platform),x)
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
else ifeq ($(platform),osx)
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
else ifeq ($(platform),win)
ifeq ($(platform),windows)
$(compiler) $(phoenixflags) -fPIC -o obj/phoenix.o -c ../phoenix/phoenix.cpp
$(compiler) $(link) -shared -o phoenix.dll obj/phoenix.o $(phoenixlink)
$(compiler) $(link) -shared -o ananke.dll obj/ananke.o -L. -lphoenix
else ifeq ($(platform),macosx)
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
else
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
endif
resource: force
@ -24,23 +24,25 @@ clean:
-@$(call delete,*.so)
install: uninstall
ifeq ($(platform),x)
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
sudo cp libananke.so $(path)/libananke.so.1
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
else ifeq ($(platform),osx)
ifeq ($(platform),windows)
else ifeq ($(platform),macosx)
if [ ! -d ~/Library/Application\ Support/ananke ]; then mkdir ~/Library/Application\ Support/ananke; fi
sudo cp libananke.dylib $(path)/libananke.1.dylib
sudo ln -s $(path)/libananke.1.dylib $(path)/libananke.dylib
else
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
sudo cp libananke.so $(path)/libananke.so.1
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
endif
uninstall:
ifeq ($(platform),x)
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
else ifeq ($(platform),osx)
ifeq ($(platform),windows)
else ifeq ($(platform),macosx)
if [ -f $(path)/libananke.dylib ]; then sudo rm $(path)/libananke.dylib; fi
if [ -f $(path)/libananke.1.dylib ]; then sudo rm $(path)/libananke.1.dylib; fi
else
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
endif
force:

View File

@ -95,7 +95,7 @@ FileDialog *fileDialog = nullptr;
Ananke::Ananke() {
libraryPath = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").replace("\\", "/");
if(libraryPath.empty()) libraryPath = {userpath(), "Emulation/"};
if(libraryPath.endswith("/") == false) libraryPath.append("/");
if(libraryPath.endsWith("/") == false) libraryPath.append("/");
}
bool Ananke::supported(const string &filename) {
@ -133,7 +133,7 @@ string Ananke::open(string filename) {
config.path = information.path; //remember last used directory
vector<uint8_t> buffer;
if(filename.endswith(".zip")) {
if(filename.endsWith(".zip")) {
information.archive = filename;
buffer = extractROM();
} else {
@ -143,23 +143,23 @@ string Ananke::open(string filename) {
applyBeatPatch(buffer);
if(information.name.endswith(".fc") || information.name.endswith(".nes")) return openFamicom(buffer);
if(information.name.endswith(".sfc") || information.name.endswith(".smc")) return openSuperFamicom(buffer);
if(information.name.endswith(".st")) return openSufamiTurbo(buffer);
if(information.name.endswith(".bs")) return openBsxSatellaview(buffer);
if(information.name.endswith(".gb") || information.name.endswith(".gbc")) return openGameBoy(buffer);
if(information.name.endswith(".gba")) return openGameBoyAdvance(buffer);
if(information.name.endsWith(".fc") || information.name.endsWith(".nes")) return openFamicom(buffer);
if(information.name.endsWith(".sfc") || information.name.endsWith(".smc")) return openSuperFamicom(buffer);
if(information.name.endsWith(".st")) return openSufamiTurbo(buffer);
if(information.name.endsWith(".bs")) return openBsxSatellaview(buffer);
if(information.name.endsWith(".gb") || information.name.endsWith(".gbc")) return openGameBoy(buffer);
if(information.name.endsWith(".gba")) return openGameBoyAdvance(buffer);
return "";
}
string Ananke::sync(string pathname) {
if(pathname.endswith(".fc/")) return syncFamicom(pathname);
if(pathname.endswith(".sfc/")) return syncSuperFamicom(pathname);
if(pathname.endswith(".st/")) return syncSufamiTurbo(pathname);
if(pathname.endswith(".bs/")) return syncBsxSatellaview(pathname);
if(pathname.endswith(".gb/")) return syncGameBoy(pathname);
if(pathname.endswith(".gbc/")) return syncGameBoy(pathname);
if(pathname.endswith(".gba/")) return syncGameBoyAdvance(pathname);
if(pathname.endsWith(".fc/")) return syncFamicom(pathname);
if(pathname.endsWith(".sfc/")) return syncSuperFamicom(pathname);
if(pathname.endsWith(".st/")) return syncSufamiTurbo(pathname);
if(pathname.endsWith(".bs/")) return syncBsxSatellaview(pathname);
if(pathname.endsWith(".gb/")) return syncGameBoy(pathname);
if(pathname.endsWith(".gbc/")) return syncGameBoy(pathname);
if(pathname.endsWith(".gba/")) return syncGameBoyAdvance(pathname);
return "";
}

View File

@ -1,12 +1,12 @@
vector<uint8_t> Ananke::extractROM() {
unzip archive;
if(archive.open(information.archive)) {
for(auto &file : archive.file) {
if(file.name.endswith(".fc") || file.name.endswith(".nes")
|| file.name.endswith(".sfc") || file.name.endswith(".smc")
|| file.name.endswith(".st") || file.name.endswith(".bs")
|| file.name.endswith(".gb") || file.name.endswith(".gbc")
|| file.name.endswith(".gba")
for(auto& file : archive.file) {
if(file.name.endsWith(".fc") || file.name.endsWith(".nes")
|| file.name.endsWith(".sfc") || file.name.endsWith(".smc")
|| file.name.endsWith(".st") || file.name.endsWith(".bs")
|| file.name.endsWith(".gb") || file.name.endsWith(".gbc")
|| file.name.endsWith(".gba")
) {
information.name = notdir(file.name);
return archive.extract(file);
@ -16,10 +16,10 @@ vector<uint8_t> Ananke::extractROM() {
return vector<uint8_t>();
}
vector<uint8_t> Ananke::extractFile(const string &filename) {
vector<uint8_t> Ananke::extractFile(const string& filename) {
unzip archive;
if(archive.open(information.archive)) {
for(auto &file : archive.file) {
for(auto& file : archive.file) {
if(notdir(file.name) == filename) {
return archive.extract(file);
}

View File

@ -21,7 +21,7 @@ struct FileDialog : Window {
void setPath(const string &path) {
pathname = string{path}.transform("\\", "/");
if(pathname.empty()) pathname = userpath();
if(pathname.endswith("/") == false) pathname.append("/");
if(pathname.endsWith("/") == false) pathname.append("/");
pathEdit.setText(pathname);
fileList.reset();
@ -88,7 +88,7 @@ struct FileDialog : Window {
string name = filenameList(fileList.selection());
if(name.empty()) return;
if(name.endswith("/")) return setPath(name);
if(name.endsWith("/")) return setPath(name);
filename = name;
onClose();
};

View File

@ -38,23 +38,23 @@ GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned s
char text[16];
memcpy(text, data + n, id.size + 3);
text[id.size + 3] = 0;
list.appendonce(text);
list.appendOnce(text);
}
}
}
}
identifiers = list.concatenate(",");
identifiers = list.merge(",");
markup = "";
markup.append("cartridge\n");
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
if(0);
else if(identifiers.beginswith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
else if(identifiers.beginswith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginswith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
else if(identifiers.beginsWith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
else if(identifiers.beginsWith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
else if(identifiers.beginsWith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
else if(identifiers.beginsWith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginsWith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
else if(identifiers.beginsWith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
}

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "093.05";
static const char Version[] = "093.06";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";

View File

@ -107,7 +107,7 @@ struct Interface {
virtual void cheatSet(const lstring& = lstring{}) {}
//utility functions
virtual void paletteUpdate() {}
virtual void paletteUpdate(bool colorEmulation) {}
//debugger functions
virtual bool tracerEnable(bool) { return false; }

View File

@ -115,8 +115,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate() {
video.generate_palette();
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
}
Interface::Interface() {

View File

@ -45,7 +45,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate();
void paletteUpdate(bool colorEmulation);
Interface();

View File

@ -6,12 +6,12 @@ namespace Famicom {
Video video;
void Video::generate_palette() {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8);
void Video::generate_palette(bool color_emulation) {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, color_emulation ? 1.8 : 2.2);
}
Video::Video() {
palette = new unsigned[1 << 9];
palette = new unsigned[1 << 9]();
}
Video::~Video() {

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette = nullptr;
void generate_palette();
void generate_palette(bool color_emulation);
Video();
~Video();

View File

@ -18,8 +18,22 @@ string Cartridge::title() {
return information.title;
}
void Cartridge::load(System::Revision revision) {
//intended for use with Super Game Boy for when no Game Boy cartridge is inserted
void Cartridge::load_empty(System::Revision revision) {
unload();
romsize = 32768;
romdata = allocate<uint8>(romsize, 0xff);
ramsize = 0;
mapper = &mbc0;
sha256 = nall::sha256(romdata, romsize);
loaded = true;
system.load(revision);
}
void Cartridge::load(System::Revision revision) {
unload();
system.revision = revision; //needed for ID::Manifest to return correct group ID
if(revision != System::Revision::SuperGameBoy) {
interface->loadRequest(ID::Manifest, "manifest.bml");
}
@ -80,15 +94,14 @@ void Cartridge::load(System::Revision revision) {
case Mapper::HuC3: mapper = &huc3; break;
}
loaded = true;
sha256 = nall::sha256(romdata, romsize);
loaded = true;
system.load(revision);
}
void Cartridge::unload() {
if(loaded == false) return;
if(romdata) { delete[] romdata; romdata = nullptr; }
if(ramdata) { delete[] ramdata; ramdata = nullptr; }
if(romdata) { delete[] romdata; romdata = nullptr; romsize = 0; }
if(ramdata) { delete[] ramdata; ramdata = nullptr; ramsize = 0; }
loaded = false;
}
@ -119,7 +132,7 @@ uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable) {
const uint8* data = nullptr;
switch(system.revision()) { default:
switch(system.revision) { default:
case System::Revision::GameBoy: data = system.bootROM.dmg; break;
case System::Revision::SuperGameBoy: data = system.bootROM.sgb; break;
case System::Revision::GameBoyColor: data = system.bootROM.cgb; break;
@ -159,8 +172,7 @@ void Cartridge::power() {
Cartridge::Cartridge() {
loaded = false;
romdata = nullptr;
ramdata = nullptr;
sha256 = "";
}
Cartridge::~Cartridge() {

View File

@ -45,15 +45,16 @@ struct Cartridge : MMIO, property<Cartridge> {
readonly<bool> loaded;
readonly<string> sha256;
uint8_t* romdata;
unsigned romsize;
uint8_t* romdata = nullptr;
unsigned romsize = 0;
uint8_t* ramdata;
unsigned ramsize;
uint8_t* ramdata = nullptr;
unsigned ramsize = 0;
MMIO* mapper;
bool bootrom_enable;
MMIO* mapper = nullptr;
bool bootrom_enable = true;
void load_empty(System::Revision revision);
void load(System::Revision revision);
void unload();

View File

@ -20,6 +20,10 @@ void CPU::mmio_joyp_poll() {
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Left) << 1;
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Right) << 0;
//D-pad pivot makes it impossible to press opposing directions at the same time
if(dpad & 4) dpad &= ~8; //disallow up+down
if(dpad & 2) dpad &= ~1; //disallow left+right
status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
@ -32,6 +36,7 @@ uint8 CPU::mmio_read(uint16 addr) {
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
if(addr == 0xff00) { //JOYP
mmio_joyp_poll();
return (status.p15 << 5)
| (status.p14 << 4)
| (status.joyp << 0);
@ -134,7 +139,6 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
status.p15 = data & 0x20;
status.p14 = data & 0x10;
interface->joypWrite(status.p15, status.p14);
mmio_joyp_poll();
return;
}

View File

@ -41,12 +41,13 @@ unsigned Interface::group(unsigned id) {
case ID::Manifest:
case ID::ROM:
case ID::RAM:
if(system.revision() == System::Revision::GameBoy) return ID::GameBoy;
if(system.revision() == System::Revision::SuperGameBoy) return ID::SuperGameBoy;
if(system.revision() == System::Revision::GameBoyColor) return ID::GameBoyColor;
switch(system.revision) {
case System::Revision::GameBoy: return ID::GameBoy;
case System::Revision::SuperGameBoy: return ID::SuperGameBoy;
case System::Revision::GameBoyColor: return ID::GameBoyColor;
}
throw;
}
throw;
}
@ -130,8 +131,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate() {
video.generate_palette();
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
}
Interface::Interface() {

View File

@ -58,7 +58,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate();
void paletteUpdate(bool colorEmulation);
Interface();

View File

@ -1,9 +1,10 @@
#ifdef PPU_CPP
void PPU::cgb_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x7fff;
origin[n] = Origin::None;
for(auto& pixel : pixels) {
pixel.color = 0x7fff;
pixel.palette = 0;
pixel.origin = Pixel::Origin::None;
}
if(status.display_enable) {
@ -13,7 +14,7 @@ void PPU::cgb_render() {
}
uint32* output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
interface->lcdScanline();
}
@ -56,14 +57,15 @@ void PPU::cgb_render_bg() {
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
unsigned palette = ((attr & 0x07) << 2) + index;
unsigned color = 0;
color |= bgpd[(palette << 1) + 0] << 0;
color |= bgpd[(palette << 1) + 1] << 8;
color &= 0x7fff;
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
pixels[ox].color = color;
pixels[ox].palette = index;
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@ -83,15 +85,16 @@ void PPU::cgb_render_window() {
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
unsigned palette = ((attr & 0x07) << 2) + index;
unsigned color = 0;
color |= bgpd[(palette << 1) + 0] << 0;
color |= bgpd[(palette << 1) + 1] << 8;
color &= 0x7fff;
if(ox - (status.wx - 7) < 160u) {
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
pixels[ox].color = color;
pixels[ox].palette = index;
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
}
ix = (ix + 1) & 255;
@ -157,26 +160,26 @@ void PPU::cgb_render_ob() {
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(index == 0) continue;
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= obpd[palette_index++] << 0;
palette |= obpd[palette_index++] << 8;
palette &= 0x7fff;
unsigned palette = ((attr & 0x07) << 2) + index;
unsigned color = 0;
color |= obpd[(palette << 1) + 0] << 0;
color |= obpd[(palette << 1) + 1] << 8;
color &= 0x7fff;
unsigned ox = sx + tx;
if(ox < 160) {
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
if(status.bg_enable) {
if(origin[ox] == Origin::BGP) continue;
if(pixels[ox].origin == Pixel::Origin::BGP) continue;
if(attr & 0x80) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) {
if(line[ox] > 0) continue;
if(pixels[ox].origin == Pixel::Origin::BG) {
if(pixels[ox].palette > 0) continue;
}
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
pixels[ox].color = color;
pixels[ox].palette = index;
pixels[ox].origin = Pixel::Origin::OB;
}
}
}

View File

@ -1,9 +1,10 @@
#ifdef PPU_CPP
void PPU::dmg_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
for(auto& pixel : pixels) {
pixel.color = 0;
pixel.palette = 0;
pixel.origin = Pixel::Origin::None;
}
if(status.display_enable) {
@ -13,7 +14,7 @@ void PPU::dmg_render() {
}
uint32* output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
interface->lcdScanline();
}
@ -38,8 +39,9 @@ void PPU::dmg_render_bg() {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
pixels[ox].color = bgp[palette];
pixels[ox].palette = palette;
pixels[ox].origin = Pixel::Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
@ -58,9 +60,11 @@ void PPU::dmg_render_window() {
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160u) {
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
pixels[ox].color = bgp[palette];
pixels[ox].palette = palette;
pixels[ox].origin = Pixel::Origin::BG;
}
ix = (ix + 1) & 255;
@ -126,17 +130,16 @@ void PPU::dmg_render_ob() {
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = obp[(bool)(attr & 0x10)][palette];
unsigned ox = sx + tx;
if(ox < 160) {
if(attr & 0x80) {
if(origin[ox] == Origin::BG) {
if(line[ox] > 0) continue;
if(pixels[ox].origin == Pixel::Origin::BG) {
if(pixels[ox].palette > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
pixels[ox].color = obp[(bool)(attr & 0x10)][palette];
pixels[ox].palette = palette;
pixels[ox].origin = Pixel::Origin::OB;
}
}
}

View File

@ -107,8 +107,11 @@ void PPU::power() {
}
for(auto& n : screen) n = 0x0000;
for(auto& n : line) n = 0x0000;
for(auto& n : origin) n = Origin::None;
for(auto& n : pixels) {
n.color = 0;
n.palette = 0;
n.origin = Pixel::Origin::None;
}
for(auto& n : vram) n = 0x00;
for(auto& n : oam) n = 0x00;

View File

@ -50,9 +50,12 @@ struct PPU : Thread, MMIO {
} status;
uint32 screen[160 * 144];
uint16 line[160];
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
uint8 origin[160];
struct Pixel {
enum class Origin : unsigned { None, BG, BGP, OB };
uint16 color;
uint8 palette;
Origin origin;
} pixels[160];
uint8 vram[16384]; //GB = 8192, GBC = 16384
uint8 oam[160];

View File

@ -4,8 +4,11 @@ void PPU::serialize(serializer& s) {
Thread::serialize(s);
s.array(screen);
s.array(line);
s.array(origin);
for(auto& pixel : pixels) {
s.integer(pixel.color);
s.integer(pixel.palette);
s.integer((unsigned&)pixel.origin);
}
s.array(vram);
s.array(oam);

View File

@ -4,13 +4,13 @@ enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System : property<System> {
struct System {
enum class Revision : unsigned {
GameBoy,
SuperGameBoy,
GameBoyColor,
};
readonly<Revision> revision;
} revision;
inline bool dmg() const { return revision == Revision::GameBoy; }
inline bool sgb() const { return revision == Revision::SuperGameBoy; }
inline bool cgb() const { return revision == Revision::GameBoyColor; }

View File

@ -5,7 +5,8 @@ namespace GameBoy {
Video video;
void Video::generate_palette() {
void Video::generate_palette(bool color_emulation) {
this->color_emulation = color_emulation;
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
@ -20,6 +21,11 @@ Video::~Video() {
}
unsigned Video::palette_dmg(unsigned color) const {
if(color_emulation == false) {
unsigned L = (3 - color) * 21845;
return interface->videoColor(color, L, L, L);
}
unsigned R = monochrome[color][0] * 65535.0;
unsigned G = monochrome[color][1] * 65535.0;
unsigned B = monochrome[color][2] * 65535.0;
@ -40,6 +46,13 @@ unsigned Video::palette_cgb(unsigned color) const {
unsigned g = (color >> 5) & 31;
unsigned b = (color >> 10) & 31;
if(color_emulation == false) {
unsigned R = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
unsigned G = (g << 11) | (g << 6) | (g << 1) | (g >> 4);
unsigned B = (b << 11) | (b << 6) | (b << 1) | (b >> 4);
return interface->videoColor(color, R, G, B);
}
unsigned R = (r * 26 + g * 4 + b * 2);
unsigned G = ( g * 24 + b * 8);
unsigned B = (r * 6 + g * 4 + b * 22);

View File

@ -1,11 +1,12 @@
struct Video {
uint32_t* palette;
void generate_palette();
void generate_palette(bool color_emulation);
Video();
~Video();
private:
bool color_emulation;
static const double monochrome[4][3];
uint32_t palette_dmg(unsigned color) const;
uint32_t palette_sgb(unsigned color) const;

View File

@ -109,8 +109,8 @@ bool Interface::unserialize(serializer& s) {
return system.unserialize(s);
}
void Interface::paletteUpdate() {
video.generate_palette();
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
}
Interface::Interface() {

View File

@ -43,7 +43,7 @@ struct Interface : Emulator::Interface {
serializer serialize();
bool unserialize(serializer&);
void paletteUpdate();
void paletteUpdate(bool colorEmulation);
Interface();

View File

@ -4,7 +4,8 @@ namespace GameBoyAdvance {
Video video;
void Video::generate_palette() {
void Video::generate_palette(bool color_emulation) {
//todo: implement LCD color emulation
for(unsigned color = 0; color < (1 << 15); color++) {
uint5 b = color >> 10;
uint5 g = color >> 5;

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette;
void generate_palette();
void generate_palette(bool color_emulation);
Video();
~Video();

View File

@ -85,7 +85,7 @@ private:
inline bool directory::remove(const string& pathname) {
lstring list = directory::contents(pathname);
for(auto& name : list) {
if(name.endswith("/")) directory::remove({pathname, name});
if(name.endsWith("/")) directory::remove({pathname, name});
else file::remove({pathname, name});
}
return _wrmdir(utf16_t(pathname)) == 0;

View File

@ -86,7 +86,7 @@ inline void library::close() {
#elif defined(PLATFORM_WINDOWS)
inline bool library::open(const string& name, const string& path) {
if(handle) close();
string filepath(path, !path.empty() && !path.endswith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
string filepath(path, !path.empty() && !path.endsWith("/") && !path.endsWith("\\") ? "/" : "", name, ".dll");
handle = (uintptr_t)LoadLibraryW(utf16_t(filepath));
return handle;
}

View File

@ -17,9 +17,9 @@ struct image {
unsigned pitch = 0;
unsigned size = 0;
bool endian = 0; //0 = lsb, 1 = msb
bool endian = 0; //0 = lsb, 1 = msb
unsigned depth = 32;
unsigned stride = 4;
unsigned stride = 4;
struct Channel {
uint64_t mask;
@ -511,7 +511,7 @@ void image::scaleLinearWidth(unsigned outputWidth) {
sp += stride;
unsigned x = 0;
while(x < outputWidth) {
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, xfraction);
uint64_t R = interpolate1D((a & red.mask ) >> red.shift , (b & red.mask ) >> red.shift, xfraction);
@ -522,6 +522,7 @@ void image::scaleLinearWidth(unsigned outputWidth) {
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
@ -553,7 +554,7 @@ void image::scaleLinearHeight(unsigned outputHeight) {
sp += pitch;
unsigned y = 0;
while(y < outputHeight) {
while(true) {
while(yfraction < 0x100000000 && y++ < outputHeight) {
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, yfraction);
uint64_t R = interpolate1D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, yfraction);
@ -564,6 +565,7 @@ void image::scaleLinearHeight(unsigned outputHeight) {
dp += pitch;
yfraction += ystride;
}
if(y >= outputHeight) break;
sp += pitch;
a = b;
@ -600,7 +602,7 @@ void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
sp += stride;
unsigned x = 0;
while(x < outputWidth) {
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
uint64_t A = interpolate2D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, (c & alpha.mask) >> alpha.shift, (d & alpha.mask) >> alpha.shift, xfraction, yfraction);
uint64_t R = interpolate2D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, (c & red.mask ) >> red.shift, (d & red.mask ) >> red.shift, xfraction, yfraction);
@ -611,6 +613,7 @@ void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
@ -647,12 +650,13 @@ void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
uint64_t a = read(sp);
unsigned x = 0;
while(x < outputWidth) {
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
write(dp, a);
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = read(sp);

View File

@ -65,11 +65,15 @@ void pTabFrame::setGeometry(Geometry geometry) {
}
void pTabFrame::setImage(unsigned selection, const image& image) {
nall::image copy = image;
unsigned size = pFont::size(widget.state.font, " ").height;
copy.scale(size, size);
GdkPixbuf* pixbuf = CreatePixbuf(copy);
gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf);
if(image.empty() == false) {
nall::image copy = image;
unsigned size = pFont::size(widget.state.font, " ").height;
copy.scale(size, size);
GdkPixbuf* pixbuf = CreatePixbuf(copy);
gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf);
} else {
gtk_image_clear(GTK_IMAGE(tabs[selection].image));
}
}
void pTabFrame::setSelection(unsigned selection) {

View File

@ -178,7 +178,7 @@ void pWindow::setMenuVisible(bool visible) {
void pWindow::setModal(bool modality) {
if(modality == true) {
modal.appendonce(this);
modal.appendOnce(this);
updateModality();
while(window.state.modal) {
Application::processEvents();

View File

@ -81,6 +81,7 @@ void Cartridge::parse_markup_icd2(Markup::Node root) {
has_gb_slot = true;
icd2.revision = max(1, numeral(root["revision"].data));
GameBoy::cartridge.load_empty(GameBoy::System::Revision::SuperGameBoy);
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
string bootROMName = root["rom"]["name"].data;

View File

@ -73,7 +73,7 @@ void ICD2::reset() {
joyp14lock = 0;
pulselock = true;
GameBoy::video.generate_palette();
GameBoy::video.generate_palette(false);
GameBoy::system.init();
GameBoy::system.power();
}

View File

@ -338,8 +338,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate() {
video.generate_palette();
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
}
bool Interface::tracerEnable(bool trace) {

View File

@ -115,7 +115,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate();
void paletteUpdate(bool colorEmulation);
//debugger functions
bool tracerEnable(bool);

View File

@ -2,18 +2,28 @@
Video video;
void Video::generate_palette() {
void Video::generate_palette(bool color_emulation) {
for(unsigned color = 0; color < (1 << 19); color++) {
unsigned l = (color >> 15) & 15;
unsigned b = (color >> 10) & 31;
unsigned g = (color >> 5) & 31;
unsigned r = (color >> 0) & 31;
if(color_emulation == true) {
r = gamma_ramp[r];
g = gamma_ramp[g];
b = gamma_ramp[b];
} else {
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
}
double L = (1.0 + l) / 16.0;
if(l == 0) L *= 0.5;
unsigned R = L * (r << 11 | r << 6 | r << 1 | r >> 4);
unsigned G = L * (g << 11 | g << 6 | g << 1 | g >> 4);
unsigned B = L * (b << 11 | b << 6 | b << 1 | b >> 4);
unsigned R = L * (r << 8 | r << 0);
unsigned G = L * (g << 8 | g << 0);
unsigned B = L * (b << 8 | b << 0);
palette[color] = interface->videoColor(color, R, G, B);
}
@ -29,6 +39,13 @@ Video::~Video() {
//internal
const uint8_t Video::gamma_ramp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};
const uint8_t Video::cursor[15 * 15] = {
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette;
void generate_palette();
void generate_palette(bool color_emulation);
Video();
~Video();
@ -12,6 +12,7 @@ private:
void scanline();
void init();
static const uint8_t gamma_ramp[32];
static const uint8_t cursor[15 * 15];
void draw_cursor(uint16_t color, int x, int y);

View File

@ -7,6 +7,7 @@ ConfigurationSettings::ConfigurationSettings() {
video.append(video.shader = "Blur", "Shader");
video.append(video.scaleMode = 0, "ScaleMode");
video.append(video.aspectCorrection = true, "AspectCorrection");
video.append(video.colorEmulation = true, "ColorEmulation");
video.maskOverscan.assign(video.maskOverscan.enable = false);
video.maskOverscan.append(video.maskOverscan.horizontal = 8, "Horizontal");
video.maskOverscan.append(video.maskOverscan.vertical = 8, "Vertical");

View File

@ -5,6 +5,7 @@ struct ConfigurationSettings : Configuration::Document {
string shader;
unsigned scaleMode;
bool aspectCorrection;
bool colorEmulation;
struct MaskOverscan : Configuration::Node {
bool enable;
unsigned horizontal;

View File

@ -150,6 +150,7 @@ LibraryManager::LibraryManager() {
};
libraryFrame.onChange = {&LibraryManager::onChange, this};
loadButton.onActivate = {&LibraryManager::onLoad, this};
//initial config value of -1 defaults to import tab on first launch of higan
if(config->library.selection < 0) config->library.selection = browsers.size();
@ -202,6 +203,11 @@ void LibraryManager::onChange() {
}
}
void LibraryManager::onLoad() {
unsigned selection = libraryFrame.selection();
if(selection < browsers.size()) browsers[selection]->onActivate();
}
void LibraryManager::setInformation(bool load) {
string text;
if(loaded.size() == 0) {
@ -235,6 +241,24 @@ void LibraryManager::show() {
onChange();
}
//set library to show a specific media type, and then show the library
void LibraryManager::show(const string& type) {
unsigned selection = 0;
for(auto& browser : browsers) {
unsigned mode = 0;
for(auto& media : browser->emulator.media) {
if(media.bootable && media.type == type) {
libraryFrame.setSelection(selection);
browser->mediaMode.setSelection(mode);
browser->setMode();
return show();
}
mode++;
}
selection++;
}
}
void LibraryManager::synchronize() {
if(libraryFrame.selection() < browsers.size()) {
auto& emulator = browsers[libraryFrame.selection()]->emulator;

View File

@ -40,8 +40,10 @@ struct LibraryManager : Window {
void bootstrap();
string load(const string& type);
void onChange();
void onLoad();
void setInformation(bool load);
void show();
void show(const string& type);
void synchronize();
lstring loaded;

View File

@ -12,7 +12,7 @@ void Presentation::synchronize() {
shaderNone.setChecked();
if(config->video.shader == "None") shaderNone.setChecked();
if(config->video.shader == "Blur") shaderBlur.setChecked();
if(config->video.shader == "Emulation") shaderEmulation.setChecked();
if(config->video.shader == "Display Emulation") shaderEmulation.setChecked();
for(auto& shader : shaderList) {
string name = notdir(config->video.shader.split<1>(".shader/")(0));
if(name == shader->text()) shader->setChecked();
@ -62,7 +62,6 @@ Presentation::Presentation() {
viewport.setDroppable();
loadMenu.setText("Library");
loadGame.setText("Load Game ...");
settingsMenu.setText("Settings");
videoMenu.setText("Video");
centerVideo.setText("Center");
@ -90,7 +89,7 @@ Presentation::Presentation() {
synchronizeTime.setText("Synchronize Time");
append(loadMenu);
loadMenu.append(loadGame);
for(auto& item : loadBootableMedia) loadMenu.append(*item);
for(auto& systemItem : emulatorList) append(systemItem->menu);
append(settingsMenu);
settingsMenu.append(videoMenu);
@ -150,7 +149,6 @@ Presentation::Presentation() {
}
};
loadGame.onActivate = [&] { libraryManager->show(); };
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
shaderEmulation.onActivate = [&] { config->video.shader = "Display Emulation"; utility->updateShader(); };
@ -174,6 +172,16 @@ Presentation::Presentation() {
}
void Presentation::bootstrap() {
for(auto& emulator : program->emulator) {
for(auto& media : emulator->media) {
if(media.bootable == false) continue;
Item* item = new Item;
item->setText({media.name, " ..."});
item->onActivate = [=] { libraryManager->show(media.type); };
loadBootableMedia.append(item);
}
}
for(auto& emulator : program->emulator) {
auto iEmulator = new Emulator;
iEmulator->interface = emulator;

View File

@ -22,7 +22,7 @@ struct Presentation : Window {
Emulator* active = nullptr;
Menu loadMenu;
Item loadGame;
vector<Item*> loadBootableMedia;
Menu settingsMenu;
Menu videoMenu;
RadioItem centerVideo;

View File

@ -19,6 +19,16 @@ void InputManager::appendHotkeys() {
};
}
{
auto hotkey = new HotkeyInput;
hotkey->name = "Show Library";
hotkey->mapping = "KB0::L";
hotkey->press = [] {
libraryManager->show();
};
}
{
auto hotkey = new HotkeyInput;
hotkey->name = "Pause Emulation";

View File

@ -250,10 +250,17 @@ void InputManager::saveConfiguration() {
InputManager::InputManager() {
inputManager = this;
scancode[0] = new int16_t[Scancode::Limit]();
scancode[1] = new int16_t[Scancode::Limit]();
activeScancode = 0;
bootstrap();
}
InputManager::~InputManager() {
delete[] scancode[0];
delete[] scancode[1];
}
void InputManager::bootstrap() {
unsigned guid = 0;
for(auto& emulator : program->emulator) {

View File

@ -44,7 +44,7 @@ struct HotkeyInput : DigitalInput {
struct InputManager {
vector<AbstractInput*> inputMap;
vector<HotkeyInput*> hotkeyMap;
int16_t scancode[2][Scancode::Limit];
int16_t* scancode[2];
bool activeScancode;
void bind();
@ -53,6 +53,7 @@ struct InputManager {
void saveConfiguration();
void bootstrap();
InputManager();
~InputManager();
//hotkeys.cpp
void appendHotkeys();

View File

@ -15,6 +15,7 @@ VideoSettings::VideoSettings() {
gamma.slider.setLength(101);
luminance.name.setText("Luminance:");
luminance.slider.setLength(101);
colorEmulation.setText("Color emulation");
overscanAdjustment.setFont(program->boldFont);
overscanAdjustment.setText("Overscan mask:");
overscanHorizontal.name.setText("Horizontal:");
@ -25,11 +26,13 @@ VideoSettings::VideoSettings() {
append(colorAdjustment, {~0, 0});
append(saturation, {~0, 0});
append(gamma, {~0, 0});
append(luminance, {~0, 0}, 5);
append(luminance, {~0, 0});
append(colorEmulation, {~0, 0}, 5);
append(overscanAdjustment, {~0, 0});
append(overscanHorizontal, {~0, 0});
append(overscanVertical, {~0, 0}, 5);
colorEmulation.setChecked(config->video.colorEmulation);
saturation.slider.setPosition(config->video.saturation);
gamma.slider.setPosition(config->video.gamma - 100);
luminance.slider.setPosition(config->video.luminance);
@ -38,7 +41,7 @@ VideoSettings::VideoSettings() {
synchronize();
saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange =
saturation.slider.onChange = gamma.slider.onChange = luminance.slider.onChange = colorEmulation.onToggle =
overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
{&VideoSettings::synchronize, this};
}
@ -47,6 +50,7 @@ void VideoSettings::synchronize() {
config->video.saturation = saturation.slider.position();
config->video.gamma = 100 + gamma.slider.position();
config->video.luminance = luminance.slider.position();
config->video.colorEmulation = colorEmulation.checked();
config->video.maskOverscan.horizontal = overscanHorizontal.slider.position();
config->video.maskOverscan.vertical = overscanVertical.slider.position();
@ -56,5 +60,5 @@ void VideoSettings::synchronize() {
overscanHorizontal.value.setText({config->video.maskOverscan.horizontal, "px"});
overscanVertical.value.setText({config->video.maskOverscan.vertical, "px"});
if(program->active) system().paletteUpdate();
if(program->active) system().paletteUpdate(config->video.colorEmulation);
}

View File

@ -11,6 +11,7 @@ struct VideoSettings : SettingsLayout {
VideoSlider saturation;
VideoSlider gamma;
VideoSlider luminance;
CheckLabel colorEmulation;
Label overscanAdjustment;
VideoSlider overscanHorizontal;
VideoSlider overscanVertical;

View File

@ -91,7 +91,7 @@ void Utility::load() {
cheatEditor->load({pathname[0], "cheats.bml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
system().paletteUpdate();
system().paletteUpdate(config->video.colorEmulation);
synchronizeDSP();
resize();