Update to v100 release.

byuu says:

higan has finally reached v100!

I feel it's important to stress right away that this is not "version
1.00", nor is it a major milestone release. Rather than arbitrary version
numbers, all of my software simply bumps version numbers by one for each
official release. As such, higan v100 is simply higan's 100th release.

That said, the primary focus of this release has been code
clean-ups. These are always somewhat dangerous in that regressions are
possible. We've tested through sixteen WIP revisions, one of which was
open to the public, to try and minimize any regressions. But all the same,
please report any regressions if you discover any.

Changelog (since v099):
FC: render during pixels 1-256 instead of 0-255 [hex_usr]
FC: rewrote controller emulation code
SFC: 8% speedup over the previous release thanks to PPU optimizations
SFC: fixed nasty DB address wrapping regression from v099
SFC: USART developer controller removed; superseded by 21fx
SFC: Super Multitap option removed from controller port 1; ports
    renamed 2-5
SFC: hidden option to experiment with 128KB VRAM (strictly for novelty)
higan: audio volume no longer divided by number of audio streams
higan: updated controller polling code to fix possible future mapping
    issues
higan: replaced nall/stream with nall/vfs for file-loading subsystem
tomoko: can now load multi-slotted games via command-line
tomoko: synchronize video removed from UI; still available in the
    settings file
tomoko, icarus: can navigate to root drive selection on Windows
all: major code cleanups and refactoring (~1MB diff against v099)

Note 1: the audio volume change means that SGB and MSU1 games won't
lose half the volume on the SNES sounds anymore. However, if one goes
overboard and drives the sound all the way to max volume with the MSU1,
clamping may occur. The obvious solution is not to drive volume that high
(it will vastly overpower the SNES audio, which usually never exceeds
25% volume.) Another option is to lower the volume in the audio settings
panel to 50%. In general, neither is likely to ever be necessary.

Note 2: the synchronize video option was hidden from the UI because it
is no longer useful. With the advent of compositors, the loss of the
complicated timing settings panel, support for the WonderSwan and its
75hz display, the need to emulate variable refresh rate behaviors in the
Game Boy, the unfortunate latency spike and audio distortion caused by
long Vsync pauses, and the arrival of adaptive sync technology ... it
no longer makes sense to present this option. However, as stated, you
can edit settings.bml to enable this option anyway if you insist and
understand the aforementioned risks.

Changelog (since v099r16 open beta):

- fixed MSU1 audio sign extension
- fixed compilation with SGB support disabled
- icarus can now navigate to root directory
- fixed compilation issues with OS X port
- (hopefully) fixed label height issue with hiro that affected icarus
  import dialog
- (mostly) fixed BS Memory, Sufami Turbo slot loading

Errata:

- forgot to remove the " - Slot A", " - Slot B" suffixes for Sufami
  Turbo slot loading
  - this means you have to navigate up one folder and then into Sufami
    Turbo/ to load games for this system
- moving WonderSwan orientation controls to the device slot is causing
  some nastiness
  - can now select orientation from the main menu, but it doesn't rotate
    the display
This commit is contained in:
Tim Allen 2016-07-08 22:04:32 +10:00
parent 13ad9644a2
commit 07995c05a5
13 changed files with 100 additions and 97 deletions

View File

@ -54,7 +54,7 @@ auto Audio::process() -> void {
if(!stream->pending()) return; if(!stream->pending()) return;
} }
double samples[channels] = {0}; double samples[channels] = {0.0};
for(auto& stream : streams) { for(auto& stream : streams) {
double buffer[16]; double buffer[16];
uint length = stream->read(buffer), offset = 0; uint length = stream->read(buffer), offset = 0;

View File

@ -11,13 +11,13 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "099.16"; static const string Version = "100";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";
//incremented only when serialization format changes //incremented only when serialization format changes
static const string SerializerVersion = "099.14"; static const string SerializerVersion = "100";
} }
#include "interface.hpp" #include "interface.hpp"

View File

