Update to v105 release.

byuu says:

This release provides several major improvements to Mega Drive emulation
which enhances compatibility a good deal. It also includes important
Super Famicom mosaic emulation improvements, plus a much-needed SuperFX
save state issue fix.

Changelog (since v104):

  - higan: many improvements to Emulator::Interface to support
    forks/frontends
  - higan: refreshed program icon
  - icarus: new program icon
  - Game Boy Advance: slight emulation speedup over v104
  - Game Boy Advance: synchronize APU FIFO updates better
  - Mega Drive: added automatic region detection [hex_usr]
  - Mega Drive: support 8-bit SRAM
  - Game Boy Advance: fixed bug when changing to THUMB mode via MSR
    [MerryMage]
  - Master System: fix bug in backdrop color and background 0 priority
    [hex_usr]
  - Mega Drive: backgrounds always update output priority bit [Cydrak]
  - Mega Drive: emulated interlaced video output
  - Mega Drive: emulated shadow/highlight mode [Cydrak]
  - Super Famicom: auto joypad polling clears the shift register when
    starting
  - Super Famicom: added new low-entropy RAM initialization mode to more
    closely match hardware
  - Game Boy Advance: rumble will now time out after being left on for
    500ms
  - ruby: improved rumble support in udev input driver [ma_rysia]
  - M68K: `move.b (a7)[+/-]` adjust a7 by two
  - M68K: illegal/lineA/lineF opcodes do not modify the stack register
  - Mega Drive: emulate VIP status bit
  - uPD7725: improved emulation of OV1/S1 flags [byuu, AWJ, Lord
    Nightmare]
  - uPD7725: improved handling of DP, RP updates [Jonas Quinn]
  - Super Famicom: improved emulation of mosaic effects in hires,
    interlace, and offset-per-tile modes [byuu, Cydrak]
  - ruby: improved Direct3D exclusive mode monitor selection [Cydrak]
  - Super Famicom: fixed save state bug affecting SuperFX games
    [Cydrak]
  - Mega Drive: added workaround for Clang compiler bug; allowing this
    core to work on macOS [Cydrak, Sintendo]
  - higan: hotkeys now also trigger when the main window lacks focus yet
    higan is set to allow input on focus loss
  - higan: fixed an edge case where `int16_t` ↔ `double` audio
    conversion could possibly result in overflows
  - higan: fixed a crash on macOS when choosing quit from the
    application menu [ncbncb]

Changelog (since the previous WIP):

  - higan: restored `make console=true`
  - tomoko: if you allow input when main window focus is lost, hotkeys
    can now be triggered without focus as well
  - hiro/cocoa: fix crash on exit from menu [ncbncb]
  - ruby: smarter `double` → `int16_t` conversion to prevent
    underflow/overflow
This commit is contained in:
Tim Allen 2017-10-07 18:28:12 +11:00
parent 9a13863adb
commit f8e71b50d0
16 changed files with 45 additions and 36 deletions

View File

@ -8,7 +8,7 @@ objects := libco emulator audio video resource
flags += -I. -I.. flags += -I. -I..
ifeq ($(platform),windows) ifeq ($(platform),windows)
link += -mwindows link += $(if $(call streq,$(console),true),-mconsole,-mwindows)
ifeq ($(binary),application) ifeq ($(binary),application)
link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -Wl,-enable-auto-import link += -Wl,-enable-auto-import

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "104.17"; static const string Version = "105";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";

View File

