Update to v093r07 release.

byuu says:

Changelog:
- importing a game won't show message box on success
- importing a game will select the game that was imported in the list
  - caveat: GTK+ port doesn't seem to be removing focus from item 0 even
    though the selection is on item 2
- Game Boy audio reduced in volume by 50%
- Game Boy Advance audio reduced in volume by 50%
- Game Boy internally mixes audio at 2MHz now
- Game Boy Advance's Game Boy audio hardware internally mixes audio at
  2MHz now
- Game Boy Color doesn't sort sprites by X-coordinate
- Game Boy Color allows transparency on BGpriority pixels
  - caveat: this seems to allow sprites to appear on top of windows
- Game Boy Color VRAM DMA transfers 16 bytes in 8 clocks (or 16 clocks
  in double speed mode)
- Game Boy Color VRAM DMA masks low 4-bits of source and destination
  address
- Game Boy Color VRAM DMA only allows reads from ROM or RAM
- Game Boy Color VRAM DMA only allows writes to VRAM
- fixed a bug in dereferencing a nullptr from pObject::find(), should
  fix crash when pressing enter key on blank windows
- fixed Windows RadioItem selection
- Game Boy Advance color emulation code added
This commit is contained in:
Tim Allen 2013-12-10 23:12:54 +11:00
parent 35f1605829
commit 0f78acffd7
37 changed files with 177 additions and 88 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "higan"; static const char Name[] = "higan";
static const char Version[] = "093.06"; static const char Version[] = "093.07";
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/";

View File