@ -99,7 +99,7 @@ auto Cartridge::loadGameBoy() -> bool {
} }
auto Cartridge::loadBSMemory() -> bool { auto Cartridge::loadBSMemory() -> bool {
if(auto fp = interface->open(ID::BSMemory, "manifest.bml", File::Read, File::Required)) { if(auto fp = interface->open(bsmemory.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.bsMemory = fp->reads(); information.manifest.bsMemory = fp->reads();
} else return false; } else return false;
loadBSMemory(BML::unserialize(information.manifest.bsMemory)); loadBSMemory(BML::unserialize(information.manifest.bsMemory));
@ -107,7 +107,7 @@ auto Cartridge::loadBSMemory() -> bool {
} }
auto Cartridge::loadSufamiTurboA() -> bool { auto Cartridge::loadSufamiTurboA() -> bool {
if(auto fp = interface->open(ID::SufamiTurboA, "manifest.bml", File::Read, File::Required)) { if(auto fp = interface->open(sufamiturboA.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.sufamiTurboA = fp->reads(); information.manifest.sufamiTurboA = fp->reads();
} else return false; } else return false;
loadSufamiTurboA(BML::unserialize(information.manifest.sufamiTurboA)); loadSufamiTurboA(BML::unserialize(information.manifest.sufamiTurboA));
@ -115,7 +115,7 @@ auto Cartridge::loadSufamiTurboA() -> bool {
} }
auto Cartridge::loadSufamiTurboB() -> bool { auto Cartridge::loadSufamiTurboB() -> bool {
if(auto fp = interface->open(ID::SufamiTurboB, "manifest.bml", File::Read, File::Required)) { if(auto fp = interface->open(sufamiturboB.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.sufamiTurboB = fp->reads(); information.manifest.sufamiTurboB = fp->reads();
} else return false; } else return false;
loadSufamiTurboB(BML::unserialize(information.manifest.sufamiTurboB)); loadSufamiTurboB(BML::unserialize(information.manifest.sufamiTurboB));

View File

@ -6,11 +6,13 @@ auto Cartridge::loadCartridge(Markup::Node node) -> void {
if(board["mcc"] || board["bsmemory"]) { if(board["mcc"] || board["bsmemory"]) {
if(auto pathID = interface->load(ID::BSMemory, "BS Memory", "bs")) { if(auto pathID = interface->load(ID::BSMemory, "BS Memory", "bs")) {
bsmemory.pathID = pathID(); bsmemory.pathID = pathID();
loadBSMemory();
} }
} }
if(board["sufamiturbo"]) { if(board["sufamiturbo"]) {
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo - Slot A", "st")) { if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo - Slot A", "st")) {
sufamiturboA.pathID = pathID(); sufamiturboA.pathID = pathID();
loadSufamiTurboA();
} }
} }
@ -55,6 +57,7 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void {
if(node["board/linkable"]) { if(node["board/linkable"]) {
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo - Slot B", "st")) { if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo - Slot B", "st")) {
sufamiturboB.pathID = pathID(); sufamiturboB.pathID = pathID();
loadSufamiTurboB();
} }
} }
} }
@ -309,7 +312,7 @@ auto Cartridge::loadOBC1(Markup::Node node) -> void {
auto Cartridge::loadMSU1(Markup::Node node) -> void { auto Cartridge::loadMSU1(Markup::Node node) -> void {
has.MSU1 = true; has.MSU1 = true;
for(auto leaf : node.find("map")) loadMap(leaf, {&MSU1::read, &msu1}, {&MSU1::write, &msu1}); for(auto leaf : node.find("map")) loadMap(leaf, {&MSU1::readIO, &msu1}, {&MSU1::writeIO, &msu1});
} }
// //

View File

@ -78,8 +78,8 @@ struct ICD2 : Coprocessor {
auto power() -> void {} auto power() -> void {}
auto reset() -> void {} auto reset() -> void {}
auto read(uint24, uint8) -> uint8 { return 0; } auto readIO(uint24, uint8) -> uint8 { return 0; }
auto write(uint24, uint8) -> void { return; } auto writeIO(uint24, uint8) -> void { return; }
auto serialize(serializer&) -> void {} auto serialize(serializer&) -> void {}

View File

@ -14,23 +14,23 @@ auto MSU1::main() -> void {
double left = 0.0; double left = 0.0;
double right = 0.0; double right = 0.0;
if(mmio.audioPlay) { if(io.audioPlay) {
if(audioFile) { if(audioFile) {
if(audioFile->end()) { if(audioFile->end()) {
if(!mmio.audioRepeat) { if(!io.audioRepeat) {
mmio.audioPlay = false; io.audioPlay = false;
audioFile->seek(mmio.audioPlayOffset = 8); audioFile->seek(io.audioPlayOffset = 8);
} else { } else {
audioFile->seek(mmio.audioPlayOffset = mmio.audioLoopOffset); audioFile->seek(io.audioPlayOffset = io.audioLoopOffset);
} }
} else { } else {
mmio.audioPlayOffset += 4; io.audioPlayOffset += 4;
left = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0; left = (double)(int16)audioFile->readl(2) / 32768.0 * (double)io.audioVolume / 255.0;
right = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0; right = (double)(int16)audioFile->readl(2) / 32768.0 * (double)io.audioVolume / 255.0;
if(dsp.mute()) left = 0, right = 0; if(dsp.mute()) left = 0, right = 0;
} }
} else { } else {
mmio.audioPlay = false; io.audioPlay = false;
} }
} }
@ -57,23 +57,23 @@ auto MSU1::reset() -> void {
create(MSU1::Enter, 44100); create(MSU1::Enter, 44100);
stream = Emulator::audio.createStream(2, 44100.0); stream = Emulator::audio.createStream(2, 44100.0);
mmio.dataSeekOffset = 0; io.dataSeekOffset = 0;
mmio.dataReadOffset = 0; io.dataReadOffset = 0;
mmio.audioPlayOffset = 0; io.audioPlayOffset = 0;
mmio.audioLoopOffset = 0; io.audioLoopOffset = 0;
mmio.audioTrack = 0; io.audioTrack = 0;
mmio.audioVolume = 0; io.audioVolume = 0;
mmio.audioResumeTrack = ~0; //no resume io.audioResumeTrack = ~0; //no resume
mmio.audioResumeOffset = 0; io.audioResumeOffset = 0;
mmio.audioError = false; io.audioError = false;
mmio.audioPlay = false; io.audioPlay = false;
mmio.audioRepeat = false; io.audioRepeat = false;
mmio.audioBusy = false; io.audioBusy = false;
mmio.dataBusy = false; io.dataBusy = false;
dataOpen(); dataOpen();
audioOpen(); audioOpen();
@ -85,16 +85,16 @@ auto MSU1::dataOpen() -> void {
string name = document["board/msu1/rom/name"].text(); string name = document["board/msu1/rom/name"].text();
if(!name) name = "msu1.rom"; if(!name) name = "msu1.rom";
if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) { if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) {
dataFile->seek(mmio.dataReadOffset); dataFile->seek(io.dataReadOffset);
} }
} }
auto MSU1::audioOpen() -> void { auto MSU1::audioOpen() -> void {
audioFile.reset(); audioFile.reset();
auto document = BML::unserialize(cartridge.information.manifest.cartridge); auto document = BML::unserialize(cartridge.information.manifest.cartridge);
string name = {"track-", mmio.audioTrack, ".pcm"}; string name = {"track-", io.audioTrack, ".pcm"};
for(auto track : document.find("board/msu1/track")) { for(auto track : document.find("board/msu1/track")) {
if(track["number"].natural() != mmio.audioTrack) continue; if(track["number"].natural() != io.audioTrack) continue;
name = track["name"].text(); name = track["name"].text();
break; break;
} }
@ -102,19 +102,19 @@ auto MSU1::audioOpen() -> void {
if(audioFile->size() >= 8) { if(audioFile->size() >= 8) {
uint32 header = audioFile->readm(4); uint32 header = audioFile->readm(4);
if(header == 0x4d535531) { //"MSU1" if(header == 0x4d535531) { //"MSU1"
mmio.audioLoopOffset = 8 + audioFile->readl(4) * 4; io.audioLoopOffset = 8 + audioFile->readl(4) * 4;
if(mmio.audioLoopOffset > audioFile->size()) mmio.audioLoopOffset = 8; if(io.audioLoopOffset > audioFile->size()) io.audioLoopOffset = 8;
mmio.audioError = false; io.audioError = false;
audioFile->seek(mmio.audioPlayOffset); audioFile->seek(io.audioPlayOffset);
return; return;
} }
} }
audioFile.reset(); audioFile.reset();
} }
mmio.audioError = true; io.audioError = true;
} }
auto MSU1::read(uint24 addr, uint8) -> uint8 { auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
cpu.synchronizeCoprocessors(); cpu.synchronizeCoprocessors();
addr = 0x2000 | (addr & 7); addr = 0x2000 | (addr & 7);
@ -122,17 +122,17 @@ auto MSU1::read(uint24 addr, uint8) -> uint8 {
case 0x2000: case 0x2000:
return ( return (
Revision << 0 Revision << 0
| mmio.audioError << 3 | io.audioError << 3
| mmio.audioPlay << 4 | io.audioPlay << 4
| mmio.audioRepeat << 5 | io.audioRepeat << 5
| mmio.audioBusy << 6 | io.audioBusy << 6
| mmio.dataBusy << 7 | io.dataBusy << 7
); );
case 0x2001: case 0x2001:
if(mmio.dataBusy) return 0x00; if(io.dataBusy) return 0x00;
if(!dataFile) return 0x00; if(!dataFile) return 0x00;
if(dataFile->end()) return 0x00; if(dataFile->end()) return 0x00;
mmio.dataReadOffset++; io.dataReadOffset++;
return dataFile->read(); return dataFile->read();
case 0x2002: return 'S'; case 0x2002: return 'S';
case 0x2003: return '-'; case 0x2003: return '-';
@ -143,40 +143,40 @@ auto MSU1::read(uint24 addr, uint8) -> uint8 {
} }
} }
auto MSU1::write(uint24 addr, uint8 data) -> void { auto MSU1::writeIO(uint24 addr, uint8 data) -> void {
cpu.synchronizeCoprocessors(); cpu.synchronizeCoprocessors();
addr = 0x2000 | (addr & 7); addr = 0x2000 | (addr & 7);
switch(addr) { switch(addr) {
case 0x2000: mmio.dataSeekOffset.byte(0) = data; break; case 0x2000: io.dataSeekOffset.byte(0) = data; break;
case 0x2001: mmio.dataSeekOffset.byte(1) = data; break; case 0x2001: io.dataSeekOffset.byte(1) = data; break;
case 0x2002: mmio.dataSeekOffset.byte(2) = data; break; case 0x2002: io.dataSeekOffset.byte(2) = data; break;
case 0x2003: mmio.dataSeekOffset.byte(3) = data; case 0x2003: io.dataSeekOffset.byte(3) = data;
mmio.dataReadOffset = mmio.dataSeekOffset; io.dataReadOffset = io.dataSeekOffset;
if(dataFile) dataFile->seek(mmio.dataReadOffset); if(dataFile) dataFile->seek(io.dataReadOffset);
break; break;
case 0x2004: mmio.audioTrack.byte(0) = data; break; case 0x2004: io.audioTrack.byte(0) = data; break;
case 0x2005: mmio.audioTrack.byte(1) = data; case 0x2005: io.audioTrack.byte(1) = data;
mmio.audioPlayOffset = 8; io.audioPlayOffset = 8;
if(mmio.audioTrack == mmio.audioResumeTrack) { if(io.audioTrack == io.audioResumeTrack) {
mmio.audioPlayOffset = mmio.audioResumeOffset; io.audioPlayOffset = io.audioResumeOffset;
mmio.audioResumeTrack = ~0; //erase resume track io.audioResumeTrack = ~0; //erase resume track
mmio.audioResumeOffset = 0; io.audioResumeOffset = 0;
} }
audioOpen(); audioOpen();
break; break;
case 0x2006: case 0x2006:
mmio.audioVolume = data; io.audioVolume = data;
break; break;
case 0x2007: case 0x2007:
if(mmio.audioBusy) break; if(io.audioBusy) break;
if(mmio.audioError) break; if(io.audioError) break;
mmio.audioPlay = data.bit(0); io.audioPlay = data.bit(0);
mmio.audioRepeat = data.bit(1); io.audioRepeat = data.bit(1);
bool audioResume = data.bit(2); bool audioResume = data.bit(2);
if(!mmio.audioPlay && audioResume) { if(!io.audioPlay && audioResume) {
mmio.audioResumeTrack = mmio.audioTrack; io.audioResumeTrack = io.audioTrack;
mmio.audioResumeOffset = mmio.audioPlayOffset; io.audioResumeOffset = io.audioPlayOffset;
} }
break; break;
} }

View File

@ -12,8 +12,8 @@ struct MSU1 : Cothread {
auto dataOpen() -> void; auto dataOpen() -> void;
auto audioOpen() -> void; auto audioOpen() -> void;
auto read(uint24 addr, uint8 data) -> uint8; auto readIO(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void; auto writeIO(uint24 addr, uint8 data) -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
@ -30,7 +30,7 @@ private:
DataBusy = 0x80, DataBusy = 0x80,
}; };
struct MMIO { struct IO {
uint32 dataSeekOffset; uint32 dataSeekOffset;
uint32 dataReadOffset; uint32 dataReadOffset;
@ -48,7 +48,7 @@ private:
bool audioRepeat; bool audioRepeat;
bool audioBusy; bool audioBusy;
bool dataBusy; bool dataBusy;
} mmio; } io;
}; };
extern MSU1 msu1; extern MSU1 msu1;

