mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r01 release.
byuu says: Changelog: - port: various compilation fixes for OS X [kode54] - nall: added programpath() function to return path to process binary [todo: need to have ethos use this function] - ruby: XAudio2 will select default game sound device instead of first sound device - ruby: DirectInput device IDs are no longer ambiguous when VID+PID are identical - ruby: OpenGL won't try and terminate if it hasn't been initialized - gb: D-pad up+down/left+right not masked in SGB mode - sfc: rewrote ICD2 video rendering to output in real-time, work with cycle-based Game Boy renderer - sfc: rewrote Bus::reduce(), reduces game loading time by about 500ms - ethos: store save states in {game}/higan/* instead of {game}/bsnes/* - loki: added target-loki/ (blank stub for now) - Makefile: purge out/* on make clean
This commit is contained in:
parent
10464b8c54
commit
04986d2bf7
4
Makefile
4
Makefile
|
@ -6,7 +6,8 @@ gb := gb
|
||||||
gba := gba
|
gba := gba
|
||||||
|
|
||||||
profile := accuracy
|
profile := accuracy
|
||||||
target := ethos
|
target := ethos
|
||||||
|
# target := loki
|
||||||
|
|
||||||
# options += debugger
|
# options += debugger
|
||||||
# arch := x86
|
# arch := x86
|
||||||
|
@ -77,6 +78,7 @@ flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||||
|
|
||||||
# targets
|
# targets
|
||||||
clean:
|
clean:
|
||||||
|
-@$(call delete,out/*)
|
||||||
-@$(call delete,obj/*.o)
|
-@$(call delete,obj/*.o)
|
||||||
-@$(call delete,obj/*.a)
|
-@$(call delete,obj/*.a)
|
||||||
-@$(call delete,obj/*.so)
|
-@$(call delete,obj/*.so)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "higan";
|
static const char Name[] = "higan";
|
||||||
static const char Version[] = "094";
|
static const char Version[] = "094.01";
|
||||||
static const char Author[] = "byuu";
|
static const char Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
static const char Website[] = "http://byuu.org/";
|
static const char Website[] = "http://byuu.org/";
|
||||||
|
|
|
@ -20,9 +20,12 @@ void CPU::mmio_joyp_poll() {
|
||||||
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Left) << 1;
|
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Left) << 1;
|
||||||
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Right) << 0;
|
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Right) << 0;
|
||||||
|
|
||||||
//D-pad pivot makes it impossible to press opposing directions at the same time
|
if(system.revision != System::Revision::SuperGameBoy) {
|
||||||
if(dpad & 4) dpad &= ~8; //disallow up+down
|
//D-pad pivot makes it impossible to press opposing directions at the same time
|
||||||
if(dpad & 2) dpad &= ~1; //disallow left+right
|
//however, Super Game Boy BIOS is able to set these bits together
|
||||||
|
if(dpad & 4) dpad &= ~8; //disallow up+down
|
||||||
|
if(dpad & 2) dpad &= ~1; //disallow left+right
|
||||||
|
}
|
||||||
|
|
||||||
status.joyp = 0x0f;
|
status.joyp = 0x0f;
|
||||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||||
|
|
|
@ -8,6 +8,10 @@ void Interface::lcdScanline() {
|
||||||
if(hook) hook->lcdScanline();
|
if(hook) hook->lcdScanline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interface::lcdOutput(uint2 color) {
|
||||||
|
if(hook) hook->lcdOutput(color);
|
||||||
|
}
|
||||||
|
|
||||||
void Interface::joypWrite(bool p15, bool p14) {
|
void Interface::joypWrite(bool p15, bool p14) {
|
||||||
if(hook) hook->joypWrite(p15, p14);
|
if(hook) hook->joypWrite(p15, p14);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,13 @@ struct Interface : Emulator::Interface {
|
||||||
//Super Game Boy bindings
|
//Super Game Boy bindings
|
||||||
struct Hook {
|
struct Hook {
|
||||||
virtual void lcdScanline() {}
|
virtual void lcdScanline() {}
|
||||||
|
virtual void lcdOutput(uint2 color) {}
|
||||||
virtual void joypWrite(bool p15, bool p14) {}
|
virtual void joypWrite(bool p15, bool p14) {}
|
||||||
};
|
};
|
||||||
Hook* hook = nullptr;
|
Hook* hook = nullptr;
|
||||||
|
|
||||||
void lcdScanline();
|
void lcdScanline();
|
||||||
|
void lcdOutput(uint2 color);
|
||||||
void joypWrite(bool p15, bool p14);
|
void joypWrite(bool p15, bool p14);
|
||||||
|
|
||||||
string title();
|
string title();
|
||||||
|
|
|
@ -79,6 +79,7 @@ void PPU::dmg_run() {
|
||||||
|
|
||||||
uint32* output = screen + status.ly * 160 + px++;
|
uint32* output = screen + status.ly * 160 + px++;
|
||||||
*output = color;
|
*output = color;
|
||||||
|
interface->lcdOutput(color); //Super Game Boy notification
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::dmg_run_bg() {
|
void PPU::dmg_run_bg() {
|
||||||
|
|
|
@ -25,6 +25,8 @@ void PPU::main() {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface->lcdScanline(); //Super Game Boy notification
|
||||||
|
|
||||||
if(status.display_enable && status.ly < 144) {
|
if(status.display_enable && status.ly < 144) {
|
||||||
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
add_clocks(92);
|
add_clocks(92);
|
||||||
|
@ -56,7 +58,6 @@ void PPU::scanline() {
|
||||||
if(++status.ly == 154) frame();
|
if(++status.ly == 154) frame();
|
||||||
|
|
||||||
if(status.ly < 144) {
|
if(status.ly < 144) {
|
||||||
interface->lcdScanline(); //Super Game Boy rendering notification
|
|
||||||
system.cgb() ? cgb_scanline() : dmg_scanline();
|
system.cgb() ? cgb_scanline() : dmg_scanline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <machine/endian.h>
|
||||||
|
#else
|
||||||
|
#include <endian.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct Intrinsics {
|
struct Intrinsics {
|
||||||
enum class Compiler : unsigned { Clang, GCC, VisualCPP, Unknown };
|
enum class Compiler : unsigned { Clang, GCC, VisualCPP, Unknown };
|
||||||
enum class Platform : unsigned { Windows, MacOSX, X, Unknown }; //X = Linux, BSD, etc
|
enum class Platform : unsigned { Windows, MacOSX, X, Unknown }; //X = Linux, BSD, etc
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Math {
|
||||||
#undef interface
|
#undef interface
|
||||||
#define dllexport __declspec(dllexport)
|
#define dllexport __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
#include <endian.h>
|
#include <dlfcn.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#define dllexport
|
#define dllexport
|
||||||
|
|
|
@ -201,6 +201,7 @@ template<signed precision = 0, char padchar = '0'> inline string binary(uintmax_
|
||||||
//platform.hpp
|
//platform.hpp
|
||||||
inline string activepath();
|
inline string activepath();
|
||||||
inline string realpath(const string& name);
|
inline string realpath(const string& name);
|
||||||
|
inline string programpath();
|
||||||
inline string userpath();
|
inline string userpath();
|
||||||
inline string configpath();
|
inline string configpath();
|
||||||
inline string sharedpath();
|
inline string sharedpath();
|
||||||
|
|
|
@ -22,6 +22,20 @@ string realpath(const string& name) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string programpath() {
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
int argc = 0;
|
||||||
|
wchar_t** argv = CommandLineToArgvW(GetCommandLine(), &argc);
|
||||||
|
string argv0 = (const char*)utf8_t(argv[0]);
|
||||||
|
LocalFree(argv);
|
||||||
|
return realpath(argv0);
|
||||||
|
#else
|
||||||
|
Dl_info info;
|
||||||
|
dladdr((void*)&programpath, &info);
|
||||||
|
return realpath(info.dli_fname);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// /home/username/
|
// /home/username/
|
||||||
// c:/users/username/
|
// c:/users/username/
|
||||||
string userpath() {
|
string userpath() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace nall {
|
||||||
|
|
||||||
//generate unique GUID
|
//generate unique GUID
|
||||||
inline string guid() {
|
inline string guid() {
|
||||||
random_lfsr lfsr;
|
LinearFeedbackShiftRegisterGenerator lfsr;
|
||||||
lfsr.seed(time(nullptr));
|
lfsr.seed(time(nullptr));
|
||||||
for(unsigned n = 0; n < 256; n++) lfsr();
|
for(unsigned n = 0; n < 256; n++) lfsr();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
/*
|
|
||||||
audio.xaudio2 (2010-08-14)
|
|
||||||
author: OV2
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "xaudio2.hpp"
|
#include "xaudio2.hpp"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
@ -132,7 +127,19 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, 2, settings.frequency, 0, 0 , NULL))) {
|
unsigned deviceCount = 0;
|
||||||
|
pXAudio2->GetDeviceCount(&deviceCount);
|
||||||
|
if(deviceCount == 0) { term(); return false; }
|
||||||
|
|
||||||
|
unsigned deviceID = 0;
|
||||||
|
for(unsigned deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) {
|
||||||
|
XAUDIO2_DEVICE_DETAILS deviceDetails;
|
||||||
|
memset(&deviceDetails, 0, sizeof(XAUDIO2_DEVICE_DETAILS));
|
||||||
|
pXAudio2->GetDeviceDetails(deviceIndex, &deviceDetails);
|
||||||
|
if(deviceDetails.Role & DefaultGameDevice) deviceID = deviceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice, 2, settings.frequency, 0, deviceID, NULL))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +152,7 @@ public:
|
||||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||||
wfx.cbSize = 0;
|
wfx.cbSize = 0;
|
||||||
|
|
||||||
if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, XAUDIO2_VOICE_NOSRC , XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL))) {
|
if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,18 +35,21 @@ struct pInputCarbon {
|
||||||
group.input[inputID].value = value;
|
group.input[inputID].value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void poll(vector<HID::Device*>& devices) {
|
vector<HID::Device*> poll() {
|
||||||
|
vector<HID::Device*> devices;
|
||||||
|
|
||||||
KeyMap keymap;
|
KeyMap keymap;
|
||||||
GetKeys(keymap);
|
GetKeys(keymap);
|
||||||
uint8_t* buffer = (uint8_t*)keymap;
|
uint8_t* buffer = (uint8_t*)keymap;
|
||||||
|
|
||||||
unsigned inputID = 0;
|
unsigned inputID = 0;
|
||||||
for(auto& key : keys) {
|
for(auto& key : keys) {
|
||||||
bool value = buffer[key.id >> 3] & (1 << (key.id & 7)));
|
bool value = buffer[key.id >> 3] & (1 << (key.id & 7));
|
||||||
assign(kb.hid, HID::Keyboard::GroupID::Button, inputID++, value);
|
assign(kb.hid, HID::Keyboard::GroupID::Button, inputID++, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
devices.append(&kb.hid);
|
devices.append(&kb.hid);
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rumble(uint64_t id, bool enable) {
|
bool rumble(uint64_t id, bool enable) {
|
||||||
|
|
|
@ -107,13 +107,9 @@ struct InputJoypadDirectInput {
|
||||||
Joypad jp;
|
Joypad jp;
|
||||||
jp.vendorID = instance->guidProduct.Data1 >> 0;
|
jp.vendorID = instance->guidProduct.Data1 >> 0;
|
||||||
jp.productID = instance->guidProduct.Data1 >> 16;
|
jp.productID = instance->guidProduct.Data1 >> 16;
|
||||||
|
jp.isXInputDevice = false;
|
||||||
if(auto device = rawinput.find(jp.vendorID, jp.productID)) {
|
if(auto device = rawinput.find(jp.vendorID, jp.productID)) {
|
||||||
jp.pathID = crc32_calculate((const uint8_t*)device().path.data(), device().path.size());
|
|
||||||
jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0;
|
|
||||||
jp.isXInputDevice = device().isXInputDevice;
|
jp.isXInputDevice = device().isXInputDevice;
|
||||||
} else {
|
|
||||||
//this should never occur
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Microsoft has intentionally imposed artificial restrictions on XInput devices when used with DirectInput
|
//Microsoft has intentionally imposed artificial restrictions on XInput devices when used with DirectInput
|
||||||
|
@ -133,6 +129,17 @@ struct InputJoypadDirectInput {
|
||||||
device->EnumObjects(DirectInput_EnumJoypadEffectsCallback, (void*)this, DIDFT_FFACTUATOR);
|
device->EnumObjects(DirectInput_EnumJoypadEffectsCallback, (void*)this, DIDFT_FFACTUATOR);
|
||||||
jp.hid.rumble = effects > 0;
|
jp.hid.rumble = effects > 0;
|
||||||
|
|
||||||
|
DIPROPGUIDANDPATH property;
|
||||||
|
memset(&property, 0, sizeof(DIPROPGUIDANDPATH));
|
||||||
|
property.diph.dwSize = sizeof(DIPROPGUIDANDPATH);
|
||||||
|
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||||
|
property.diph.dwObj = 0;
|
||||||
|
property.diph.dwHow = DIPH_DEVICE;
|
||||||
|
device->GetProperty(DIPROP_GUIDANDPATH, &property.diph);
|
||||||
|
string devicePath = (const char*)utf8_t(property.wszPath);
|
||||||
|
jp.pathID = crc32_calculate((const uint8_t*)devicePath.data(), devicePath.size());
|
||||||
|
jp.hid.id = (uint64_t)jp.pathID << 32 | jp.vendorID << 16 | jp.productID << 0;
|
||||||
|
|
||||||
if(jp.hid.rumble) {
|
if(jp.hid.rumble) {
|
||||||
//disable auto-centering spring for rumble support
|
//disable auto-centering spring for rumble support
|
||||||
DIPROPDWORD property;
|
DIPROPDWORD property;
|
||||||
|
|
|
@ -15,13 +15,12 @@ namespace ruby {
|
||||||
namespace ruby {
|
namespace ruby {
|
||||||
|
|
||||||
struct pVideoCGL : OpenGL {
|
struct pVideoCGL : OpenGL {
|
||||||
RubyVideoCGL* view;
|
RubyVideoCGL* view = nullptr;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
NSView* handle;
|
NSView* handle = nullptr;
|
||||||
|
bool synchronize = false;
|
||||||
bool synchronize;
|
unsigned filter = 0;
|
||||||
unsigned filter;
|
|
||||||
string shader;
|
string shader;
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
@ -157,14 +156,6 @@ struct pVideoCGL : OpenGL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pVideoCGL() {
|
|
||||||
view = nil;
|
|
||||||
|
|
||||||
settings.handle = nil;
|
|
||||||
settings.synchronize = false;
|
|
||||||
settings.filter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
~pVideoCGL() {
|
~pVideoCGL() {
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,11 +200,13 @@ bool OpenGL::init() {
|
||||||
glrLinkProgram(program);
|
glrLinkProgram(program);
|
||||||
|
|
||||||
shader(nullptr);
|
shader(nullptr);
|
||||||
return true;
|
return initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::term() {
|
void OpenGL::term() {
|
||||||
|
if(initialized == false) return;
|
||||||
shader(nullptr); //release shader resources (eg frame[] history)
|
shader(nullptr); //release shader resources (eg frame[] history)
|
||||||
OpenGLSurface::release();
|
OpenGLSurface::release();
|
||||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||||
|
initialized = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ struct OpenGL : OpenGLProgram {
|
||||||
Setting(const string& name, const string& value) : name(name), value(value) {}
|
Setting(const string& name, const string& value) : name(name), value(value) {}
|
||||||
};
|
};
|
||||||
set<Setting> settings;
|
set<Setting> settings;
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
void shader(const char* pathname);
|
void shader(const char* pathname);
|
||||||
void allocateHistory(unsigned size);
|
void allocateHistory(unsigned size);
|
||||||
|
|
|
@ -52,20 +52,19 @@ void ICD2::power() {
|
||||||
void ICD2::reset() {
|
void ICD2::reset() {
|
||||||
create(ICD2::Enter, cpu.frequency / 5);
|
create(ICD2::Enter, cpu.frequency / 5);
|
||||||
|
|
||||||
r6000_ly = 0x00;
|
|
||||||
r6000_row = 0x00;
|
|
||||||
r6003 = 0x00;
|
r6003 = 0x00;
|
||||||
r6004 = 0xff;
|
r6004 = 0xff;
|
||||||
r6005 = 0xff;
|
r6005 = 0xff;
|
||||||
r6006 = 0xff;
|
r6006 = 0xff;
|
||||||
r6007 = 0xff;
|
r6007 = 0xff;
|
||||||
for(auto& r : r7000) r = 0x00;
|
for(auto& r : r7000) r = 0x00;
|
||||||
r7800 = 0x0000;
|
|
||||||
mlt_req = 0;
|
mlt_req = 0;
|
||||||
|
|
||||||
for(auto& n : lcd.buffer) n = 0;
|
for(auto& n : output) n = 0xff;
|
||||||
for(auto& n : lcd.output) n = 0;
|
read_bank = 0;
|
||||||
lcd.row = 0;
|
read_addr = 0;
|
||||||
|
write_bank = 0;
|
||||||
|
write_addr = 0;
|
||||||
|
|
||||||
packetsize = 0;
|
packetsize = 0;
|
||||||
joyp_id = 3;
|
joyp_id = 3;
|
||||||
|
|
|
@ -16,8 +16,8 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Coprocessor {
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Emulator::Interface::Bind* bind;
|
Emulator::Interface::Bind* bind = nullptr;
|
||||||
GameBoy::Interface::Hook* hook;
|
GameBoy::Interface::Hook* hook = nullptr;
|
||||||
#include "interface/interface.hpp"
|
#include "interface/interface.hpp"
|
||||||
#include "mmio/mmio.hpp"
|
#include "mmio/mmio.hpp"
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
#ifdef ICD2_CPP
|
#ifdef ICD2_CPP
|
||||||
|
|
||||||
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
|
||||||
void ICD2::lcdScanline() {
|
void ICD2::lcdScanline() {
|
||||||
|
if(GameBoy::ppu.status.ly > 143) return; //Vblank
|
||||||
if((GameBoy::ppu.status.ly & 7) == 0) {
|
if((GameBoy::ppu.status.ly & 7) == 0) {
|
||||||
lcd.row = (lcd.row + 1) & 3;
|
write_bank = (write_bank + 1) & 3;
|
||||||
|
write_addr = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::ppu.status.ly & 7) * 160);
|
void ICD2::lcdOutput(uint2 color) {
|
||||||
memcpy(lcd.buffer + offset, GameBoy::ppu.screen + GameBoy::ppu.status.ly * 160, 160 * sizeof(uint32));
|
unsigned y = write_addr / 160;
|
||||||
|
unsigned x = write_addr % 160;
|
||||||
|
unsigned addr = write_bank * 512 + y * 2 + x / 8 * 16;
|
||||||
|
output[addr + 0] = (output[addr + 0] << 1) | (bool)(color & 1);
|
||||||
|
output[addr + 1] = (output[addr + 1] << 1) | (bool)(color & 2);
|
||||||
|
write_addr = (write_addr + 1) % 1280;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::joypWrite(bool p15, bool p14) {
|
void ICD2::joypWrite(bool p15, bool p14) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
void lcdScanline();
|
void lcdScanline();
|
||||||
|
void lcdOutput(uint2 color);
|
||||||
void joypWrite(bool p15, bool p14);
|
void joypWrite(bool p15, bool p14);
|
||||||
|
|
||||||
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
#ifdef ICD2_CPP
|
#ifdef ICD2_CPP
|
||||||
|
|
||||||
//convert linear pixel data to 2bpp planar tiledata
|
|
||||||
void ICD2::render(const uint32* source) {
|
|
||||||
memset(lcd.output, 0x00, 320 * sizeof(uint16));
|
|
||||||
|
|
||||||
for(unsigned y = 0; y < 8; y++) {
|
|
||||||
for(unsigned x = 0; x < 160; x++) {
|
|
||||||
unsigned pixel = *source++;
|
|
||||||
unsigned addr = y * 2 + (x / 8 * 16);
|
|
||||||
lcd.output[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7));
|
|
||||||
lcd.output[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 ICD2::read(unsigned addr) {
|
uint8 ICD2::read(unsigned addr) {
|
||||||
addr &= 0xffff;
|
addr &= 0xffff;
|
||||||
|
|
||||||
//LY counter
|
//LY counter
|
||||||
if(addr == 0x6000) {
|
if(addr == 0x6000) {
|
||||||
r6000_ly = GameBoy::ppu.status.ly;
|
unsigned y = min(143u, GameBoy::ppu.status.ly);
|
||||||
r6000_row = lcd.row;
|
return (y & ~7) | write_bank;
|
||||||
return r6000_ly;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//command ready port
|
//command ready port
|
||||||
|
@ -47,8 +32,8 @@ uint8 ICD2::read(unsigned addr) {
|
||||||
|
|
||||||
//VRAM port
|
//VRAM port
|
||||||
if(addr == 0x7800) {
|
if(addr == 0x7800) {
|
||||||
uint8 data = lcd.output[r7800];
|
uint8 data = output[read_bank * 512 + read_addr];
|
||||||
r7800 = (r7800 + 1) % 320;
|
read_addr = (read_addr + 1) & 511;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +45,8 @@ void ICD2::write(unsigned addr, uint8 data) {
|
||||||
|
|
||||||
//VRAM port
|
//VRAM port
|
||||||
if(addr == 0x6001) {
|
if(addr == 0x6001) {
|
||||||
r6001 = data;
|
read_bank = data & 3;
|
||||||
r7800 = 0;
|
read_addr = 0;
|
||||||
|
|
||||||
unsigned offset = (r6000_row - (4 - (r6001 - (r6000_ly & 3)))) & 3;
|
|
||||||
render(lcd.buffer + offset * 160 * 8);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
void render(const uint32* source);
|
|
||||||
|
|
||||||
uint8 r6000_ly; //SGB BIOS' cache of LY
|
|
||||||
uint8 r6000_row; //SGB BIOS' cache of ROW
|
|
||||||
uint8 r6001; //VRAM conversion
|
|
||||||
uint8 r6003; //control port
|
uint8 r6003; //control port
|
||||||
uint8 r6004; //joypad 1
|
uint8 r6004; //joypad 1
|
||||||
uint8 r6005; //joypad 2
|
uint8 r6005; //joypad 2
|
||||||
uint8 r6006; //joypad 3
|
uint8 r6006; //joypad 3
|
||||||
uint8 r6007; //joypad 4
|
uint8 r6007; //joypad 4
|
||||||
uint8 r7000[16]; //JOYP packet data
|
uint8 r7000[16]; //JOYP packet data
|
||||||
unsigned r7800; //VRAM offset
|
|
||||||
uint8 mlt_req; //number of active joypads
|
uint8 mlt_req; //number of active joypads
|
||||||
|
|
||||||
struct LCD {
|
uint8 output[4 * 512];
|
||||||
uint32 buffer[4 * 160 * 8]; //four tile rows of linear video data
|
unsigned read_bank;
|
||||||
uint16 output[320]; //one tile row of 2bpp video data
|
unsigned read_addr;
|
||||||
unsigned row; //active ICD2 rendering tile row
|
unsigned write_bank;
|
||||||
} lcd;
|
unsigned write_addr;
|
||||||
|
|
|
@ -18,21 +18,19 @@ void ICD2::serialize(serializer& s) {
|
||||||
s.integer(bitdata);
|
s.integer(bitdata);
|
||||||
s.integer(bitoffset);
|
s.integer(bitoffset);
|
||||||
|
|
||||||
s.integer(r6000_ly);
|
|
||||||
s.integer(r6000_row);
|
|
||||||
s.integer(r6001);
|
|
||||||
s.integer(r6003);
|
s.integer(r6003);
|
||||||
s.integer(r6004);
|
s.integer(r6004);
|
||||||
s.integer(r6005);
|
s.integer(r6005);
|
||||||
s.integer(r6006);
|
s.integer(r6006);
|
||||||
s.integer(r6007);
|
s.integer(r6007);
|
||||||
s.array(r7000);
|
s.array(r7000);
|
||||||
s.integer(r7800);
|
|
||||||
s.integer(mlt_req);
|
s.integer(mlt_req);
|
||||||
|
|
||||||
s.array(lcd.buffer);
|
s.array(output);
|
||||||
s.array(lcd.output);
|
s.integer(read_bank);
|
||||||
s.integer(lcd.row);
|
s.integer(read_addr);
|
||||||
|
s.integer(write_bank);
|
||||||
|
s.integer(write_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -56,31 +56,28 @@ MappedRAM::MappedRAM() : data_(nullptr), size_(0), write_protect_(false) {}
|
||||||
//Bus
|
//Bus
|
||||||
|
|
||||||
unsigned Bus::mirror(unsigned addr, unsigned size) {
|
unsigned Bus::mirror(unsigned addr, unsigned size) {
|
||||||
|
if(size == 0) return 0;
|
||||||
unsigned base = 0;
|
unsigned base = 0;
|
||||||
if(size) {
|
unsigned mask = 1 << 23;
|
||||||
unsigned mask = 1 << 23;
|
while(addr >= size) {
|
||||||
while(addr >= size) {
|
while(!(addr & mask)) mask >>= 1;
|
||||||
while(!(addr & mask)) mask >>= 1;
|
addr -= mask;
|
||||||
addr -= mask;
|
if(size > mask) {
|
||||||
if(size > mask) {
|
size -= mask;
|
||||||
size -= mask;
|
base += mask;
|
||||||
base += mask;
|
|
||||||
}
|
|
||||||
mask >>= 1;
|
|
||||||
}
|
}
|
||||||
base += addr;
|
mask >>= 1;
|
||||||
}
|
}
|
||||||
return base;
|
return base + addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Bus::reduce(unsigned addr, unsigned mask) {
|
unsigned Bus::reduce(unsigned addr, unsigned mask) {
|
||||||
unsigned result = 0, length = 0;
|
while(mask) {
|
||||||
for(unsigned n = 0; n < 24; n++) {
|
unsigned bits = (mask & -mask) - 1;
|
||||||
unsigned bit = 1 << n;
|
addr = ((addr >> 1) & ~bits) | (addr & bits);
|
||||||
if(mask & bit) continue;
|
mask = (mask & (mask - 1)) >> 1;
|
||||||
result |= (bool)(addr & bit) << length++;
|
|
||||||
}
|
}
|
||||||
return result;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 Bus::read(unsigned addr) {
|
uint8 Bus::read(unsigned addr) {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bsnes";
|
static const char Name[] = "bsnes";
|
||||||
static const unsigned SerializerVersion = 27;
|
static const unsigned SerializerVersion = 28;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,10 +54,10 @@ obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
|
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
|
||||||
|
|
||||||
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
|
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/)
|
||||||
$(compiler) $(rubyflags) -c $< -o $@
|
$(compiler) $(rubyflags) -c $< -o $@
|
||||||
|
|
||||||
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*)
|
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
|
||||||
$(compiler) $(phoenixflags) -c $< -o $@
|
$(compiler) $(phoenixflags) -c $< -o $@
|
||||||
|
|
||||||
obj/resource.o: $(ui)/resource.rc
|
obj/resource.o: $(ui)/resource.rc
|
||||||
|
@ -99,7 +99,6 @@ else
|
||||||
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
|
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
|
||||||
sudo install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
|
sudo install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
|
||||||
sudo install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
|
sudo install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
|
||||||
|
|
||||||
sudo mkdir -p /usr/share/$(name)
|
sudo mkdir -p /usr/share/$(name)
|
||||||
sudo cp -R profile/* /usr/share/$(name)
|
sudo cp -R profile/* /usr/share/$(name)
|
||||||
sudo cp data/cheats.bml /usr/share/$(name)/cheats.bml
|
sudo cp data/cheats.bml /usr/share/$(name)/cheats.bml
|
||||||
|
|
|
@ -89,7 +89,7 @@ void Utility::load() {
|
||||||
presentation->setTitle(system().title());
|
presentation->setTitle(system().title());
|
||||||
|
|
||||||
cheatEditor->load({pathname[0], "cheats.bml"});
|
cheatEditor->load({pathname[0], "cheats.bml"});
|
||||||
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
|
stateManager->load({pathname[0], "higan/states.bsa"}, 1);
|
||||||
|
|
||||||
synchronizeDSP();
|
synchronizeDSP();
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ void Utility::unload() {
|
||||||
if(tracerEnable) tracerToggle();
|
if(tracerEnable) tracerToggle();
|
||||||
|
|
||||||
cheatEditor->save({pathname[0], "cheats.bml"});
|
cheatEditor->save({pathname[0], "cheats.bml"});
|
||||||
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
|
stateManager->save({pathname[0], "higan/states.bsa"}, 1);
|
||||||
|
|
||||||
system().unload();
|
system().unload();
|
||||||
path.reset();
|
path.reset();
|
||||||
|
@ -125,14 +125,14 @@ void Utility::saveState(unsigned slot) {
|
||||||
if(program->active == nullptr) return;
|
if(program->active == nullptr) return;
|
||||||
serializer s = system().serialize();
|
serializer s = system().serialize();
|
||||||
if(s.size() == 0) return;
|
if(s.size() == 0) return;
|
||||||
directory::create({pathname[0], "bsnes/"});
|
directory::create({pathname[0], "higan/"});
|
||||||
if(file::write({pathname[0], "bsnes/state-", slot, ".bsa"}, s.data(), s.size()) == false);
|
if(file::write({pathname[0], "higan/state-", slot, ".bsa"}, s.data(), s.size()) == false);
|
||||||
showMessage({"Saved to slot ", slot});
|
showMessage({"Saved to slot ", slot});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::loadState(unsigned slot) {
|
void Utility::loadState(unsigned slot) {
|
||||||
if(program->active == nullptr) return;
|
if(program->active == nullptr) return;
|
||||||
auto memory = file::read({pathname[0], "bsnes/state-", slot, ".bsa"});
|
auto memory = file::read({pathname[0], "higan/state-", slot, ".bsa"});
|
||||||
if(memory.size() == 0) return showMessage({"Unable to locate slot ", slot, " state"});
|
if(memory.size() == 0) return showMessage({"Unable to locate slot ", slot, " state"});
|
||||||
serializer s(memory.data(), memory.size());
|
serializer s(memory.data(), memory.size());
|
||||||
if(system().unserialize(s) == false) return showMessage({"Slot ", slot, " state incompatible"});
|
if(system().unserialize(s) == false) return showMessage({"Slot ", slot, " state incompatible"});
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
name := loki
|
||||||
|
|
||||||
|
processors := arm gsu hg51b lr35902 r65816 spc700 upd96050
|
||||||
|
include processor/Makefile
|
||||||
|
|
||||||
|
include sfc/Makefile
|
||||||
|
include gb/Makefile
|
||||||
|
|
||||||
|
ui_objects := ui-loki
|
||||||
|
ui_objects += phoenix
|
||||||
|
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||||
|
|
||||||
|
include phoenix/Makefile
|
||||||
|
link += $(phoenixlink)
|
||||||
|
|
||||||
|
objects := $(ui_objects) $(objects)
|
||||||
|
objects := $(patsubst %,obj/%.o,$(objects))
|
||||||
|
|
||||||
|
obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
|
||||||
|
|
||||||
|
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/)
|
||||||
|
$(compiler) $(phoenixflags) -c $< -o $@
|
||||||
|
|
||||||
|
obj/resource.o: $(ui)/resource.rc
|
||||||
|
ifeq ($(arch),win32)
|
||||||
|
windres --target=pe-i386 $(ui)/resource.rc obj/resource.o
|
||||||
|
else
|
||||||
|
windres $(ui)/resource.rc obj/resource.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
build: $(objects)
|
||||||
|
ifeq ($(platform),windows)
|
||||||
|
$(strip $(compiler) -shared -o out/phoenix.dll obj/phoenix.o $(phoenixlink))
|
||||||
|
$(strip $(compiler) -o out/$(name) $(subst obj/phoenix.o,,$(objects)) $(link) -Lout -lphoenix)
|
||||||
|
else ifeq ($(platform),macosx)
|
||||||
|
if [ -d out/$(name).app ]; then rm -r out/$(name).app; fi
|
||||||
|
mkdir out/$(name).app
|
||||||
|
mkdir out/$(name).app/Contents
|
||||||
|
mkdir out/$(name).app/Contents/MacOS
|
||||||
|
mkdir out/$(name).app/Contents/Resources
|
||||||
|
cp data/Info.plist out/$(name).app/Contents/Info.plist
|
||||||
|
$(strip $(compiler) -o out/$(name).app/Contents/MacOS/$(name) $(objects) $(link))
|
||||||
|
else
|
||||||
|
$(strip $(compiler) -o out/$(name) $(objects) $(link))
|
||||||
|
endif
|
||||||
|
|
||||||
|
install:
|
||||||
|
ifeq ($(platform),windows)
|
||||||
|
else ifeq ($(platform),macosx)
|
||||||
|
sudo mkdir -p /Library/Application\ Support/$(name)
|
||||||
|
sudo cp -R profile/* /Library/Application\ Support/$(name)
|
||||||
|
sudo chmod -R 777 /Library/Application\ Support/$(name)
|
||||||
|
else
|
||||||
|
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
|
||||||
|
sudo mkdir -p /usr/share/$(name)
|
||||||
|
sudo cp -R profile/* /usr/share/$(name)
|
||||||
|
sudo chmod -R 777 /usr/share/$(name)
|
||||||
|
endif
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
ifeq ($(platform),windows)
|
||||||
|
else ifeq ($(platform),macosx)
|
||||||
|
else
|
||||||
|
sudo rm $(DESTDIR)$(prefix)/bin/$(name)
|
||||||
|
endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "loki.hpp"
|
||||||
|
|
||||||
|
Program* program = nullptr;
|
||||||
|
DSP dspaudio;
|
||||||
|
|
||||||
|
string Program::path(string name) {
|
||||||
|
string path = {basepath, name};
|
||||||
|
if(file::exists(path) || directory::exists(path)) return path;
|
||||||
|
path = {userpath, name};
|
||||||
|
if(file::exists(path) || directory::exists(path)) return path;
|
||||||
|
path = {sharedpath, name};
|
||||||
|
if(file::exists(path) || directory::exists(path)) return path;
|
||||||
|
return {userpath, name};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Program::main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Program::Program(int argc, char** argv) {
|
||||||
|
program = this;
|
||||||
|
|
||||||
|
basepath = nall::programpath();
|
||||||
|
userpath = {nall::configpath(), "loki/"};
|
||||||
|
sharedpath = {nall::sharedpath(), "loki/"};
|
||||||
|
directory::create(userpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
utf8_args(argc, argv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Application::setName("loki");
|
||||||
|
new Program(argc, argv);
|
||||||
|
delete program;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <emulator/emulator.hpp>
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/config.hpp>
|
||||||
|
#include <nall/directory.hpp>
|
||||||
|
#include <nall/dsp.hpp>
|
||||||
|
#include <nall/invoke.hpp>
|
||||||
|
#include <nall/map.hpp>
|
||||||
|
#include <nall/stream/file.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
|
#include <nall/stream/mmap.hpp>
|
||||||
|
#include <nall/stream/vector.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
|
#include <phoenix/phoenix.hpp>
|
||||||
|
using namespace phoenix;
|
||||||
|
|
||||||
|
struct Program {
|
||||||
|
string basepath;
|
||||||
|
string userpath;
|
||||||
|
string sharedpath;
|
||||||
|
|
||||||
|
string path(string name);
|
||||||
|
void main();
|
||||||
|
Program(int argc, char** argv);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Program* program;
|
||||||
|
extern DSP dspaudio;
|
Loading…
Reference in New Issue