@ -54,7 +54,7 @@ void APU::main() {
} }
void APU::power() { void APU::power() {
create(Main, 4 * 1024 * 1024); create(Main, 2 * 1024 * 1024);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
for(auto& n : mmio_data) n = 0x00; for(auto& n : mmio_data) n = 0x00;

View File

@ -6,7 +6,7 @@ struct APU : Thread, MMIO {
#include "master/master.hpp" #include "master/master.hpp"
uint8 mmio_data[48]; uint8 mmio_data[48];
uint13 sequencer_base; uint12 sequencer_base;
uint3 sequencer_step; uint3 sequencer_step;
Square1 square1; Square1 square1;

View File

@ -21,8 +21,8 @@ void APU::Master::run() {
if(channel3_left_enable) sample += apu.wave.output; if(channel3_left_enable) sample += apu.wave.output;
if(channel4_left_enable) sample += apu.noise.output; if(channel4_left_enable) sample += apu.noise.output;
sample = (sample * 512) - 16384; sample = (sample * 512) - 16384;
sample = (sample * (left_volume + 1)) / 8; sample = (sample * (left_volume + 1)) / 8;
left = sample; left = sample;
sample = 0; sample = 0;
if(channel1_right_enable) sample += apu.square1.output; if(channel1_right_enable) sample += apu.square1.output;
@ -32,6 +32,11 @@ void APU::Master::run() {
sample = (sample * 512) - 16384; sample = (sample * 512) - 16384;
sample = (sample * (right_volume + 1)) / 8; sample = (sample * (right_volume + 1)) / 8;
right = sample; right = sample;
//reduce audio volume
center >>= 1;
left >>= 1;
right >>= 1;
} }
void APU::Master::write(unsigned r, uint8 data) { void APU::Master::write(unsigned r, uint8 data) {

View File

@ -48,8 +48,8 @@ void APU::Noise::write(unsigned r, uint8 data) {
if(r == 3) { //$ff22 NR43 if(r == 3) { //$ff22 NR43
frequency = data >> 4; frequency = data >> 4;
narrow_lfsr = data & 0x08; narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4; divisor = (data & 0x07) << 3;
if(divisor == 0) divisor = 8; if(divisor == 0) divisor = 4;
period = divisor << frequency; period = divisor << frequency;
} }

View File

@ -6,7 +6,7 @@ bool APU::Square1::dac_enable() {
void APU::Square1::run() { void APU::Square1::run() {
if(period && --period == 0) { if(period && --period == 0) {
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
switch(duty) { switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_ case 0: duty_output = (phase == 6); break; //______-_
@ -34,7 +34,7 @@ void APU::Square1::sweep(bool update) {
} else if(sweep_shift && update) { } else if(sweep_shift && update) {
frequency_shadow = freq; frequency_shadow = freq;
frequency = freq & 2047; frequency = freq & 2047;
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
} }
} }
@ -91,7 +91,7 @@ void APU::Square1::write(unsigned r, uint8 data) {
if(initialize) { if(initialize) {
enable = dac_enable(); enable = dac_enable();
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope_period = envelope_frequency; envelope_period = envelope_frequency;
volume = envelope_volume; volume = envelope_volume;
frequency_shadow = frequency; frequency_shadow = frequency;

View File

@ -6,7 +6,7 @@ bool APU::Square2::dac_enable() {
void APU::Square2::run() { void APU::Square2::run() {
if(period && --period == 0) { if(period && --period == 0) {
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
switch(duty) { switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_ case 0: duty_output = (phase == 6); break; //______-_
@ -60,7 +60,7 @@ void APU::Square2::write(unsigned r, uint8 data) {
if(initialize) { if(initialize) {
enable = dac_enable(); enable = dac_enable();
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope_period = envelope_frequency; envelope_period = envelope_frequency;
volume = envelope_volume; volume = envelope_volume;
} }

View File

@ -2,7 +2,7 @@
void APU::Wave::run() { void APU::Wave::run() {
if(period && --period == 0) { if(period && --period == 0) {
period = 2 * (2048 - frequency); period = 1 * (2048 - frequency);
pattern_sample = pattern[++pattern_offset]; pattern_sample = pattern[++pattern_offset];
} }
@ -48,7 +48,7 @@ void APU::Wave::write(unsigned r, uint8 data) {
if(initialize) { if(initialize) {
enable = dac_enable; enable = dac_enable;
period = 2 * (2048 - frequency); period = 1 * (2048 - frequency);
pattern_offset = 0; pattern_offset = 0;
} }
} }

View File

@ -185,6 +185,7 @@ void CPU::power() {
status.dma_mode = 0; status.dma_mode = 0;
status.dma_length = 0; status.dma_length = 0;
status.dma_completed = true;
status.ff6c = 0; status.ff6c = 0;
status.ff72 = 0; status.ff72 = 0;

View File

@ -57,6 +57,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
//$ff55 HDMA5 //$ff55 HDMA5
bool dma_mode; bool dma_mode;
uint16 dma_length; uint16 dma_length;
bool dma_completed;
//$ff6c ??? //$ff6c ???
uint8 ff6c; uint8 ff6c;
@ -102,6 +103,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
uint8 op_read(uint16 addr); uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data); void op_write(uint16 addr, uint8 data);
void cycle_edge(); void cycle_edge();
uint8 dma_read(uint16 addr);
void dma_write(uint16 addr, uint8 data);
uint8 debugger_read(uint16 addr); uint8 debugger_read(uint16 addr);
//timing.cpp //timing.cpp

View File

@ -25,6 +25,20 @@ void CPU::cycle_edge() {
} }
} }
//VRAM DMA source can only be ROM or RAM
uint8 CPU::dma_read(uint16 addr) {
if(addr < 0x8000) return bus.read(addr); //0000-7fff
if(addr < 0xa000) return 0x00; //8000-9fff
if(addr < 0xe000) return bus.read(addr); //a000-dfff
return 0x00; //e000-ffff
}
//VRAM DMA target is always VRAM
void CPU::dma_write(uint16 addr, uint8 data) {
addr = 0x8000 | (addr & 0x1fff); //8000-9fff
return bus.write(addr, data);
}
uint8 CPU::debugger_read(uint16 addr) { uint8 CPU::debugger_read(uint16 addr) {
return bus.read(addr); return bus.read(addr);
} }

View File

@ -81,7 +81,8 @@ uint8 CPU::mmio_read(uint16 addr) {
} }
if(addr == 0xff55) { //HDMA5 if(addr == 0xff55) { //HDMA5
return (status.dma_length / 16) - 1; return (status.dma_completed << 7)
| (((status.dma_length / 16) - 1) & 0x7f);
} }
if(addr == 0xff56) { //RP if(addr == 0xff56) { //RP
@ -203,7 +204,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
} }
if(addr == 0xff52) { //HDMA2 if(addr == 0xff52) { //HDMA2
status.dma_source = (status.dma_source & 0xff00) | (data << 0); status.dma_source = (status.dma_source & 0xff00) | (data & 0xf0);
return; return;
} }
@ -213,18 +214,24 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
} }
if(addr == 0xff54) { //HDMA4 if(addr == 0xff54) { //HDMA4
status.dma_target = (status.dma_target & 0xff00) | (data << 0); status.dma_target = (status.dma_target & 0xff00) | (data & 0xf0);
return; return;
} }
if(addr == 0xff55) { //HDMA5 if(addr == 0xff55) { //HDMA5
status.dma_mode = data & 0x80; status.dma_mode = data & 0x80;
status.dma_length = ((data & 0x7f) + 1) * 16; status.dma_length = ((data & 0x7f) + 1) * 16;
status.dma_completed = !status.dma_mode;
if(status.dma_mode == 0) do { if(status.dma_mode == 0) {
bus.write(status.dma_target++, bus.read(status.dma_source++)); do {
add_clocks(4 << status.speed_double); for(unsigned n = 0; n < 16; n++) {
} while(--status.dma_length); dma_write(status.dma_target++, dma_read(status.dma_source++));
}
add_clocks(8 << status.speed_double);
status.dma_length -= 16;
} while(status.dma_length);
}
return; return;
} }

View File

@ -39,6 +39,7 @@ void CPU::serialize(serializer& s) {
s.integer(status.dma_target); s.integer(status.dma_target);
s.integer(status.dma_mode); s.integer(status.dma_mode);
s.integer(status.dma_length); s.integer(status.dma_length);
s.integer(status.dma_completed);
s.integer(status.ff6c); s.integer(status.ff6c);

View File

@ -76,11 +76,11 @@ void CPU::timer_4096hz() {
} }
void CPU::hblank() { void CPU::hblank() {
if(status.dma_mode == 1 && status.dma_length) { if(status.dma_mode == 1 && status.dma_length && ppu.status.ly < 144) {
for(unsigned n = 0; n < 16; n++) { for(unsigned n = 0; n < 16; n++) {
bus.write(status.dma_target++, bus.read(status.dma_source++)); dma_write(status.dma_target++, dma_read(status.dma_source++));
add_clocks(4);
} }
add_clocks(8 << status.speed_double);
status.dma_length -= 16; status.dma_length -= 16;
} }
} }

View File

@ -21,7 +21,7 @@ double Interface::videoFrequency() {
} }
double Interface::audioFrequency() { double Interface::audioFrequency() {
return 4194304.0; return 4194304.0 / 2.0;
} }
bool Interface::loaded() { bool Interface::loaded() {

View File

@ -125,19 +125,6 @@ void PPU::cgb_render_ob() {
if(sprites == 10) break; if(sprites == 10) break;
} }
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority //render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) { for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2; unsigned n = sprite[s] << 2;
@ -170,9 +157,8 @@ void PPU::cgb_render_ob() {
if(ox < 160) { if(ox < 160) {
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window //When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
if(status.bg_enable) { if(status.bg_enable) {
if(pixels[ox].origin == Pixel::Origin::BGP) continue;
if(attr & 0x80) { if(attr & 0x80) {
if(pixels[ox].origin == Pixel::Origin::BG) { if(pixels[ox].origin == Pixel::Origin::BG || pixels[ox].origin == Pixel::Origin::BGP) {
if(pixels[ox].palette > 0) continue; if(pixels[ox].palette > 0) continue;
} }
} }

View File

@ -95,16 +95,12 @@ void PPU::dmg_render_ob() {
if(sprites == 10) break; if(sprites == 10) break;
} }
//sort by X-coordinate, when equal, lower address comes first //sort by X-coordinate; when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) { for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) { for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8; signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8; signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) { if(sy < sx) std::swap(sprite[x], sprite[y]);
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
} }
} }

View File

@ -25,7 +25,7 @@ void APU::Enter() {
} }
void APU::main() { void APU::main() {
for(unsigned n = 0; n < 128; n++) { for(unsigned n = 0; n < 64; n++) {
runsequencer(); runsequencer();
} }
@ -70,7 +70,7 @@ void APU::main() {
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15; if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0; if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
interface->audioSample(sclamp<16>(lsample << 7), sclamp<16>(rsample << 7)); //should be <<5, use <<7 for added volume interface->audioSample(sclamp<16>(lsample << 6), sclamp<16>(rsample << 6)); //should be <<5, use <<6 for added volume
step(512); step(512);
} }

View File

@ -1,6 +1,6 @@
unsigned APU::Noise::divider() const { unsigned APU::Noise::divider() const {
if(divisor == 0) return 8; if(divisor == 0) return 4;
return divisor * 16; return divisor * 8;
} }
void APU::Noise::run() { void APU::Noise::run() {

View File

@ -128,7 +128,7 @@ struct Sequencer {
uint1 enable[4]; uint1 enable[4];
uint1 masterenable; uint1 masterenable;
uint13 base; uint12 base;
uint3 step; uint3 step;
int16 lsample; int16 lsample;
int16 rsample; int16 rsample;

View File

@ -1,6 +1,6 @@
void APU::Square::run() { void APU::Square::run() {
if(period && --period == 0) { if(period && --period == 0) {
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
switch(duty) { switch(duty) {
case 0: signal = (phase == 6); break; //_____-_ case 0: signal = (phase == 6); break; //_____-_

View File

@ -10,7 +10,7 @@ void APU::Square1::runsweep(bool update) {
} else if(sweep.shift && update) { } else if(sweep.shift && update) {
shadowfrequency = updatefrequency; shadowfrequency = updatefrequency;
frequency = updatefrequency; frequency = updatefrequency;
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
} }
} }
@ -64,7 +64,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
if(initialize) { if(initialize) {
enable = envelope.dacenable(); enable = envelope.dacenable();
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
volume = envelope.volume; volume = envelope.volume;
shadowfrequency = frequency; shadowfrequency = frequency;

View File

@ -32,7 +32,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
if(initialize) { if(initialize) {
enable = envelope.dacenable(); enable = envelope.dacenable();
period = 4 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
volume = envelope.volume; volume = envelope.volume;
} }

View File

@ -1,6 +1,6 @@
void APU::Wave::run() { void APU::Wave::run() {
if(period && --period == 0) { if(period && --period == 0) {
period = 2 * (2048 - frequency); period = 1 * (2048 - frequency);
patternsample = pattern[patternbank * 16 + patternaddr++]; patternsample = pattern[patternbank * 16 + patternaddr++];
if(patternaddr == 0) patternbank ^= mode; if(patternaddr == 0) patternbank ^= mode;
} }
@ -55,7 +55,7 @@ void APU::Wave::write(unsigned addr, uint8 byte) {
if(initialize) { if(initialize) {
enable = dacenable; enable = dacenable;
period = 2 * (2048 - frequency); period = 1 * (2048 - frequency);
patternaddr = 0; patternaddr = 0;
patternbank = mode ? (uint1)0 : bank; patternbank = mode ? (uint1)0 : bank;
} }

View File

@ -7,13 +7,51 @@ Video video;
void Video::generate_palette(bool color_emulation) { void Video::generate_palette(bool color_emulation) {
//todo: implement LCD color emulation //todo: implement LCD color emulation
for(unsigned color = 0; color < (1 << 15); color++) { for(unsigned color = 0; color < (1 << 15); color++) {
uint5 b = color >> 10; unsigned B = (color >> 10) & 31;
uint5 g = color >> 5; unsigned G = (color >> 5) & 31;
uint5 r = color >> 0; unsigned R = (color >> 0) & 31;
uint16 R = r << 11 | r << 6 | r << 1 | r >> 4; if(color_emulation) {
uint16 G = g << 11 | g << 6 | g << 1 | g >> 4; R = curve[R];
uint16 B = b << 11 | b << 6 | b << 1 | b >> 4; G = curve[G];
B = curve[B];
unsigned Rr = R * 16;
unsigned Gr = R * 4;
unsigned Br = R * 4;
unsigned Rg = G * 8;
unsigned Gg = G * 16;
unsigned Bg = G * 8;
unsigned Rb = B * 0; //intentionally always zero
unsigned Gb = B * 8;
unsigned Bb = B * 16;
if(Rr < Rg) std::swap(Rr, Rg);
if(Rr < Rb) std::swap(Rr, Rb);
if(Rg < Rb) std::swap(Rg, Rb);
if(Gr < Gg) std::swap(Gr, Gg);
if(Gr < Gb) std::swap(Gr, Gb);
if(Gg < Gb) std::swap(Gg, Gb);
if(Br < Bg) std::swap(Br, Bg);
if(Br < Bb) std::swap(Br, Bb);
if(Bg < Bb) std::swap(Bg, Bb);
R = (((4 * Rr + 2 * Rg + Rb) * 160) >> 14) + 32;
G = (((4 * Gr + 2 * Gg + Gb) * 160) >> 14) + 32;
B = (((4 * Br + 2 * Bg + Bb) * 160) >> 14) + 32;
R = R << 8 | R;
G = G << 8 | G;
B = B << 8 | B;
} else {
R = R << 11 | R << 6 | R << 1 | R >> 4;
G = G << 11 | G << 6 | G << 1 | G >> 4;
B = B << 11 | B << 6 | B << 1 | B >> 4;
}
palette[color] = interface->videoColor(color, R, G, B); palette[color] = interface->videoColor(color, R, G, B);
} }
@ -27,4 +65,11 @@ Video::~Video() {
delete[] palette; delete[] palette;
} }
const uint8 Video::curve[32] = {
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38,
0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80,
0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
};
} }

View File

@ -4,6 +4,9 @@ struct Video {
Video(); Video();
~Video(); ~Video();
private:
static const uint8 curve[32];
}; };
extern Video video; extern Video video;

View File

@ -15,8 +15,8 @@ string activepath() {
string realpath(const string& name) { string realpath(const string& name) {
string result; string result;
char path[PATH_MAX] = ""; char path[PATH_MAX] = "";
if(::realpath(name, path)) result = path; if(::realpath(name, path)) result = dir(path);
if(result.empty()) result = {activepath(), name}; if(result.empty()) result = activepath();
result.transform("\\", "/"); result.transform("\\", "/");
if(result.endsWith("/") == false) result.append("/"); if(result.endsWith("/") == false) result.append("/");
return result; return result;
@ -25,15 +25,14 @@ string realpath(const string& name) {
// /home/username/ // /home/username/
// c:/users/username/ // c:/users/username/
string userpath() { string userpath() {
string result;
#if defined(PLATFORM_WINDOWS) #if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L""; wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path); SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path); string result = (const char*)utf8_t(path);
result.transform("\\", "/"); result.transform("\\", "/");
#else #else
struct passwd *userinfo = getpwuid(getuid()); struct passwd* userinfo = getpwuid(getuid());
result = userinfo->pw_dir; string result = userinfo->pw_dir;
#endif #endif
if(result.empty()) result = "."; if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/"); if(result.endsWith("/") == false) result.append("/");
@ -43,49 +42,55 @@ string userpath() {
// /home/username/.config/ // /home/username/.config/
// c:/users/username/appdata/roaming/ // c:/users/username/appdata/roaming/
string configpath() { string configpath() {
string result;
#if defined(PLATFORM_WINDOWS) #if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L""; wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path); string result = (const char*)utf8_t(path);
result.transform("\\", "/"); result.transform("\\", "/");
#elif defined(PLATFORM_MACOSX) #elif defined(PLATFORM_MACOSX)
result = {userpath(), "Library/Application Support/"}; string result = {userpath(), "Library/Application Support/"};
#else #else
result = {userpath(), ".config/"}; string result = {userpath(), ".config/"};
#endif #endif
if(result.empty()) result = "."; if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/"); if(result.endsWith("/") == false) result.append("/");
return result; return result;
} }
// /usr/share
// /Library/Application Support/
// c:/ProgramData/
string sharedpath() { string sharedpath() {
string result;
#if defined(PLATFORM_WINDOWS) #if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L""; wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
result = (const char*)utf8_t(path); string result = (const char*)utf8_t(path);
result.transform("\\", "/"); result.transform("\\", "/");
#elif defined(PLATFORM_MACOSX) #elif defined(PLATFORM_MACOSX)
result = "/Library/Application Support/"; string result = "/Library/Application Support/";
#else #else
result = "/usr/share/"; string result = "/usr/share/";
#endif #endif
if(result.empty()) result = "."; if(result.empty()) result = ".";
if(result.endsWith("/") == false) result.append("/"); if(result.endsWith("/") == false) result.append("/");
return result; return result;
} }
// /tmp
// c:/users/username/AppData/Local/Temp/
string temppath() { string temppath() {
#if defined(PLATFORM_WINDOWS) #if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L""; wchar_t path[PATH_MAX] = L"";
GetTempPathW(PATH_MAX, path); GetTempPathW(PATH_MAX, path);
string result = (const char*)utf8_t(path); string result = (const char*)utf8_t(path);
result.transform("\\", "/"); result.transform("\\", "/");
return result; #elif defined(P_tmpdir)
string result = P_tmpdir;
#else #else
return "/tmp/"; string result = "/tmp/";
#endif #endif
if(result.endsWith("/") == false) result.append("/");
return result;
} }
} }

View File

@ -9,9 +9,9 @@ pObject::pObject(Object& object) : object(object) {
locked = false; locked = false;
} }
pObject* pObject::find(unsigned id) { Object* pObject::find(unsigned id) {
for(auto& item : objects) if(item->id == id) return item; for(auto& item : objects) if(item->id == id) return &item->object;
return 0; return nullptr;
} }
} }

View File

@ -71,7 +71,7 @@ struct pObject {
bool locked; bool locked;
pObject(Object& object); pObject(Object& object);
static pObject* find(unsigned id); static Object* find(unsigned id);
virtual ~pObject() {} virtual ~pObject() {}
void constructor() {} void constructor() {}

View File

@ -278,10 +278,11 @@ static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT
case WM_COMMAND: { case WM_COMMAND: {
unsigned id = LOWORD(wparam); unsigned id = LOWORD(wparam);
HWND control = GetDlgItem(hwnd, id); HWND control = GetDlgItem(hwnd, id);
Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object); Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : pObject::find(id);
if(object == nullptr) break; if(object == nullptr) break;
if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); return FALSE; } if(dynamic_cast<Item*>(object)) { ((Item*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); return FALSE; } if(dynamic_cast<CheckItem*>(object)) { ((CheckItem*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<RadioItem*>(object)) { ((RadioItem*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); return FALSE; } if(dynamic_cast<Button*>(object)) { ((Button*)object)->p.onActivate(); return FALSE; }
if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); return FALSE; } if(dynamic_cast<CheckButton*>(object)) { ((CheckButton*)object)->p.onToggle(); return FALSE; }
if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); return FALSE; } if(dynamic_cast<CheckLabel*>(object)) { ((CheckLabel*)object)->p.onToggle(); return FALSE; }

View File

@ -87,7 +87,7 @@ void SuperFX::disassemble_alt0(char* output) {
case (0x9f): sprintf(t, "fmult"); break; case (0x9f): sprintf(t, "fmult"); break;
case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break; case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break;
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
case (0xc0): sprintf(t, "hib"); case (0xc0): sprintf(t, "hib"); break;
case15(0xc1): sprintf(t, "or r%u", op0 & 15); break; case15(0xc1): sprintf(t, "or r%u", op0 & 15); break;
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
case (0xdf): sprintf(t, "getc"); break; case (0xdf): sprintf(t, "getc"); break;

View File

@ -9,7 +9,7 @@ namespace SuperFamicom {
#include "memory/memory.cpp" #include "memory/memory.cpp"
#include "mmio/mmio.cpp" #include "mmio/mmio.cpp"
#include "timing/timing.cpp" #include "timing/timing.cpp"
#include "disasm/disasm.cpp" #include "disassembler/disassembler.cpp"
SuperFX superfx; SuperFX superfx;

View File

@ -7,7 +7,7 @@ struct SuperFX : Processor::GSU, Coprocessor {
#include "memory/memory.hpp" #include "memory/memory.hpp"
#include "mmio/mmio.hpp" #include "mmio/mmio.hpp"
#include "timing/timing.hpp" #include "timing/timing.hpp"
#include "disasm/disasm.hpp" #include "disassembler/disassembler.hpp"
static void Enter(); static void Enter();
void enter(); void enter();

View File

@ -43,6 +43,7 @@ ConfigurationSettings::ConfigurationSettings() {
append(server, "Server"); append(server, "Server");
library.append(library.selection = -1, "Selection"); library.append(library.selection = -1, "Selection");
library.append(library.mediaMode = 0, "MediaMode");
library.append(library.showOnStartup = true, "ShowOnStartup"); library.append(library.showOnStartup = true, "ShowOnStartup");
append(library, "Library"); append(library, "Library");

View File

@ -48,6 +48,7 @@ struct ConfigurationSettings : Configuration::Document {
struct Library : Configuration::Node { struct Library : Configuration::Node {
signed selection; signed selection;
unsigned mediaMode;
bool showOnStartup; bool showOnStartup;
} library; } library;

View File

@ -68,6 +68,7 @@ void LibraryBrowser::refresh() {
} }
void LibraryBrowser::setMode() { void LibraryBrowser::setMode() {
config->library.mediaMode = mediaMode.selection();
auto& media = emulator.media[mediaMode.selection()]; auto& media = emulator.media[mediaMode.selection()];
pathname = {utility->libraryPath(), media.name, "/"}; pathname = {utility->libraryPath(), media.name, "/"};
@ -100,9 +101,10 @@ void LibraryImport::onImportActivate() {
} }
function<string ()> browse = program->ananke.sym("ananke_browse"); function<string ()> browse = program->ananke.sym("ananke_browse");
if(!browse) return; if(!browse) return;
audio.clear(); //ananke's browser is modal
string pathname = browse(); string pathname = browse();
pathname.rtrim<1>("/");
if(pathname.empty()) return; if(pathname.empty()) return;
MessageWindow().setText({"Successfully imported ", notdir(pathname.rtrim<1>("/"))}).information();
//after importing game, take user to the relevant game list to show the newly imported title //after importing game, take user to the relevant game list to show the newly imported title
string type = extension(pathname); string type = extension(pathname);
@ -115,6 +117,17 @@ void LibraryImport::onImportActivate() {
browser->mediaMode.setSelection(mode); browser->mediaMode.setSelection(mode);
libraryManager->libraryFrame.setSelection(selection); libraryManager->libraryFrame.setSelection(selection);
libraryManager->onChange(); libraryManager->onChange();
//find game in list and select it
string name = notdir(nall::basename(pathname));
for(unsigned n = 0; n < browser->folders.rows(); n++) {
if(browser->folders.text(n, 0) == name) {
browser->folders.setSelection(n);
browser->onChange();
break;
}
}
return; return;
} }
mode++; mode++;
@ -125,6 +138,8 @@ void LibraryImport::onImportActivate() {
} }
LibraryManager::LibraryManager() { LibraryManager::LibraryManager() {
libraryManager = this;
setTitle("Game Library"); setTitle("Game Library");
setGeometry({128, 128, 640, 680}); setGeometry({128, 128, 640, 680});
windowManager->append(this, "LibraryManager"); windowManager->append(this, "LibraryManager");
@ -155,6 +170,11 @@ LibraryManager::LibraryManager() {
//initial config value of -1 defaults to import tab on first launch of higan //initial config value of -1 defaults to import tab on first launch of higan
if(config->library.selection < 0) config->library.selection = browsers.size(); if(config->library.selection < 0) config->library.selection = browsers.size();
libraryFrame.setSelection(config->library.selection); libraryFrame.setSelection(config->library.selection);
if(libraryFrame.selection() < browsers.size()) {
browsers[libraryFrame.selection()]->mediaMode.setSelection(config->library.mediaMode);
browsers[libraryFrame.selection()]->setMode();
}
} }
void LibraryManager::bootstrap() { void LibraryManager::bootstrap() {