View File

@ -1,23 +1,23 @@
auto MSU1::serialize(serializer& s) -> void { auto MSU1::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.integer(mmio.dataSeekOffset); s.integer(io.dataSeekOffset);
s.integer(mmio.dataReadOffset); s.integer(io.dataReadOffset);
s.integer(mmio.audioPlayOffset); s.integer(io.audioPlayOffset);
s.integer(mmio.audioLoopOffset); s.integer(io.audioLoopOffset);
s.integer(mmio.audioTrack); s.integer(io.audioTrack);
s.integer(mmio.audioVolume); s.integer(io.audioVolume);
s.integer(mmio.audioResumeTrack); s.integer(io.audioResumeTrack);
s.integer(mmio.audioResumeOffset); s.integer(io.audioResumeOffset);
s.integer(mmio.audioError); s.integer(io.audioError);
s.integer(mmio.audioPlay); s.integer(io.audioPlay);
s.integer(mmio.audioRepeat); s.integer(io.audioRepeat);
s.integer(mmio.audioBusy); s.integer(io.audioBusy);
s.integer(mmio.dataBusy); s.integer(io.dataBusy);
dataOpen(); dataOpen();
audioOpen(); audioOpen();

View File

@ -38,7 +38,7 @@
[item setTarget:self]; [item setTarget:self];
[rootMenu addItem:item]; [rootMenu addItem:item];
string result = nall::execute("defaults", "read", "/Library/Preferences/com.apple.security", "GKAutoRearm").strip(); string result = nall::execute("defaults", "read", "/Library/Preferences/com.apple.security", "GKAutoRearm").output.strip();
if(result != "0") { if(result != "0") {
disableGatekeeperAutoRearm = [[[NSMenuItem alloc] initWithTitle:@"Disable Gatekeeper Auto-Rearm" action:@selector(menuDisableGatekeeperAutoRearm) keyEquivalent:@""] autorelease]; disableGatekeeperAutoRearm = [[[NSMenuItem alloc] initWithTitle:@"Disable Gatekeeper Auto-Rearm" action:@selector(menuDisableGatekeeperAutoRearm) keyEquivalent:@""] autorelease];
[disableGatekeeperAutoRearm setTarget:self]; [disableGatekeeperAutoRearm setTarget:self];
@ -144,7 +144,7 @@
[alert setMessageText:@"Disable Gatekeeper Auto-Rearm"]; [alert setMessageText:@"Disable Gatekeeper Auto-Rearm"];
nall::execute("sudo", "defaults", "write", "/Library/Preferences/com.apple.security", "GKAutoRearm", "-bool", "NO"); nall::execute("sudo", "defaults", "write", "/Library/Preferences/com.apple.security", "GKAutoRearm", "-bool", "NO");
if(nall::execute("defaults", "read", "/Library/Preferences/com.apple.security", "GKAutoRearm").strip() == "0") { if(nall::execute("defaults", "read", "/Library/Preferences/com.apple.security", "GKAutoRearm").output.strip() == "0") {
[alert setAlertStyle:NSInformationalAlertStyle]; [alert setAlertStyle:NSInformationalAlertStyle];
[alert setInformativeText:@"Gatekeeper's automatic 30-day rearm behavior has been disabled successfully."]; [alert setInformativeText:@"Gatekeeper's automatic 30-day rearm behavior has been disabled successfully."];
[disableGatekeeperAutoRearm setHidden:YES]; [disableGatekeeperAutoRearm setHidden:YES];

View File

@ -17,7 +17,7 @@ auto pLabel::destruct() -> void {
} }
auto pLabel::minimumSize() const -> Size { auto pLabel::minimumSize() const -> Size {
auto size = pFont::size(hfont, state().text); auto size = pFont::size(self().font(true), state().text ? state().text : " ");
return {size.width(), size.height()}; return {size.width(), size.height()};
} }

View File

@ -521,7 +521,7 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, boo
" map address=00-3f,80-bf:3800-38ff\n" " map address=00-3f,80-bf:3800-38ff\n"
" prom name=st018.program.rom size=0x20000\n" " prom name=st018.program.rom size=0x20000\n"
" drom name=st018.data.rom size=0x8000\n" " drom name=st018.data.rom size=0x8000\n"
" dram name=save.ram size=0x4000\n" " ram name=save.ram size=0x4000\n"
); );
} }