@ -96,7 +96,7 @@ auto InputManager::appendHotkeys() -> void {
} }
auto InputManager::pollHotkeys() -> void { auto InputManager::pollHotkeys() -> void {
if(!program->focused()) return; if(!program->focused() && !settings["Input/FocusLoss/AllowInput"].boolean()) return;
for(auto& hotkey : hotkeys) { for(auto& hotkey : hotkeys) {
int16 state = hotkey->poll(); int16 state = hotkey->poll();

View File

@ -33,17 +33,18 @@
@end @end
CocoaDelegate* cocoaDelegate = nullptr; CocoaDelegate* cocoaDelegate = nullptr;
NSTimer* applicationTimer = nullptr;
namespace hiro { namespace hiro {
auto pApplication::run() -> void { auto pApplication::run() -> void {
//NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:0.1667 target:cocoaDelegate selector:@selector(updateInDock:) userInfo:nil repeats:YES]; //applicationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1667 target:cocoaDelegate selector:@selector(updateInDock:) userInfo:nil repeats:YES];
if(Application::state.onMain) { if(Application::state.onMain) {
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:cocoaDelegate selector:@selector(run:) userInfo:nil repeats:YES]; applicationTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:cocoaDelegate selector:@selector(run:) userInfo:nil repeats:YES];
//below line is needed to run application during window resize; however it has a large performance penalty on the resize smoothness //below line is needed to run application during window resize; however it has a large performance penalty on the resize smoothness
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; //[[NSRunLoop currentRunLoop] addTimer:applicationTimer forMode:NSEventTrackingRunLoopMode];
} }
@autoreleasepool { @autoreleasepool {
@ -74,6 +75,7 @@ auto pApplication::processEvents() -> void {
auto pApplication::quit() -> void { auto pApplication::quit() -> void {
@autoreleasepool { @autoreleasepool {
[applicationTimer invalidate];
[NSApp stop:nil]; [NSApp stop:nil];
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
[NSApp postEvent:event atStart:true]; [NSApp postEvent:event atStart:true];

View File

@ -54,8 +54,9 @@ struct AudioALSA : Audio {
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
if(!ready()) return; if(!ready()) return;
_buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
if(_offset < _periodSize) return; _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_offset < _periodSize) return;
snd_pcm_sframes_t available; snd_pcm_sframes_t available;
do { do {

View File

@ -33,7 +33,9 @@ struct AudioAO : Audio {
} }
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
uint32_t sample = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; uint32_t sample = 0;
sample |= (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
sample |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
ao_play(_interface, (char*)&sample, 4); ao_play(_interface, (char*)&sample, 4);
} }

View File

@ -208,12 +208,12 @@ private:
switch(_sampleFormat) { switch(_sampleFormat) {
case ASIOSTInt16LSB: { case ASIOSTInt16LSB: {
*(int16_t*)buffer = samples[n] * double(1 << 15); *(uint16_t*)buffer = (uint16_t)sclamp<16>(samples[n] * (32768.0 - 1.0));
break; break;
} }
case ASIOSTInt24LSB: { case ASIOSTInt24LSB: {
int value = samples[n] * double(1 << 23); auto value = (uint32_t)sclamp<24>(samples[n] * (256.0 * 32768.0 - 1.0));
buffer[0] = value >> 0; buffer[0] = value >> 0;
buffer[1] = value >> 8; buffer[1] = value >> 8;
buffer[2] = value >> 16; buffer[2] = value >> 16;
@ -221,17 +221,17 @@ private:
} }
case ASIOSTInt32LSB: { case ASIOSTInt32LSB: {
*(int32_t*)buffer = samples[n] * double(1 << 31); *(uint32_t*)buffer = (uint32_t)sclamp<32>(samples[n] * (65536.0 * 32768.0 - 1.0));
break; break;
} }
case ASIOSTFloat32LSB: { case ASIOSTFloat32LSB: {
*(float*)buffer = samples[n]; *(float*)buffer = max(-1.0, min(+1.0, samples[n]));
break; break;
} }
case ASIOSTFloat64LSB: { case ASIOSTFloat64LSB: {
*(double*)buffer = samples[n]; *(double*)buffer = max(-1.0, min(+1.0, samples[n]));
break; break;
} }
} }

View File

@ -70,8 +70,9 @@ struct AudioDirectSound : Audio {
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
if(!ready()) return; if(!ready()) return;
_buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
if(_offset < _period) return; _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_offset < _period) return;
_offset = 0; _offset = 0;
if(_blocking) { if(_blocking) {

View File

@ -61,8 +61,8 @@ struct AudioOpenAL : Audio {
} }
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
_buffer[_bufferLength] = int16_t(samples[0] * 32768.0) << 0; _buffer[_bufferLength] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
_buffer[_bufferLength] |= int16_t(samples[1] * 32768.0) << 16; _buffer[_bufferLength] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_bufferLength < _bufferSize) return; if(++_bufferLength < _bufferSize) return;
ALuint alBuffer = 0; ALuint alBuffer = 0;

View File

@ -77,7 +77,7 @@ struct AudioOSS : Audio {
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
if(!ready()) return; if(!ready()) return;
for(auto n : range(_channels)) { for(auto n : range(_channels)) {
int16_t sample = samples[n] * 32768.0; auto sample = (uint16_t)sclamp<16>(samples[n] * 32767.0);
auto unused = write(_fd, &sample, 2); auto unused = write(_fd, &sample, 2);
} }
} }

View File

@ -46,8 +46,9 @@ struct AudioPulseAudio : Audio {
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
pa_stream_begin_write(_stream, (void**)&_buffer, &_period); pa_stream_begin_write(_stream, (void**)&_buffer, &_period);
_buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
if((_offset + 1) * pa_frame_size(&_specification) <= _period) return; _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if((++_offset + 1) * pa_frame_size(&_specification) <= _period) return;
while(true) { while(true) {
if(_first) { if(_first) {

View File

@ -36,8 +36,9 @@ struct AudioPulseAudioSimple : Audio {
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
if(!ready()) return; if(!ready()) return;
_buffer[_offset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
if(_offset >= 64) { _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_offset >= 64) {
int error; int error;
pa_simple_write(_interface, (const void*)_buffer, _offset * sizeof(uint32_t), &error); pa_simple_write(_interface, (const void*)_buffer, _offset * sizeof(uint32_t), &error);
_offset = 0; _offset = 0;

View File

@ -205,16 +205,16 @@ private:
} }
if(_mode == 1 && _precision == 16) { if(_mode == 1 && _precision == 16) {
auto output = (int16_t*)buffer; auto output = (uint16_t*)buffer;
for(uint n : range(_channels)) *output++ = int16_t(samples[n] * 32768.0); for(uint n : range(_channels)) *output++ = (uint16_t)sclamp<16>(samples[n] * (32768.0 - 1.0));
buffer = (uint8_t*)output; buffer = (uint8_t*)output;
} else if(_mode == 1 && _precision == 32) { } else if(_mode == 1 && _precision == 32) {
auto output = (int32_t*)buffer; auto output = (uint32_t*)buffer;
for(uint n : range(_channels)) *output++ = int32_t(samples[n] * 65536.0 * 32768.0); for(uint n : range(_channels)) *output++ = (uint32_t)sclamp<32>(samples[n] * (65536.0 * 32768.0 - 1.0));
buffer = (uint8_t*)output; buffer = (uint8_t*)output;
} else if(_mode == 3 && _precision == 32) { } else if(_mode == 3 && _precision == 32) {
auto output = (float*)buffer; auto output = (float*)buffer;
for(uint n : range(_channels)) *output++ = float(samples[n]); for(uint n : range(_channels)) *output++ = float(max(-1.0, min(+1.0, samples[n])));
buffer = (uint8_t*)output; buffer = (uint8_t*)output;
} else { } else {
//output silence for unsupported sample formats //output silence for unsupported sample formats

View File

@ -59,8 +59,9 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback {
} }
auto output(const double samples[]) -> void { auto output(const double samples[]) -> void {
_buffer[_bufferIndex * _period + _bufferOffset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; _buffer[_bufferIndex * _period + _bufferOffset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
if(_bufferOffset < _period) return; _buffer[_bufferIndex * _period + _bufferOffset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
if(++_bufferOffset < _period) return;
_bufferOffset = 0; _bufferOffset = 0;
if(_bufferQueue == _bufferCount - 1) { if(_bufferQueue == _bufferCount - 1) {

View File

@ -55,10 +55,10 @@ struct InputJoypadDirectInput {
int16_t yaxis = 0; int16_t yaxis = 0;
if(pov < 36000) { if(pov < 36000) {
if(pov >= 31500 || pov <= 4500) yaxis = -32768; if(pov >= 31500 || pov <= 4500) yaxis = -32767;
if(pov >= 4500 && pov <= 13500) xaxis = +32767; if(pov >= 4500 && pov <= 13500) xaxis = +32767;
if(pov >= 13500 && pov <= 22500) yaxis = +32767; if(pov >= 13500 && pov <= 22500) yaxis = +32767;
if(pov >= 22500 && pov <= 31500) xaxis = -32768; if(pov >= 22500 && pov <= 31500) xaxis = -32767;
} }
assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 0, xaxis); assign(jp.hid, HID::Joypad::GroupID::Hat, n * 2 + 0, xaxis);

View File

@ -30,8 +30,8 @@ struct InputJoypadSDL {
for(signed n = 0; n < (signed)jp.hid->hats().size() - 1; n += 2) { for(signed n = 0; n < (signed)jp.hid->hats().size() - 1; n += 2) {
uint8_t state = SDL_JoystickGetHat(jp.handle, n >> 1); uint8_t state = SDL_JoystickGetHat(jp.handle, n >> 1);
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32768 : state & SDL_HAT_RIGHT ? +32767 : 0); assign(jp.hid, HID::Joypad::GroupID::Hat, n + 0, state & SDL_HAT_LEFT ? -32767 : state & SDL_HAT_RIGHT ? +32767 : 0);
assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32768 : state & SDL_HAT_DOWN ? +32767 : 0); assign(jp.hid, HID::Joypad::GroupID::Hat, n + 1, state & SDL_HAT_UP ? -32767 : state & SDL_HAT_DOWN ? +32767 : 0);
} }
for(auto n : range(jp.hid->buttons())) { for(auto n : range(jp.hid->buttons())) {