View File

@ -48,8 +48,8 @@ auto ScanDialog::show() -> void {
auto ScanDialog::refresh() -> void { auto ScanDialog::refresh() -> void {
scanList.reset(); scanList.reset();
auto pathname = pathEdit.text().transform("\\", "/").trimRight("/").append("/"); auto pathname = pathEdit.text().transform("\\", "/");
if(!directory::exists(pathname)) return; if((pathname || Path::root() == "/") && !pathname.endsWith("/")) pathname.append("/");
settings["icarus/Path"].setValue(pathname); settings["icarus/Path"].setValue(pathname);
pathEdit.setText(pathname); pathEdit.setText(pathname);
@ -74,7 +74,7 @@ auto ScanDialog::refresh() -> void {
auto ScanDialog::activate() -> void { auto ScanDialog::activate() -> void {
if(auto item = scanList.selected()) { if(auto item = scanList.selected()) {
string location{settings["icarus/Path"].text(), item.text()}; string location{settings["icarus/Path"].text(), item.text()};
if(directory::exists(location) && !gamePakType(Location::suffix(location))) { if(!gamePakType(Location::suffix(location))) {
pathEdit.setText(location); pathEdit.setText(location);
refresh(); refresh();
} }

View File

@ -78,7 +78,7 @@ struct VideoCGL : Video, OpenGL {
return false; return false;
} }
auto lock(uint32*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
OpenGL::size(width, height); OpenGL::size(width, height);
return OpenGL::lock(data, pitch); return OpenGL::lock(data, pitch);
} }