diff --git a/src/Makefile b/src/Makefile index d9d81be3..feb11718 100644 --- a/src/Makefile +++ b/src/Makefile @@ -26,7 +26,7 @@ ifeq ($(platform),x) link += -s ruby := video.glx video.xv video.qtraster video.sdl - ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao + ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao ruby += input.sdl input.x link += $(if $(findstring audio.openal,$(ruby)),-lopenal) diff --git a/src/base.hpp b/src/base.hpp index 4827ce03..17bc404a 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,4 +1,4 @@ -static const char bsnesVersion[] = "0.058"; +static const char bsnesVersion[] = "0.059"; static const char bsnesTitle[] = "bsnes"; static const unsigned bsnesSerializerVersion = 4; @@ -15,6 +15,7 @@ static const unsigned bsnesSerializerVersion = 4; #include #include +#include #include #include #include diff --git a/src/cartridge/cartridge.cpp b/src/cartridge/cartridge.cpp index 8ab0e17d..af477d29 100644 --- a/src/cartridge/cartridge.cpp +++ b/src/cartridge/cartridge.cpp @@ -7,6 +7,7 @@ namespace SNES { #include "header.cpp" #include "gameboyheader.cpp" +#include "serialization.cpp" namespace memory { MappedRAM cartrom, cartram, cartrtc; @@ -19,31 +20,28 @@ namespace memory { Cartridge cartridge; void Cartridge::load(Mode cartridge_mode) { - cartinfo_t cartinfo; - read_header(cartinfo, memory::cartrom.data(), memory::cartrom.size()); - set_cartinfo(cartinfo); + mode = cartridge_mode; + read_header(memory::cartrom.data(), memory::cartrom.size()); - set(mode, cartridge_mode); - - if(cartinfo.ram_size > 0) { - memory::cartram.map(allocate(cartinfo.ram_size, 0xff), cartinfo.ram_size); + if(ram_size > 0) { + memory::cartram.map(allocate(ram_size, 0xff), ram_size); } - if(cartinfo.srtc || cartinfo.spc7110rtc) { + if(has_srtc || has_spc7110rtc) { memory::cartrtc.map(allocate(20, 0xff), 20); } - if(mode() == ModeBsx) { + if(mode == ModeBsx) { memory::bsxram.map (allocate( 32 * 1024, 0xff), 32 * 1024); memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); } - if(mode() == ModeSufamiTurbo) { + if(mode == ModeSufamiTurbo) { if(memory::stArom.data()) memory::stAram.map(allocate(128 * 1024, 0xff), 128 * 1024); if(memory::stBrom.data()) memory::stBram.map(allocate(128 * 1024, 0xff), 128 * 1024); } - if(mode() == ModeSuperGameBoy) { + if(mode == ModeSuperGameBoy) { if(memory::gbrom.data()) { unsigned ram_size = gameboy_ram_size(); unsigned rtc_size = gameboy_rtc_size(); @@ -77,10 +75,10 @@ void Cartridge::load(Mode cartridge_mode) { for(unsigned n = 0; n < memory::stBrom.size(); n++) checksum = crc32_adjust(checksum, memory::stBrom[n]); if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0) for(unsigned n = 0; n < memory::gbrom.size(); n++) checksum = crc32_adjust(checksum, memory::gbrom[n]); - set(crc32, ~checksum); + crc32 = ~checksum; #if 0 - fprintf(stdout, "crc32 = %.8x\n", crc32()); + fprintf(stdout, "crc32 = %.8x\n", (unsigned)crc32); sha256_ctx sha; uint8_t shahash[32]; @@ -96,7 +94,7 @@ void Cartridge::load(Mode cartridge_mode) { bus.load_cart(); system.serialize_init(); - set(loaded, true); + loaded = true; } void Cartridge::unload() { @@ -114,55 +112,17 @@ void Cartridge::unload() { memory::gbram.reset(); memory::gbrtc.reset(); - if(loaded() == false) return; + if(loaded == false) return; bus.unload_cart(); - set(loaded, false); + loaded = false; } -Cartridge::Type Cartridge::detect_image_type(uint8_t *data, unsigned size) const { - cartinfo_t info; - read_header(info, data, size); - return info.type; -} - -bool Cartridge::has_21fx() const { return s21fx.exists(); } - -void Cartridge::serialize(serializer &s) { - if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) { - s.array(memory::cartram.data(), memory::cartram.size()); - } - - if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) { - s.array(memory::cartrtc.data(), memory::cartrtc.size()); - } - - if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) { - s.array(memory::bsxram.data(), memory::bsxram.size()); - } - - if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) { - s.array(memory::bsxpram.data(), memory::bsxpram.size()); - } - - if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) { - s.array(memory::stAram.data(), memory::stAram.size()); - } - - if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) { - s.array(memory::stBram.data(), memory::stBram.size()); - } - - if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) { - s.array(memory::gbram.data(), memory::gbram.size()); - } - - if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) { - s.array(memory::gbrtc.data(), memory::gbrtc.size()); - } +bool Cartridge::has_21fx() const { + return s21fx.exists(); } Cartridge::Cartridge() { - set(loaded, false); + loaded = false; unload(); } @@ -170,62 +130,4 @@ Cartridge::~Cartridge() { unload(); } -void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) { - set(region, source.region); - set(mapper, source.mapper); - set(dsp1_mapper, source.dsp1_mapper); - - set(has_bsx_slot, source.bsx_slot); - set(has_superfx, source.superfx); - set(has_sa1, source.sa1); - set(has_srtc, source.srtc); - set(has_sdd1, source.sdd1); - set(has_spc7110, source.spc7110); - set(has_spc7110rtc, source.spc7110rtc); - set(has_cx4, source.cx4); - set(has_dsp1, source.dsp1); - set(has_dsp2, source.dsp2); - set(has_dsp3, source.dsp3); - set(has_dsp4, source.dsp4); - set(has_obc1, source.obc1); - set(has_st010, source.st010); - set(has_st011, source.st011); - set(has_st018, source.st018); -} - -//========== -//cartinfo_t -//========== - -void Cartridge::cartinfo_t::reset() { - type = TypeUnknown; - mapper = LoROM; - dsp1_mapper = DSP1Unmapped; - region = NTSC; - - rom_size = 0; - ram_size = 0; - - bsx_slot = false; - superfx = false; - sa1 = false; - srtc = false; - sdd1 = false; - spc7110 = false; - spc7110rtc = false; - cx4 = false; - dsp1 = false; - dsp2 = false; - dsp3 = false; - dsp4 = false; - obc1 = false; - st010 = false; - st011 = false; - st018 = false; -} - -Cartridge::cartinfo_t::cartinfo_t() { - reset(); -} - } diff --git a/src/cartridge/cartridge.hpp b/src/cartridge/cartridge.hpp index b1de06e1..3773e926 100644 --- a/src/cartridge/cartridge.hpp +++ b/src/cartridge/cartridge.hpp @@ -1,4 +1,4 @@ -class Cartridge : public property { +class Cartridge : property { public: enum Mode { ModeNormal, @@ -15,7 +15,8 @@ public: TypeBsx, TypeSufamiTurboBios, TypeSufamiTurbo, - TypeSuperGameBoyBios, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, TypeGameBoy, TypeUnknown, }; @@ -46,61 +47,41 @@ public: DSP1HiROM, }; - //properties can be read via operator(), eg "if(cartridge.loaded() == true)"; - //warning: if loaded() == false, no other property is considered valid! + readonly loaded; //is a base cartridge inserted? + readonly crc32; //crc32 of all cartridges (base+slot(s)) - property_t loaded; //is a base cartridge inserted? - property_t crc32; //crc32 of all files sans headers + readonly mode; + readonly type; + readonly region; + readonly mapper; + readonly dsp1_mapper; - property_t mode; - property_t region; - property_t mapper; - property_t dsp1_mapper; - - property_t has_bsx_slot; - property_t has_superfx; - property_t has_sa1; - property_t has_srtc; - property_t has_sdd1; - property_t has_spc7110, has_spc7110rtc; - property_t has_cx4; - property_t has_dsp1, has_dsp2, has_dsp3, has_dsp4; - property_t has_obc1; - property_t has_st010, has_st011, has_st018; + readonly has_bsx_slot; + readonly has_superfx; + readonly has_sa1; + readonly has_srtc; + readonly has_sdd1; + readonly has_spc7110; + readonly has_spc7110rtc; + readonly has_cx4; + readonly has_dsp1; + readonly has_dsp2; + readonly has_dsp3; + readonly has_dsp4; + readonly has_obc1; + readonly has_st010; + readonly has_st011; + readonly has_st018; bool has_21fx() const; - //main interface void load(Mode); void unload(); - Type detect_image_type(uint8_t *data, unsigned size) const; void serialize(serializer&); Cartridge(); ~Cartridge(); private: - struct cartinfo_t { - Type type; - Region region; - MemoryMapper mapper; - DSP1MemoryMapper dsp1_mapper; - unsigned rom_size, ram_size; - - bool bsx_slot; - bool superfx; - bool sa1; - bool srtc; - bool sdd1; - bool spc7110, spc7110rtc; - bool cx4; - bool dsp1, dsp2, dsp3, dsp4; - bool obc1; - bool st010, st011, st018; - - void reset(); - cartinfo_t(); - }; - enum HeaderField { CartName = 0x00, Mapper = 0x15, @@ -115,10 +96,10 @@ private: ResetVector = 0x3c, }; - void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const; + unsigned ram_size; + void read_header(const uint8_t *data, unsigned size); unsigned find_header(const uint8_t *data, unsigned size) const; unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; - void set_cartinfo(const cartinfo_t&); unsigned gameboy_ram_size() const; unsigned gameboy_rtc_size() const; diff --git a/src/cartridge/header.cpp b/src/cartridge/header.cpp index 37c8ba7f..805029ad 100644 --- a/src/cartridge/header.cpp +++ b/src/cartridge/header.cpp @@ -1,7 +1,28 @@ #ifdef CARTRIDGE_CPP -void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const { - info.reset(); +void Cartridge::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; //===================== //detect Game Boy carts @@ -10,23 +31,23 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size if(size >= 0x0140) { if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { - info.type = TypeGameBoy; + type = TypeGameBoy; return; } } const unsigned index = find_header(data, size); - const uint8 mapper = data[index + Mapper]; + const uint8 mapperid = data[index + Mapper]; const uint8 rom_type = data[index + RomType]; const uint8 rom_size = data[index + RomSize]; const uint8 company = data[index + Company]; - const uint8 region = data[index + CartRegion] & 0x7f; + const uint8 regionid = data[index + CartRegion] & 0x7f; - info.ram_size = 1024 << (data[index + RamSize] & 7); - if(info.ram_size == 1024) info.ram_size = 0; //no RAM present, eg RamSize == 0 + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present //0, 1, 13 = NTSC; 2 - 12 = PAL - info.region = (region <= 1 || region >= 13) ? NTSC : PAL; + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; //======================= //detect BS-X flash carts @@ -37,9 +58,9 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size const uint8_t n15 = data[index + 0x15]; if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { - info.type = TypeBsx; - info.mapper = BSXROM; - info.region = NTSC; //BS-X only released in Japan + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan return; } } @@ -52,21 +73,26 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size if(!memcmp(data, "BANDAI SFC-ADX", 14)) { if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { - info.type = TypeSufamiTurboBios; + type = TypeSufamiTurboBios; } else { - info.type = TypeSufamiTurbo; + type = TypeSufamiTurbo; } - info.mapper = STROM; - info.region = NTSC; //Sufami Turbo only released in Japan - return; //RAM size handled internally by load_cart_st(); + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine } //========================== //detect Super Game Boy BIOS //========================== + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + if(!memcmp(data + index, "Super GAMEBOY", 13)) { - info.type = TypeSuperGameBoyBios; + type = TypeSuperGameBoy1Bios; return; } @@ -80,118 +106,119 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size uint8 n13 = data[index - 13]; if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { - info.bsx_slot = true; + has_bsx_slot = true; } } } } - if(info.bsx_slot == true) { + if(has_bsx_slot) { if(!memcmp(data + index, "Satellaview BS-X ", 21)) { //BS-X base cart - info.type = TypeBsxBios; - info.mapper = BSXROM; - info.region = NTSC; //BS-X only released in Japan - return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class } else { - info.type = TypeBsxSlotted; - info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan } } else { //standard cart - info.type = TypeNormal; + type = TypeNormal; if(index == 0x7fc0 && size >= 0x401000) { - info.mapper = ExLoROM; - } else if(index == 0x7fc0 && mapper == 0x32) { - info.mapper = ExLoROM; + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; } else if(index == 0x7fc0) { - info.mapper = LoROM; + mapper = LoROM; } else if(index == 0xffc0) { - info.mapper = HiROM; + mapper = HiROM; } else { //index == 0x40ffc0 - info.mapper = ExHiROM; + mapper = ExHiROM; } } - if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { - info.superfx = true; - info.mapper = SuperFXROM; - info.ram_size = 1024 << (data[index - 3] & 7); - if(info.ram_size == 1024) info.ram_size = 0; + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; } - if(mapper == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { - info.sa1 = true; - info.mapper = SA1ROM; + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; } - if(mapper == 0x35 && rom_type == 0x55) { - info.srtc = true; + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; } - if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { - info.sdd1 = true; + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; } - if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { - info.spc7110 = true; - info.spc7110rtc = (rom_type == 0xf9); - info.mapper = SPC7110ROM; + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; } - if(mapper == 0x20 && rom_type == 0xf3) { - info.cx4 = true; + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; } - if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) { - info.dsp1 = true; + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; } - if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) { - info.dsp1 = true; + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; } - if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { - info.dsp1 = true; + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; } - if(info.dsp1 == true) { - if((mapper & 0x2f) == 0x20 && size <= 0x100000) { - info.dsp1_mapper = DSP1LoROM1MB; - } else if((mapper & 0x2f) == 0x20) { - info.dsp1_mapper = DSP1LoROM2MB; - } else if((mapper & 0x2f) == 0x21) { - info.dsp1_mapper = DSP1HiROM; + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; } } - if(mapper == 0x20 && rom_type == 0x05) { - info.dsp2 = true; + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; } - if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) { - info.dsp3 = true; + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; } - if(mapper == 0x30 && rom_type == 0x03) { - info.dsp4 = true; + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; } - if(mapper == 0x30 && rom_type == 0x25) { - info.obc1 = true; + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; } - if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) { - info.st010 = true; + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; } - if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) { - info.st011 = true; + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; } - if(mapper == 0x30 && rom_type == 0xf5) { - info.st018 = true; + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; } } diff --git a/src/cartridge/serialization.cpp b/src/cartridge/serialization.cpp new file mode 100644 index 00000000..847b2354 --- /dev/null +++ b/src/cartridge/serialization.cpp @@ -0,0 +1,37 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) { + s.array(memory::cartram.data(), memory::cartram.size()); + } + + if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) { + s.array(memory::cartrtc.data(), memory::cartrtc.size()); + } + + if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) { + s.array(memory::bsxram.data(), memory::bsxram.size()); + } + + if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) { + s.array(memory::bsxpram.data(), memory::bsxpram.size()); + } + + if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) { + s.array(memory::stAram.data(), memory::stAram.size()); + } + + if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) { + s.array(memory::stBram.data(), memory::stBram.size()); + } + + if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) { + s.array(memory::gbram.data(), memory::gbram.size()); + } + + if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) { + s.array(memory::gbrtc.data(), memory::gbrtc.size()); + } +} + +#endif diff --git a/src/cheat/cheat-inline.hpp b/src/cheat/cheat-inline.hpp index a1886bc1..a80f47f3 100644 --- a/src/cheat/cheat-inline.hpp +++ b/src/cheat/cheat-inline.hpp @@ -1,3 +1,2 @@ -unsigned Cheat::count() const { return code.size(); } bool Cheat::active() const { return cheat_enabled; } -bool Cheat::exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); } +bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); } diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index b9aad5de..381f6e21 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -5,230 +5,73 @@ namespace SNES { Cheat cheat; -Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) { - enabled = source.enabled; - code = source.code; - desc = source.desc; - count = source.count; - - addr.reset(); - data.reset(); - for(unsigned n = 0; n < count; n++) { - addr[n] = source.addr[n]; - data[n] = source.data[n]; - } - - return *this; +bool Cheat::enabled() const { + return system_enabled; } -//used to sort cheat code list by description -bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) { - return strcmp(desc, source.desc) < 0; +void Cheat::enable(bool state) { + system_enabled = state; + cheat_enabled = system_enabled && code_enabled; } -//parse item ("0123-4567+89AB-CDEF"), return cheat_t item -//return true if code is valid, false otherwise -bool Cheat::decode(const char *s, Cheat::cheat_t &item) const { - item.enabled = false; - item.count = 0; +void Cheat::synchronize() { + memset(bitmask, 0x00, sizeof bitmask); + code_enabled = false; - string code = s; - code.replace(" ", ""); + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; - lstring list; - list.split("+", code); + for(unsigned n = 0; n < code.addr.size(); n++) { + code_enabled = true; - for(unsigned n = 0; n < list.size(); n++) { - unsigned addr; - uint8_t data; - type_t type; - if(decode(list[n], addr, data, type) == false) { - item.count = 0; - return false; + unsigned addr = mirror(code.addr[n]); + bitmask[addr >> 3] |= 1 << (addr & 7); + if((addr & 0xffe000) == 0x7e0000) { + //mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff + unsigned mirroraddr; + for(unsigned x = 0; x <= 0x3f; x++) { + mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + + mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + } + } } - - item.addr[item.count] = addr; - item.data[item.count] = data; - item.count++; } - return true; + cheat_enabled = system_enabled && code_enabled; } -//read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code. -//returns true if cheat code was found, false if it was not. -//when true, cheat code substitution value is stored in data. -bool Cheat::read(unsigned addr, uint8_t &data) const { - addr = mirror_address(addr); - for(unsigned i = 0; i < code.size(); i++) { - if(enabled(i) == false) continue; +bool Cheat::read(unsigned addr, uint8 &data) const { + addr = mirror(addr); - for(unsigned n = 0; n < code[i].count; n++) { - if(addr == mirror_address(code[i].addr[n])) { - data = code[i].data[n]; + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; + + for(unsigned n = 0; n < code.addr.size(); n++) { + if(addr == mirror(code.addr[n])) { + data = code.data[n]; return true; } } } - //code not found, or code is disabled return false; } -//============== -//master control -//============== - -//global cheat system enable/disable: -//if disabled, *all* cheat codes are disabled; -//otherwise only individually disabled codes are. - -bool Cheat::enabled() const { - return cheat_system_enabled; +Cheat::Cheat() { + system_enabled = true; + synchronize(); } -void Cheat::enable() { - cheat_system_enabled = true; - cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists); -} +//=============== +//encode / decode +//=============== -void Cheat::disable() { - cheat_system_enabled = false; - cheat_enabled = false; -} - -//================================ -//cheat list manipulation routines -//================================ - -void Cheat::add(bool enable, const char *code_, const char *desc_) { - cheat_t item; - decode(code_, item); - - unsigned i = code.size(); - code[i] = item; - code[i].enabled = enable; - code[i].desc = desc_; - code[i].code = code_; - encode_description(code[i].desc); - update(code[i]); - - update_cheat_status(); -} - -void Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) { - cheat_t item; - decode(code_, item); - - //disable current code and clear from code lookup table - code[i].enabled = false; - update(code[i]); - - code[i] = item; - code[i].enabled = enable; - code[i].desc = desc_; - code[i].code = code_; - encode_description(code[i].desc); - update(code[i]); - - update_cheat_status(); -} - -bool Cheat::remove(unsigned i) { - unsigned size = code.size(); - if(i >= size) return false; //also verifies size cannot be < 1 - - for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1]; - code.resize(size - 1); - - update_cheat_status(); - return true; -} - -bool Cheat::get(unsigned i, cheat_t &item) const { - if(i >= code.size()) return false; - - item = code[i]; - decode_description(item.desc); - return true; -} - -//============================== -//cheat status modifier routines -//============================== - -bool Cheat::enabled(unsigned i) const { - return (i < code.size() ? code[i].enabled : false); -} - -void Cheat::enable(unsigned i) { - if(i >= code.size()) return; - - code[i].enabled = true; - update(code[i]); - update_cheat_status(); -} - -void Cheat::disable(unsigned i) { - if(i >= code.size()) return; - - code[i].enabled = false; - update(code[i]); - update_cheat_status(); -} - -//=============================== -//cheat file load / save routines -// -//file format: -//"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n -//... -//=============================== - -void Cheat::load(string data) { - data.replace("\r", ""); - data.qreplace(" ", ""); - - lstring line; - line.split("\n", data); - for(unsigned i = 0; i < line.size(); i++) { - lstring part; - part.qsplit(",", line[i]); - if(part.size() != 3) continue; - trim(part[2], "\""); - add(part[0] == "enabled", /* code = */ part[1], /* desc = */ part[2]); - } -} - -string Cheat::save() const { - string data; - for(unsigned i = 0; i < code.size(); i++) { - data << (code[i].enabled ? "enabled," : "disabled,") - << code[i].code << "," - << "\"" << code[i].desc << "\"\r\n"; - } - return data; -} - -void Cheat::clear() { - cheat_enabled_code_exists = false; - memset(mask, 0, 0x200000); - code.reset(); -} - -Cheat::Cheat() : cheat_system_enabled(true) { - clear(); -} - -//================== -//internal functions -//================== - -//string <> binary code translation routines -//decode() "7e123456" -> 0x7e123456 -//encode() 0x7e123456 -> "7e123456" - -bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const { +bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) { string t = s; strlower(t); @@ -274,9 +117,11 @@ bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) c } else { return false; } + + #undef ischr } -bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const { +bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) { char t[16]; if(type == ProActionReplay) { @@ -306,90 +151,46 @@ bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const { } } -//speed up S-CPU memory reads by disabling cheat code lookup when either: -//a) cheat system is disabled by user, or b) no enabled cheat codes exist -void Cheat::update_cheat_status() { - for(unsigned i = 0; i < code.size(); i++) { - if(code[i].enabled) { - cheat_enabled_code_exists = true; - cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists); - return; +//======== +//internal +//======== + +unsigned Cheat::mirror(unsigned addr) const { + //$00-3f|80-bf:0000-1fff -> $7e:0000-1fff + if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff)); + return addr; +} + +//========= +//CheatCode +//========= + +bool CheatCode::operator=(string s) { + addr.reset(); + data.reset(); + + lstring list; + list.split("+", s.replace(" ", "")); + + for(unsigned i = 0; i < list.size(); i++) { + unsigned addr_; + uint8 data_; + Cheat::Type type_; + if(Cheat::decode(list[i], addr_, data_, type_) == false) { + addr.reset(); + data.reset(); + return false; } + + addr.add(addr_); + data.add(data_); } - cheat_enabled_code_exists = false; - cheat_enabled = false; + + return true; } -//address lookup table manipulation and mirroring -//mirror_address() 0x000000 -> 0x7e0000 -//set() enable specified address, mirror accordingly -//clear() disable specified address, mirror accordingly -unsigned Cheat::mirror_address(unsigned addr) const { - if((addr & 0x40e000) != 0x0000) return addr; - //8k WRAM mirror - //$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff] - return (0x7e0000 + (addr & 0x1fff)); +CheatCode::CheatCode() { + enabled = false; } -//updates mask[] table enabled bits; -//must be called after modifying item.enabled state. -void Cheat::update(const cheat_t &item) { - for(unsigned n = 0; n < item.count; n++) { - (item.enabled) ? set(item.addr[n]) : clear(item.addr[n]); - } } - -void Cheat::set(unsigned addr) { - addr = mirror_address(addr); - - mask[addr >> 3] |= 1 << (addr & 7); - if((addr & 0xffe000) == 0x7e0000) { - //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] - unsigned mirror; - for(unsigned x = 0; x <= 0x3f; x++) { - mirror = ((0x00 + x) << 16) + (addr & 0x1fff); - mask[mirror >> 3] |= 1 << (mirror & 7); - mirror = ((0x80 + x) << 16) + (addr & 0x1fff); - mask[mirror >> 3] |= 1 << (mirror & 7); - } - } -} - -void Cheat::clear(unsigned addr) { - addr = mirror_address(addr); - - //if there is more than one cheat code using the same address, - //(eg with a different override value) then do not clear code - //lookup table entry. - uint8_t r; - if(read(addr, r) == true) return; - - mask[addr >> 3] &= ~(1 << (addr & 7)); - if((addr & 0xffe000) == 0x7e0000) { - //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff] - unsigned mirror; - for(unsigned x = 0; x <= 0x3f; x++) { - mirror = ((0x00 + x) << 16) + (addr & 0x1fff); - mask[mirror >> 3] &= ~(1 << (mirror & 7)); - mirror = ((0x80 + x) << 16) + (addr & 0x1fff); - mask[mirror >> 3] &= ~(1 << (mirror & 7)); - } - } -} - -//these two functions are used to safely store description text inside .cfg file format. - -string& Cheat::encode_description(string &desc) const { - desc.replace("\"", "\\q"); - desc.replace("\n", "\\n"); - return desc; -} - -string& Cheat::decode_description(string &desc) const { - desc.replace("\\q", "\""); - desc.replace("\\n", "\n"); - return desc; -} - -}; - diff --git a/src/cheat/cheat.hpp b/src/cheat/cheat.hpp index 7c80d53b..388085c8 100644 --- a/src/cheat/cheat.hpp +++ b/src/cheat/cheat.hpp @@ -1,69 +1,35 @@ -class Cheat { +struct CheatCode { + bool enabled; + array addr; + array data; + + bool operator=(string); + CheatCode(); +}; + +class Cheat : public vector { public: - enum type_t { - ProActionReplay, - GameGenie, - }; - - struct cheat_t { - bool enabled; - string code; - string desc; - - unsigned count; - array addr; - array data; - - cheat_t& operator=(const cheat_t&); - bool operator<(const cheat_t&); - }; - - bool decode(const char *s, cheat_t &item) const; - bool read(unsigned addr, uint8_t &data) const; + enum Type { ProActionReplay, GameGenie }; bool enabled() const; - void enable(); - void disable(); + void enable(bool); + void synchronize(); + bool read(unsigned, uint8&) const; - inline unsigned count() const; inline bool active() const; inline bool exists(unsigned addr) const; - void add(bool enable, const char *code, const char *desc); - void edit(unsigned i, bool enable, const char *code, const char *desc); - bool remove(unsigned i); - bool get(unsigned i, cheat_t &item) const; - - bool enabled(unsigned i) const; - void enable(unsigned i); - void disable(unsigned i); - - void load(string data); - string save() const; - void clear(); - Cheat(); + static bool decode(const char*, unsigned&, uint8&, Type&); + static bool encode(string&, unsigned, uint8, Type); + private: - bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled); - bool cheat_enabled_code_exists; - bool cheat_system_enabled; - - uint8_t mask[0x200000]; - vector code; - - bool decode(const char *str, unsigned &addr, uint8_t &data, type_t &type) const; - bool encode(string &str, unsigned addr, uint8_t data, type_t type) const; - - void update_cheat_status(); - unsigned mirror_address(unsigned addr) const; - - void update(const cheat_t& item); - void set(unsigned addr); - void clear(unsigned addr); - - string& encode_description(string &desc) const; - string& decode_description(string &desc) const; + uint8 bitmask[0x200000]; + bool system_enabled; + bool code_enabled; + bool cheat_enabled; + unsigned mirror(unsigned) const; }; extern Cheat cheat; diff --git a/src/chip/21fx/21fx.cpp b/src/chip/21fx/21fx.cpp index 2f38a309..d1ff1ddc 100644 --- a/src/chip/21fx/21fx.cpp +++ b/src/chip/21fx/21fx.cpp @@ -1,7 +1,9 @@ #include <../base.hpp> +//B-bus interface + //$21f0 command port (r/w) -//------------------- +//------------------------- //$00 set data port address (sr[3-0] = address) //$01 set audio track number (sr[1-0] = track number) //$02 set volume (sr[1] = left, sr[0] = right) @@ -11,16 +13,37 @@ //d6 = audio port busy //d5 = audio playing //d4 = reserved (0) -//d3-d0 = version (1) +//d3-d0 = version (0) // // //$21f1 parameter port (w) -//--------------------- +//------------------------- //(shift register) // // //$21f2 data port (r) -//---------------- +//-------------------- +//(auto-increment read port) + +//A-bus interface + +//$2200 command port (r/w) +//------------------------- +//$00 set data port address (sr[3-0] = address) +//$01 set audio track number (sr[1-0] = track number) +//$02 set volume (sr[1] = left, sr[0] = right) +//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat) +// +//d7 = data port busy +//d6 = audio port busy +//d5 = audio playing +//d4 = reserved (0) +//d3-d0 = version (0) +// +//$2201 data port (r/w) +//---------------------- +//(shift register) +// //(auto-increment read port) #define S21FX_CPP @@ -34,6 +57,10 @@ void S21fx::enter() { scheduler.clock.cop_freq = 44100; while(true) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + int16 left = 0, right = 0; if((mmio.status & AudioPlaying) && !mmio.audio_pause) { @@ -71,6 +98,9 @@ void S21fx::enable() { memory::mmio.map(i, *this); } + memory::mmio.map(0x2200, *this); + memory::mmio.map(0x2201, *this); + if(datafile.open()) datafile.close(); datafile.open(string() << basepath << "21fx.bin", file::mode_read); } @@ -95,11 +125,11 @@ void S21fx::reset() { uint8 S21fx::mmio_read(unsigned addr) { addr &= 0xffff; - if(addr == 0x21f0) { - return mmio.status | 0x01; + if((addr == 0x21f0) || (addr == 0x2200)) { + return mmio.status | 0x00; } - if(addr == 0x21f2) { + if((addr == 0x21f2) || (addr == 0x2201)) { if(mmio.status & DataPortBusy) return 0x00; mmio.data_offset++; if(datafile.open()) return datafile.read(); @@ -112,7 +142,7 @@ uint8 S21fx::mmio_read(unsigned addr) { void S21fx::mmio_write(unsigned addr, uint8 data) { addr &= 0xffff; - if(addr == 0x21f0) { + if((addr == 0x21f0) || (addr == 0x2200)) { if(data == 0x00) { mmio.data_offset = mmio.shift_register & 0xffffffff; if(datafile.open()) { @@ -146,7 +176,7 @@ void S21fx::mmio_write(unsigned addr, uint8 data) { mmio.shift_register = 0; } - if(addr == 0x21f1) { + if((addr == 0x21f1) || (addr == 0x2201)) { mmio.shift_register = (mmio.shift_register << 8) | data; } } diff --git a/src/chip/sa1/bus/bus.cpp b/src/chip/sa1/bus/bus.cpp index 160adba4..062328d0 100644 --- a/src/chip/sa1/bus/bus.cpp +++ b/src/chip/sa1/bus/bus.cpp @@ -4,14 +4,14 @@ VBRBus vbrbus; SA1Bus sa1bus; namespace memory { - static StaticRAM iram(2048); - //accessed by: - static VectorSelectionPage vectorsp; //S-CPU + SA-1 - static CPUIRAM cpuiram; //S-CPU - static SA1IRAM sa1iram; //SA-1 - static SA1BWRAM sa1bwram; //SA-1 - static CC1BWRAM cc1bwram; //S-CPU - static BitmapRAM bitmapram; //SA-1 + StaticRAM iram(2048); + //accessed by: + VectorSelectionPage vectorsp; //S-CPU + SA-1 + CPUIRAM cpuiram; //S-CPU + SA1IRAM sa1iram; //SA-1 + SA1BWRAM sa1bwram; //SA-1 + CC1BWRAM cc1bwram; //S-CPU + BitmapRAM bitmapram; //SA-1 } //$230c (VDPL), $230d (VDPH) use this bus to read variable-length data. diff --git a/src/chip/sa1/bus/bus.hpp b/src/chip/sa1/bus/bus.hpp index 4d680b21..bec2f82d 100644 --- a/src/chip/sa1/bus/bus.hpp +++ b/src/chip/sa1/bus/bus.hpp @@ -43,3 +43,14 @@ struct BitmapRAM : Memory { alwaysinline uint8 read(unsigned); alwaysinline void write(unsigned, uint8); }; + +namespace memory { + extern StaticRAM iram; + + extern VectorSelectionPage vectorsp; + extern CPUIRAM cpuiram; + extern SA1IRAM sa1iram; + extern SA1BWRAM sa1bwram; + extern CC1BWRAM cc1bwram; + extern BitmapRAM bitmapram; +}; diff --git a/src/chip/sa1/sa1.cpp b/src/chip/sa1/sa1.cpp index 5e65bdf5..82470a44 100644 --- a/src/chip/sa1/sa1.cpp +++ b/src/chip/sa1/sa1.cpp @@ -5,6 +5,7 @@ namespace SNES { SA1 sa1; +#include "serialization.cpp" #include "bus/bus.cpp" #include "dma/dma.cpp" #include "memory/memory.cpp" @@ -12,10 +13,15 @@ SA1 sa1; void SA1::enter() { while(true) { - while(mmio.sa1_rdyb || mmio.sa1_resb) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + if(mmio.sa1_rdyb || mmio.sa1_resb) { //SA-1 co-processor is asleep tick(); scheduler.sync_copcpu(); + continue; } if(status.interrupt_pending) { @@ -139,7 +145,7 @@ void SA1::reset() { regs.e = 1; regs.mdr = 0x00; regs.wai = false; - update_table(); + CPUcore::update_table(); status.tick_counter = 0; diff --git a/src/chip/sa1/sa1.hpp b/src/chip/sa1/sa1.hpp index 69f47ba9..83abc93e 100644 --- a/src/chip/sa1/sa1.hpp +++ b/src/chip/sa1/sa1.hpp @@ -30,6 +30,7 @@ public: void power(); void reset(); + void serialize(serializer&); SA1(); }; diff --git a/src/chip/sa1/serialization.cpp b/src/chip/sa1/serialization.cpp new file mode 100644 index 00000000..9d599c1c --- /dev/null +++ b/src/chip/sa1/serialization.cpp @@ -0,0 +1,150 @@ +#ifdef SA1_CPP + +void SA1::serialize(serializer &s) { + CPUcore::core_serialize(s); + + //sa1.hpp + s.integer(status.tick_counter); + + s.integer(status.interrupt_pending); + s.integer(status.interrupt_vector); + + s.integer(status.scanlines); + s.integer(status.vcounter); + s.integer(status.hcounter); + + //bus/bus.hpp + s.array(memory::iram.data(), memory::iram.size()); + + memory::vectorsp.sync(); + + s.integer(memory::cc1bwram.dma); + + //dma/dma.hpp + s.integer(dma.line); + + //mmio/mmio.hpp + s.integer(mmio.sa1_irq); + s.integer(mmio.sa1_rdyb); + s.integer(mmio.sa1_resb); + s.integer(mmio.sa1_nmi); + s.integer(mmio.smeg); + + s.integer(mmio.cpu_irqen); + s.integer(mmio.chdma_irqen); + + s.integer(mmio.cpu_irqcl); + s.integer(mmio.chdma_irqcl); + + s.integer(mmio.crv); + + s.integer(mmio.cnv); + + s.integer(mmio.civ); + + s.integer(mmio.cpu_irq); + s.integer(mmio.cpu_ivsw); + s.integer(mmio.cpu_nvsw); + s.integer(mmio.cmeg); + + s.integer(mmio.sa1_irqen); + s.integer(mmio.timer_irqen); + s.integer(mmio.dma_irqen); + s.integer(mmio.sa1_nmien); + + s.integer(mmio.sa1_irqcl); + s.integer(mmio.timer_irqcl); + s.integer(mmio.dma_irqcl); + s.integer(mmio.sa1_nmicl); + + s.integer(mmio.snv); + + s.integer(mmio.siv); + + s.integer(mmio.hvselb); + s.integer(mmio.ven); + s.integer(mmio.hen); + + s.integer(mmio.hcnt); + + s.integer(mmio.vcnt); + + s.integer(mmio.cbmode); + s.integer(mmio.cb); + + s.integer(mmio.dbmode); + s.integer(mmio.db); + + s.integer(mmio.ebmode); + s.integer(mmio.eb); + + s.integer(mmio.fbmode); + s.integer(mmio.fb); + + s.integer(mmio.sbm); + + s.integer(mmio.sw46); + s.integer(mmio.cbm); + + s.integer(mmio.swen); + + s.integer(mmio.cwen); + + s.integer(mmio.bwp); + + s.integer(mmio.siwp); + + s.integer(mmio.ciwp); + + s.integer(mmio.dmaen); + s.integer(mmio.dprio); + s.integer(mmio.cden); + s.integer(mmio.cdsel); + s.integer(mmio.dd); + s.integer(mmio.sd); + + s.integer(mmio.chdend); + s.integer(mmio.dmasize); + s.integer(mmio.dmacb); + + s.integer(mmio.dsa); + + s.integer(mmio.dda); + + s.integer(mmio.dtc); + + s.integer(mmio.bbf); + + s.array(mmio.brf); + + s.integer(mmio.acm); + s.integer(mmio.md); + + s.integer(mmio.ma); + + s.integer(mmio.mb); + + s.integer(mmio.hl); + s.integer(mmio.vb); + + s.integer(mmio.va); + s.integer(mmio.vbit); + + s.integer(mmio.cpu_irqfl); + s.integer(mmio.chdma_irqfl); + + s.integer(mmio.sa1_irqfl); + s.integer(mmio.timer_irqfl); + s.integer(mmio.dma_irqfl); + s.integer(mmio.sa1_nmifl); + + s.integer(mmio.hcr); + + s.integer(mmio.vcr); + + s.integer(mmio.mr); + + s.integer(mmio.overflow); +} + +#endif diff --git a/src/chip/superfx/bus/bus.cpp b/src/chip/superfx/bus/bus.cpp index d41112a7..ac932ac4 100644 --- a/src/chip/superfx/bus/bus.cpp +++ b/src/chip/superfx/bus/bus.cpp @@ -3,11 +3,11 @@ SuperFXBus superfxbus; namespace memory { - static SuperFXGSUROM gsurom; - static SuperFXGSURAM gsuram; - static SuperFXCPUROM fxrom; - static SuperFXCPURAM fxram; -}; + SuperFXGSUROM gsurom; + SuperFXGSURAM gsuram; + SuperFXCPUROM fxrom; + SuperFXCPURAM fxram; +} void SuperFXBus::init() { map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); @@ -34,7 +34,7 @@ unsigned SuperFXGSUROM::size() const { } uint8 SuperFXGSUROM::read(unsigned addr) { - while(!superfx.regs.scmr.ron) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) { superfx.add_clocks(6); scheduler.sync_copcpu(); } @@ -42,7 +42,7 @@ uint8 SuperFXGSUROM::read(unsigned addr) { } void SuperFXGSUROM::write(unsigned addr, uint8 data) { - while(!superfx.regs.scmr.ron) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) { superfx.add_clocks(6); scheduler.sync_copcpu(); } @@ -54,7 +54,7 @@ unsigned SuperFXGSURAM::size() const { } uint8 SuperFXGSURAM::read(unsigned addr) { - while(!superfx.regs.scmr.ran) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) { superfx.add_clocks(6); scheduler.sync_copcpu(); } @@ -62,7 +62,7 @@ uint8 SuperFXGSURAM::read(unsigned addr) { } void SuperFXGSURAM::write(unsigned addr, uint8 data) { - while(!superfx.regs.scmr.ran) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) { superfx.add_clocks(6); scheduler.sync_copcpu(); } diff --git a/src/chip/superfx/bus/bus.hpp b/src/chip/superfx/bus/bus.hpp index af37da1e..dd615c65 100644 --- a/src/chip/superfx/bus/bus.hpp +++ b/src/chip/superfx/bus/bus.hpp @@ -25,3 +25,10 @@ struct SuperFXCPURAM : Memory { uint8 read(unsigned); void write(unsigned, uint8); }; + +namespace memory { + extern SuperFXGSUROM gsurom; + extern SuperFXGSURAM gsuram; + extern SuperFXCPUROM fxrom; + extern SuperFXCPURAM fxram; +} diff --git a/src/chip/superfx/core/opcodes.cpp b/src/chip/superfx/core/opcodes.cpp index 5817079f..7d2f13a2 100644 --- a/src/chip/superfx/core/opcodes.cpp +++ b/src/chip/superfx/core/opcodes.cpp @@ -114,7 +114,7 @@ void SuperFX::op_bvs() { //$10-1f(b1): move rN template void SuperFX::op_to_r() { if(regs.sfr.b == 0) { - regs.dreg = ®s.r[n]; + regs.dreg = n; } else { regs.r[n] = regs.sr(); regs.reset(); @@ -123,8 +123,8 @@ template void SuperFX::op_to_r() { //$20-2f: with rN template void SuperFX::op_with_r() { - regs.sreg = ®s.r[n]; - regs.dreg = ®s.r[n]; + regs.sreg = n; + regs.dreg = n; regs.sfr.b = 1; } @@ -519,7 +519,7 @@ template void SuperFX::op_sms_r() { //$b0-bf(b1): moves rN template void SuperFX::op_from_r() { if(regs.sfr.b == 0) { - regs.sreg = ®s.r[n]; + regs.sreg = n; } else { regs.dr() = regs.r[n]; regs.sfr.ov = (regs.dr() & 0x80); diff --git a/src/chip/superfx/core/registers.hpp b/src/chip/superfx/core/registers.hpp index 67537929..7346f631 100644 --- a/src/chip/superfx/core/registers.hpp +++ b/src/chip/superfx/core/registers.hpp @@ -149,17 +149,17 @@ struct regs_t { uint16 ramar; //RAM buffer address register uint8 ramdr; //RAM buffer data register - reg16_t *sreg, *dreg; - reg16_t& sr() { return *sreg; } //source register (from) - reg16_t& dr() { return *dreg; } //destination register (to) + unsigned sreg, dreg; + reg16_t& sr() { return r[sreg]; } //source register (from) + reg16_t& dr() { return r[dreg]; } //destination register (to) void reset() { sfr.b = 0; sfr.alt1 = 0; sfr.alt2 = 0; - sreg = &r[0]; - dreg = &r[0]; + sreg = 0; + dreg = 0; } } regs; diff --git a/src/chip/superfx/disasm/disasm.cpp b/src/chip/superfx/disasm/disasm.cpp index 9ec016b5..c2d3e387 100644 --- a/src/chip/superfx/disasm/disasm.cpp +++ b/src/chip/superfx/disasm/disasm.cpp @@ -1,3 +1,5 @@ +#ifdef SUPERFX_CPP + void SuperFX::disassemble_opcode(char *output) { *output = 0; @@ -273,3 +275,5 @@ void SuperFX::disassemble_alt3(char *output) { #undef op0 #undef op1 #undef op2 + +#endif diff --git a/src/chip/superfx/memory/memory.cpp b/src/chip/superfx/memory/memory.cpp index 4e7f43d1..9ae6091c 100644 --- a/src/chip/superfx/memory/memory.cpp +++ b/src/chip/superfx/memory/memory.cpp @@ -1,3 +1,5 @@ +#ifdef SUPERFX_CPP + uint8 SuperFX::op_read(uint16 addr) { uint16 offset = addr - regs.cbr; if(offset < 512) { @@ -65,3 +67,5 @@ void SuperFX::memory_reset() { pixelcache[n].bitpend = 0x00; } } + +#endif diff --git a/src/chip/superfx/serialization.cpp b/src/chip/superfx/serialization.cpp new file mode 100644 index 00000000..e0355674 --- /dev/null +++ b/src/chip/superfx/serialization.cpp @@ -0,0 +1,94 @@ +#ifdef SUPERFX_CPP + +void SuperFX::serialize(serializer &s) { + //superfx.hpp + s.integer(clockmode); + s.integer(instruction_counter); + + //core/registers.hpp + s.integer(regs.pipeline); + s.integer(regs.ramaddr); + + s.integer(regs.r[ 0].data); + s.integer(regs.r[ 1].data); + s.integer(regs.r[ 2].data); + s.integer(regs.r[ 3].data); + s.integer(regs.r[ 4].data); + s.integer(regs.r[ 5].data); + s.integer(regs.r[ 6].data); + s.integer(regs.r[ 7].data); + s.integer(regs.r[ 8].data); + s.integer(regs.r[ 9].data); + s.integer(regs.r[10].data); + s.integer(regs.r[11].data); + s.integer(regs.r[12].data); + s.integer(regs.r[13].data); + s.integer(regs.r[14].data); + s.integer(regs.r[15].data); + + s.integer(regs.sfr.irq); + s.integer(regs.sfr.b); + s.integer(regs.sfr.ih); + s.integer(regs.sfr.il); + s.integer(regs.sfr.alt2); + s.integer(regs.sfr.alt1); + s.integer(regs.sfr.r); + s.integer(regs.sfr.g); + s.integer(regs.sfr.ov); + s.integer(regs.sfr.s); + s.integer(regs.sfr.cy); + s.integer(regs.sfr.z); + + s.integer(regs.pbr); + s.integer(regs.rombr); + s.integer(regs.rambr); + s.integer(regs.cbr); + s.integer(regs.scbr); + + s.integer(regs.scmr.ht); + s.integer(regs.scmr.ron); + s.integer(regs.scmr.ran); + s.integer(regs.scmr.md); + + s.integer(regs.colr); + + s.integer(regs.por.obj); + s.integer(regs.por.freezehigh); + s.integer(regs.por.highnibble); + s.integer(regs.por.dither); + s.integer(regs.por.transparent); + + s.integer(regs.bramr); + s.integer(regs.vcr); + + s.integer(regs.cfgr.irq); + s.integer(regs.cfgr.ms0); + + s.integer(regs.clsr); + + s.integer(regs.romcl); + s.integer(regs.romdr); + + s.integer(regs.ramcl); + s.integer(regs.ramar); + s.integer(regs.ramdr); + + s.integer(regs.sreg); + s.integer(regs.dreg); + + s.array(cache.buffer); + s.array(cache.valid); + + for(unsigned i = 0; i < 2; i++) { + s.integer(pixelcache[i].offset); + s.integer(pixelcache[i].bitpend); + s.array(pixelcache[i].data); + } + + //timing/timing.hpp + s.integer(cache_access_speed); + s.integer(memory_access_speed); + s.integer(r15_modified); +} + +#endif diff --git a/src/chip/superfx/superfx.cpp b/src/chip/superfx/superfx.cpp index 717eb02d..8040f999 100644 --- a/src/chip/superfx/superfx.cpp +++ b/src/chip/superfx/superfx.cpp @@ -3,6 +3,7 @@ #define SUPERFX_CPP namespace SNES { +#include "serialization.cpp" #include "bus/bus.cpp" #include "core/core.cpp" #include "memory/memory.cpp" @@ -14,9 +15,14 @@ SuperFX superfx; void SuperFX::enter() { while(true) { - while(regs.sfr.g == 0) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + if(regs.sfr.g == 0) { add_clocks(6); scheduler.sync_copcpu(); + continue; } (this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])(); @@ -70,4 +76,4 @@ void SuperFX::reset() { timing_reset(); } -}; +} diff --git a/src/chip/superfx/superfx.hpp b/src/chip/superfx/superfx.hpp index 627549bf..e1561ea9 100644 --- a/src/chip/superfx/superfx.hpp +++ b/src/chip/superfx/superfx.hpp @@ -15,6 +15,8 @@ public: void power(); void reset(); + void serialize(serializer&); + private: unsigned clockmode; unsigned instruction_counter; diff --git a/src/chip/superfx/timing/timing.cpp b/src/chip/superfx/timing/timing.cpp index 7fa3f364..b1771667 100644 --- a/src/chip/superfx/timing/timing.cpp +++ b/src/chip/superfx/timing/timing.cpp @@ -1,3 +1,5 @@ +#ifdef SUPERFX_CPP + void SuperFX::add_clocks(unsigned clocks) { if(regs.romcl) { regs.romcl -= min(clocks, regs.romcl); @@ -91,3 +93,5 @@ void SuperFX::timing_reset() { regs.ramar = 0; regs.ramdr = 0; } + +#endif diff --git a/src/chip/supergameboy/supergameboy.cpp b/src/chip/supergameboy/supergameboy.cpp index 424e24b3..c53d8557 100644 --- a/src/chip/supergameboy/supergameboy.cpp +++ b/src/chip/supergameboy/supergameboy.cpp @@ -9,12 +9,20 @@ void SuperGameBoy::enter() { scheduler.clock.cop_freq = (version == SuperGameBoy1 ? 2147727 : 2097152); if(!sgb_run) while(true) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + audio.coprocessor_sample(0, 0); scheduler.addclocks_cop(1); scheduler.sync_copcpu(); } while(true) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + unsigned samples = sgb_run(samplebuffer, 16); for(unsigned i = 0; i < samples; i++) { int16 left = samplebuffer[i] >> 0; @@ -23,6 +31,7 @@ void SuperGameBoy::enter() { //SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects audio.coprocessor_sample(left / 3, right / 3); } + scheduler.addclocks_cop(samples); scheduler.sync_copcpu(); } @@ -98,8 +107,7 @@ void SuperGameBoy::enable() { } void SuperGameBoy::power() { - //determine whether to use SGB1 or SGB2 mode based on the cartridge title (look for the '2') - version = memory::cartrom[0x7fcd] != 0x32 ? SuperGameBoy1 : SuperGameBoy2; + version = (cartridge.type() == Cartridge::TypeSuperGameBoy1Bios ? SuperGameBoy1 : SuperGameBoy2); audio.coprocessor_enable(true); audio.coprocessor_frequency(version == SuperGameBoy1 ? 2147727.0 : 2097152.0); @@ -111,7 +119,6 @@ void SuperGameBoy::power() { sgb_ram(memory::gbram.data(), memory::gbram.size() == -1U ? 0 : memory::gbram.size()); sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size()); - //determine whether to use SGB1 or SGB2 mode based on the cartridge title (look for the '2') if(sgb_init) sgb_init(version); if(sgb_power) sgb_power(); } diff --git a/src/cpu/core/core.cpp b/src/cpu/core/core.cpp index bdeb9985..d38cbae2 100644 --- a/src/cpu/core/core.cpp +++ b/src/cpu/core/core.cpp @@ -5,7 +5,7 @@ namespace SNES { #include "serialization.cpp" #include "algorithms.cpp" -#include "disasm/disasm.cpp" +#include "disassembler/disassembler.cpp" #define L last_cycle(); #define A 0 diff --git a/src/cpu/core/core.hpp b/src/cpu/core/core.hpp index dc0b58ce..71304ebf 100644 --- a/src/cpu/core/core.hpp +++ b/src/cpu/core/core.hpp @@ -2,7 +2,7 @@ class CPUcore { public: #include "registers.hpp" #include "memory.hpp" - #include "disasm/disasm.hpp" + #include "disassembler/disassembler.hpp" regs_t regs; reg24_t aa, rd; diff --git a/src/cpu/core/disasm/disasm.hpp b/src/cpu/core/disasm/disasm.hpp deleted file mode 100644 index a67abbb8..00000000 --- a/src/cpu/core/disasm/disasm.hpp +++ /dev/null @@ -1,30 +0,0 @@ - enum { - OPTYPE_DP = 0, //dp - OPTYPE_DPX, //dp,x - OPTYPE_DPY, //dp,y - OPTYPE_IDP, //(dp) - OPTYPE_IDPX, //(dp,x) - OPTYPE_IDPY, //(dp),y - OPTYPE_ILDP, //[dp] - OPTYPE_ILDPY, //[dp],y - OPTYPE_ADDR, //addr - OPTYPE_ADDRX, //addr,x - OPTYPE_ADDRY, //addr,y - OPTYPE_IADDRX, //(addr,x) - OPTYPE_ILADDR, //[addr] - OPTYPE_LONG, //long - OPTYPE_LONGX, //long, x - OPTYPE_SR, //sr,s - OPTYPE_ISRY, //(sr,s),y - OPTYPE_ADDR_PC, //pbr:addr - OPTYPE_IADDR_PC, //pbr:(addr) - OPTYPE_RELB, //relb - OPTYPE_RELW, //relw - }; - - void disassemble_opcode(char *output, uint32 addr); - uint8 dreadb(uint32 addr); - uint16 dreadw(uint32 addr); - uint32 dreadl(uint32 addr); - uint32 decode(uint8 offset_type, uint32 addr); - uint8 opcode_length(); diff --git a/src/cpu/core/disasm/disasm.cpp b/src/cpu/core/disassembler/disassembler.cpp similarity index 99% rename from src/cpu/core/disasm/disasm.cpp rename to src/cpu/core/disassembler/disassembler.cpp index b5c67077..030b3ab5 100644 --- a/src/cpu/core/disasm/disasm.cpp +++ b/src/cpu/core/disassembler/disassembler.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + uint8 CPUcore::dreadb(uint32 addr) { if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) { //$[00-3f|80-bf]:[2000-5fff] @@ -477,3 +479,5 @@ uint8 CPUcore::opcode_length() { if(len == 6) return (regs.e || regs.p.x) ? 2 : 3; return len; } + +#endif diff --git a/src/cpu/core/disassembler/disassembler.hpp b/src/cpu/core/disassembler/disassembler.hpp new file mode 100644 index 00000000..b0ee6f04 --- /dev/null +++ b/src/cpu/core/disassembler/disassembler.hpp @@ -0,0 +1,30 @@ +enum { + OPTYPE_DP = 0, //dp + OPTYPE_DPX, //dp,x + OPTYPE_DPY, //dp,y + OPTYPE_IDP, //(dp) + OPTYPE_IDPX, //(dp,x) + OPTYPE_IDPY, //(dp),y + OPTYPE_ILDP, //[dp] + OPTYPE_ILDPY, //[dp],y + OPTYPE_ADDR, //addr + OPTYPE_ADDRX, //addr,x + OPTYPE_ADDRY, //addr,y + OPTYPE_IADDRX, //(addr,x) + OPTYPE_ILADDR, //[addr] + OPTYPE_LONG, //long + OPTYPE_LONGX, //long, x + OPTYPE_SR, //sr,s + OPTYPE_ISRY, //(sr,s),y + OPTYPE_ADDR_PC, //pbr:addr + OPTYPE_IADDR_PC, //pbr:(addr) + OPTYPE_RELB, //relb + OPTYPE_RELW, //relw +}; + +void disassemble_opcode(char *output, uint32 addr); +uint8 dreadb(uint32 addr); +uint16 dreadw(uint32 addr); +uint32 dreadl(uint32 addr); +uint32 decode(uint8 offset_type, uint32 addr); +uint8 opcode_length(); diff --git a/src/cpu/core/opcode_misc.cpp b/src/cpu/core/opcode_misc.cpp index 244f519a..354082a5 100644 --- a/src/cpu/core/opcode_misc.cpp +++ b/src/cpu/core/opcode_misc.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + void CPUcore::op_nop() { L op_io_irq(); } @@ -346,3 +348,5 @@ void CPUcore::op_per_n() { op_writestackn(rd.h); L op_writestackn(rd.l); } + +#endif diff --git a/src/cpu/core/opcode_pc.cpp b/src/cpu/core/opcode_pc.cpp index b209d8bb..3b4543f3 100644 --- a/src/cpu/core/opcode_pc.cpp +++ b/src/cpu/core/opcode_pc.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + template void CPUcore::op_branch() { if((bool)(regs.p & bit) != val) { L rd.l = op_readpc(); @@ -175,3 +177,5 @@ L rd.b = op_readstackn(); regs.pc.b = rd.b; regs.pc.w = ++rd.w; } + +#endif diff --git a/src/cpu/core/opcode_read.cpp b/src/cpu/core/opcode_read.cpp index 8ded68b5..d539dbc5 100644 --- a/src/cpu/core/opcode_read.cpp +++ b/src/cpu/core/opcode_read.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + template void CPUcore::op_read_const_b() { L rd.l = op_readpc(); call(op); @@ -273,3 +275,5 @@ template void CPUcore::op_read_isry_w() { L rd.h = op_readdbr(aa.w + regs.y.w + 1); call(op); } + +#endif diff --git a/src/cpu/core/opcode_rmw.cpp b/src/cpu/core/opcode_rmw.cpp index 51036b38..fed939e1 100644 --- a/src/cpu/core/opcode_rmw.cpp +++ b/src/cpu/core/opcode_rmw.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + template void CPUcore::op_adjust_imm_b() { L op_io_irq(); regs.r[n].l += adjust; @@ -163,3 +165,5 @@ template void CPUcore::op_adjust_dpx_w() { op_writedp(dp + regs.x.w + 1, rd.h); L op_writedp(dp + regs.x.w + 0, rd.l); } + +#endif diff --git a/src/cpu/core/opcode_write.cpp b/src/cpu/core/opcode_write.cpp index 80b2a108..de85e672 100644 --- a/src/cpu/core/opcode_write.cpp +++ b/src/cpu/core/opcode_write.cpp @@ -1,3 +1,5 @@ +#ifdef CPUCORE_CPP + template void CPUcore::op_write_addr_b() { aa.l = op_readpc(); aa.h = op_readpc(); @@ -193,3 +195,5 @@ void CPUcore::op_sta_isry_w() { op_writedbr(aa.w + regs.y.w + 0, regs.a.l); L op_writedbr(aa.w + regs.y.w + 1, regs.a.h); } + +#endif diff --git a/src/cpu/cpu-debugger.cpp b/src/cpu/cpu-debugger.cpp new file mode 100644 index 00000000..6dde93cc --- /dev/null +++ b/src/cpu/cpu-debugger.cpp @@ -0,0 +1,59 @@ +#ifdef CPU_CPP + +bool CPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-CPU MDR"; value = string::printf("0x%.2x", mdr()); return true; } + + //$2181-2183 + if(id == n++) { name = "$2181-$2183"; value = ""; return true; } + if(id == n++) { name = "WRAM Address"; value = string::printf("0x%.6x", wram_address()); return true; } + + //$4016 + if(id == n++) { name = "$4016"; value = ""; return true; } + if(id == n++) { name = "Joypad Strobe Latch"; value = joypad_strobe_latch(); return true; } + + //$4200 + if(id == n++) { name = "$4200"; value = ""; return true; } + if(id == n++) { name = "NMI Enable"; value = nmi_enable(); return true; } + if(id == n++) { name = "H-IRQ Enable"; value = hirq_enable(); return true; } + if(id == n++) { name = "V-IRQ Enable"; value = virq_enable(); return true; } + if(id == n++) { name = "Auto Joypad Poll"; value = auto_joypad_poll(); return true; } + + //$4201 + if(id == n++) { name = "$4201"; value = ""; return true; } + if(id == n++) { name = "PIO"; value = string::printf("0x%.2x", pio_bits()); return true; } + + //$4202 + if(id == n++) { name = "$4202"; value = ""; return true; } + if(id == n++) { name = "Multiplicand"; value = string::printf("0x%.2x", multiplicand()); return true; } + + //$4203 + if(id == n++) { name = "$4203"; value = ""; return true; } + if(id == n++) { name = "Multiplier"; value = string::printf("0x%.2x", multiplier()); return true; } + + //$4204-$4205 + if(id == n++) { name = "$4204-$4205"; value = ""; return true; } + if(id == n++) { name = "Dividend"; value = string::printf("0x%.4x", dividend()); return true; } + + //$4206 + if(id == n++) { name = "$4206"; value = ""; return true; } + if(id == n++) { name = "Divisor"; value = string::printf("0x%.2x", divisor()); return true; } + + //$4207-$4208 + if(id == n++) { name = "$4207-$4208"; value = ""; return true; } + if(id == n++) { name = "H-Time"; value = string::printf("0x%.4x", htime()); return true; } + + //$4209-$420a + if(id == n++) { name = "$4209-$420a"; value = ""; return true; } + if(id == n++) { name = "V-Time"; value = string::printf("0x%.4x", vtime()); return true; } + + //$420d + if(id == n++) { name = "$420d"; value = ""; return true; } + if(id == n++) { name = "FastROM Enable"; value = fastrom_enable(); return true; } + + return false; +} + +#endif diff --git a/src/cpu/cpu-debugger.hpp b/src/cpu/cpu-debugger.hpp new file mode 100644 index 00000000..9ed04d09 --- /dev/null +++ b/src/cpu/cpu-debugger.hpp @@ -0,0 +1,42 @@ +struct CPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned mdr() { return 0; } + + //$2181-2183 + virtual unsigned wram_address() { return 0; } + + //$4016 + virtual bool joypad_strobe_latch() { return 0; } + + //$4200 + virtual bool nmi_enable() { return 0; } + virtual bool hirq_enable() { return 0; } + virtual bool virq_enable() { return 0; } + virtual bool auto_joypad_poll() { return 0; } + + //$4201 + virtual unsigned pio_bits() { return 0; } + + //$4202 + virtual unsigned multiplicand() { return 0; } + + //$4203 + virtual unsigned multiplier() { return 0; } + + //$4204-$4205 + virtual unsigned dividend() { return 0; } + + //$4206 + virtual unsigned divisor() { return 0; } + + //$4207-$4208 + virtual unsigned htime() { return 0; } + + //$4209-$420a + virtual unsigned vtime() { return 0; } + + //$420d + virtual bool fastrom_enable() { return 0; } +}; diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 0c09b772..df02d313 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -3,6 +3,10 @@ #define CPU_CPP namespace SNES { +#if defined(DEBUGGER) + #include "cpu-debugger.cpp" +#endif + void CPU::power() { cpu_version = config.cpu.version; } diff --git a/src/cpu/cpu.hpp b/src/cpu/cpu.hpp index 9045f0e6..654c8c6d 100644 --- a/src/cpu/cpu.hpp +++ b/src/cpu/cpu.hpp @@ -1,3 +1,7 @@ +#if defined(DEBUGGER) + #include "cpu-debugger.hpp" +#endif + class CPU : public PPUcounter, public MMIO { public: virtual void enter() = 0; diff --git a/src/cpu/scpu/debugger/debugger.cpp b/src/cpu/scpu/debugger/debugger.cpp index 8eb0d230..5edb6168 100644 --- a/src/cpu/scpu/debugger/debugger.cpp +++ b/src/cpu/scpu/debugger/debugger.cpp @@ -1,45 +1,88 @@ #ifdef SCPU_CPP -void sCPUDebug::op_step() { +void sCPUDebugger::op_step() { bool break_event = false; - if(debugger.step_cpu) { - debugger.break_event = Debugger::CPUStep; - scheduler.exit(); - } else { - debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Exec, regs.pc, 0x00); - } - usage[regs.pc] &= ~(UsageFlagM | UsageFlagX); usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); opcode_pc = regs.pc; + if(debugger.step_cpu) { + debugger.break_event = Debugger::CPUStep; + scheduler.exit(Scheduler::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Exec, regs.pc, 0x00); + } + if(step_event) step_event(); sCPU::op_step(); scheduler.sync_cpusmp(); } -uint8 sCPUDebug::op_read(uint32 addr) { +uint8 sCPUDebugger::op_read(uint32 addr) { uint8 data = sCPU::op_read(addr); usage[addr] |= UsageRead; debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Read, addr, data); return data; } -void sCPUDebug::op_write(uint32 addr, uint8 data) { +void sCPUDebugger::op_write(uint32 addr, uint8 data) { sCPU::op_write(addr, data); usage[addr] |= UsageWrite; usage[addr] &= ~UsageExec; debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Write, addr, data); } -sCPUDebug::sCPUDebug() { +sCPUDebugger::sCPUDebugger() { usage = new uint8[1 << 24](); opcode_pc = 0x8000; } -sCPUDebug::~sCPUDebug() { +sCPUDebugger::~sCPUDebugger() { delete[] usage; } +//=========== +//CPUDebugger +//=========== + +//internal +unsigned sCPUDebugger::mdr() { return regs.mdr; } + +//$2181-$2183 +unsigned sCPUDebugger::wram_address() { return status.wram_addr; } + +//$4016 +bool sCPUDebugger::joypad_strobe_latch() { return status.joypad_strobe_latch; } + +//$4200 +bool sCPUDebugger::nmi_enable() { return status.nmi_enabled; } +bool sCPUDebugger::hirq_enable() { return status.hirq_enabled; } +bool sCPUDebugger::virq_enable() { return status.virq_enabled; } +bool sCPUDebugger::auto_joypad_poll() { return status.auto_joypad_poll; } + +//$4201 +unsigned sCPUDebugger::pio_bits() { return status.pio; } + +//$4202 +unsigned sCPUDebugger::multiplicand() { return status.mul_a; } + +//$4203 +unsigned sCPUDebugger::multiplier() { return status.mul_b; } + +//$4204-$4205 +unsigned sCPUDebugger::dividend() { return status.div_a; } + +//$4206 +unsigned sCPUDebugger::divisor() { return status.div_b; } + +//$4207-$4208 +unsigned sCPUDebugger::htime() { return status.hirq_pos; } + +//$4209-$420a +unsigned sCPUDebugger::vtime() { return status.virq_pos; } + +//$420d +bool sCPUDebugger::fastrom_enable() { return status.rom_speed; } + #endif diff --git a/src/cpu/scpu/debugger/debugger.hpp b/src/cpu/scpu/debugger/debugger.hpp index 8d6af0b0..c3e1d1e3 100644 --- a/src/cpu/scpu/debugger/debugger.hpp +++ b/src/cpu/scpu/debugger/debugger.hpp @@ -1,4 +1,4 @@ -class sCPUDebug : public sCPU { +class sCPUDebugger : public sCPU, public CPUDebugger { public: function step_event; @@ -16,6 +16,49 @@ public: uint8 op_read(uint32 addr); void op_write(uint32 addr, uint8 data); - sCPUDebug(); - ~sCPUDebug(); + sCPUDebugger(); + ~sCPUDebugger(); + + //=========== + //CPUDebugger + //=========== + + //internal + unsigned mdr(); + + //$2181-$2183 + unsigned wram_address(); + + //$4016 + bool joypad_strobe_latch(); + + //$4200 + bool nmi_enable(); + bool hirq_enable(); + bool virq_enable(); + bool auto_joypad_poll(); + + //$4201 + unsigned pio_bits(); + + //$4202 + unsigned multiplicand(); + + //$4203 + unsigned multiplier(); + + //$4204-$4205 + unsigned dividend(); + + //$4206 + unsigned divisor(); + + //$4207-$4208 + unsigned htime(); + + //$4209-$420a + unsigned vtime(); + + //$420d + bool fastrom_enable(); }; diff --git a/src/cpu/scpu/scpu.cpp b/src/cpu/scpu/scpu.cpp index bbae3b56..33953b4e 100644 --- a/src/cpu/scpu/scpu.cpp +++ b/src/cpu/scpu/scpu.cpp @@ -5,7 +5,7 @@ namespace SNES { #if defined(DEBUGGER) #include "debugger/debugger.cpp" - sCPUDebug cpu; + sCPUDebugger cpu; #else sCPU cpu; #endif @@ -20,7 +20,7 @@ void sCPU::enter() { while(true) { if(scheduler.sync == Scheduler::SyncCpu) { scheduler.sync = Scheduler::SyncAll; - scheduler.exit(); + scheduler.exit(Scheduler::SynchronizeEvent); } if(status.interrupt_pending) { diff --git a/src/cpu/scpu/scpu.hpp b/src/cpu/scpu/scpu.hpp index 351fe849..687862e5 100644 --- a/src/cpu/scpu/scpu.hpp +++ b/src/cpu/scpu/scpu.hpp @@ -100,7 +100,7 @@ public: #if defined(DEBUGGER) #include "debugger/debugger.hpp" - extern sCPUDebug cpu; + extern sCPUDebugger cpu; #else extern sCPU cpu; #endif diff --git a/src/data/icons-16x16/accessories-text-editor.png b/src/data/icons-16x16/accessories-text-editor.png new file mode 100644 index 00000000..188e1c12 Binary files /dev/null and b/src/data/icons-16x16/accessories-text-editor.png differ diff --git a/src/data/icons-16x16/audio-volume-high.png b/src/data/icons-16x16/audio-volume-high.png new file mode 100644 index 00000000..ec8f00b4 Binary files /dev/null and b/src/data/icons-16x16/audio-volume-high.png differ diff --git a/src/data/icons-16x16/folder.png b/src/data/icons-16x16/folder.png new file mode 100644 index 00000000..65bd0bbd Binary files /dev/null and b/src/data/icons-16x16/folder.png differ diff --git a/src/data/icons-16x16/preferences-system.png b/src/data/icons-16x16/preferences-system.png new file mode 100644 index 00000000..9460dfc7 Binary files /dev/null and b/src/data/icons-16x16/preferences-system.png differ diff --git a/src/data/icons-16x16/system-file-manager.png b/src/data/icons-16x16/system-file-manager.png new file mode 100644 index 00000000..60cade46 Binary files /dev/null and b/src/data/icons-16x16/system-file-manager.png differ diff --git a/src/data/icons-16x16/system-search.png b/src/data/icons-16x16/system-search.png new file mode 100644 index 00000000..fd7f0b07 Binary files /dev/null and b/src/data/icons-16x16/system-search.png differ diff --git a/src/data/icons-22x22/accessories-text-editor.png b/src/data/icons-22x22/accessories-text-editor.png deleted file mode 100644 index c3d245de..00000000 Binary files a/src/data/icons-22x22/accessories-text-editor.png and /dev/null differ diff --git a/src/data/icons-22x22/audio-volume-high.png b/src/data/icons-22x22/audio-volume-high.png deleted file mode 100644 index 1b2f30e6..00000000 Binary files a/src/data/icons-22x22/audio-volume-high.png and /dev/null differ diff --git a/src/data/icons-22x22/folder.png b/src/data/icons-22x22/folder.png deleted file mode 100644 index 01f45b82..00000000 Binary files a/src/data/icons-22x22/folder.png and /dev/null differ diff --git a/src/data/icons-22x22/image-x-generic.png b/src/data/icons-22x22/image-x-generic.png deleted file mode 100644 index 10f46719..00000000 Binary files a/src/data/icons-22x22/image-x-generic.png and /dev/null differ diff --git a/src/data/icons-22x22/input-gaming.png b/src/data/icons-22x22/input-gaming.png deleted file mode 100644 index 18f77392..00000000 Binary files a/src/data/icons-22x22/input-gaming.png and /dev/null differ diff --git a/src/data/icons-22x22/preferences-system.png b/src/data/icons-22x22/preferences-system.png deleted file mode 100644 index cc91d659..00000000 Binary files a/src/data/icons-22x22/preferences-system.png and /dev/null differ diff --git a/src/data/icons-22x22/system-file-manager.png b/src/data/icons-22x22/system-file-manager.png deleted file mode 100644 index 44d63106..00000000 Binary files a/src/data/icons-22x22/system-file-manager.png and /dev/null differ diff --git a/src/data/icons-22x22/system-search.png b/src/data/icons-22x22/system-search.png deleted file mode 100644 index 4e522b23..00000000 Binary files a/src/data/icons-22x22/system-search.png and /dev/null differ diff --git a/src/data/icons-22x22/video-display.png b/src/data/icons-22x22/video-display.png deleted file mode 100644 index f244adb1..00000000 Binary files a/src/data/icons-22x22/video-display.png and /dev/null differ diff --git a/src/data/joypad.png b/src/data/joypad.png deleted file mode 100644 index 43d7c7d9..00000000 Binary files a/src/data/joypad.png and /dev/null differ diff --git a/src/dsp/adsp/adsp.cpp b/src/dsp/adsp/adsp.cpp index b11e1605..dd893590 100644 --- a/src/dsp/adsp/adsp.cpp +++ b/src/dsp/adsp/adsp.cpp @@ -1,7 +1,7 @@ #include <../base.hpp> #define ADSP_CPP -namespaec SNES { +namespace SNES { aDSP dsp; diff --git a/src/dsp/sdsp/debugger/debugger.hpp b/src/dsp/sdsp/debugger/debugger.hpp index c51b50d8..5f152b4e 100644 --- a/src/dsp/sdsp/debugger/debugger.hpp +++ b/src/dsp/sdsp/debugger/debugger.hpp @@ -1,3 +1,3 @@ -class sDSPDebug : public sDSP { +class sDSPDebugger : public sDSP { public: }; diff --git a/src/dsp/sdsp/sdsp.cpp b/src/dsp/sdsp/sdsp.cpp index 9b9a1f6c..b78a6bbe 100644 --- a/src/dsp/sdsp/sdsp.cpp +++ b/src/dsp/sdsp/sdsp.cpp @@ -9,7 +9,7 @@ namespace SNES { #if defined(DEBUGGER) #include "debugger/debugger.cpp" - sDSPDebug dsp; + sDSPDebugger dsp; #else sDSP dsp; #endif @@ -20,7 +20,10 @@ namespace SNES { #define VREG(n) state.regs[v.vidx + v_##n] #if !defined(DSP_STATE_MACHINE) - #define phase_start() while(true) { if(scheduler.sync == Scheduler::SyncAll) scheduler.exit(); + #define phase_start() while(true) { \ + if(scheduler.sync == Scheduler::SyncAll) { \ + scheduler.exit(Scheduler::SynchronizeEvent); \ + } #define phase(n) #define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp() #define phase_end() } diff --git a/src/dsp/sdsp/sdsp.hpp b/src/dsp/sdsp/sdsp.hpp index 5efb0a8d..f9b17f0c 100644 --- a/src/dsp/sdsp/sdsp.hpp +++ b/src/dsp/sdsp/sdsp.hpp @@ -170,7 +170,7 @@ private: #if defined(DEBUGGER) #include "debugger/debugger.hpp" - extern sDSPDebug dsp; + extern sDSPDebugger dsp; #else extern sDSP dsp; #endif diff --git a/src/interface.hpp b/src/interface.hpp index c1f55fd6..136be569 100644 --- a/src/interface.hpp +++ b/src/interface.hpp @@ -5,6 +5,10 @@ #endif namespace SNES { + struct ChipDebugger { + virtual bool property(unsigned id, string &name, string &value) = 0; + }; + #include "memory/memory.hpp" #include "memory/smemory/smemory.hpp" diff --git a/src/lib/nall/property.hpp b/src/lib/nall/property.hpp index 0099939c..6fd33acd 100644 --- a/src/lib/nall/property.hpp +++ b/src/lib/nall/property.hpp @@ -1,45 +1,91 @@ #ifndef NALL_PROPERTY_HPP #define NALL_PROPERTY_HPP -//nall::property implements a variable container that disallows write access -//to non-derived objects. This requires use of property::set(), as C++ lacks -//the ability to make this implementation completely transparent. +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; namespace nall { - class property { - public: - template class property_t; + template struct property { + template struct traits { typedef T type; }; - protected: - template T& get(property_t&); - template property_t& set(property_t&, const T); - - public: - template - class property_t { - public: + template struct readonly { + const T* operator->() const { return &value; } const T& operator()() const { return value; } - property_t() : value() {} - property_t(const T value_) : value(value_) {} - - protected: - T value; + operator const T&() const { return value; } + private: + T* operator->() { return &value; } operator T&() { return value; } - property_t& operator=(const T newValue) { value = newValue; return *this; } - friend T& property::get(property_t&); - friend property_t& property::set(property_t&, const T); + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; }; }; - - template - T& property::get(property::property_t &p) { - return p.operator T&(); - } - - template - property::property_t& property::set(property::property_t &p, const T value) { - return p.operator=(value); - } } #endif diff --git a/src/lib/nall/string/base.hpp b/src/lib/nall/string/base.hpp index 24a5b1d4..e5851b00 100644 --- a/src/lib/nall/string/base.hpp +++ b/src/lib/nall/string/base.hpp @@ -1,6 +1,7 @@ #ifndef NALL_STRING_BASE_HPP #define NALL_STRING_BASE_HPP +#include #include #include #include @@ -48,6 +49,8 @@ namespace nall { class string { public: + static string printf(const char*, ...); + inline void reserve(size_t); inline unsigned length() const; diff --git a/src/lib/nall/string/core.hpp b/src/lib/nall/string/core.hpp index 5c16ff5c..29ca7173 100644 --- a/src/lib/nall/string/core.hpp +++ b/src/lib/nall/string/core.hpp @@ -3,6 +3,15 @@ namespace nall { +inline string string::printf(const char *fmt, ...) { + static char text[4096]; + va_list args; + va_start(args, fmt); + vsprintf(text, fmt, args); + va_end(args); + return text; +} + void string::reserve(size_t size_) { if(size_ > size) { size = size_; diff --git a/src/lib/nall/string/filename.hpp b/src/lib/nall/string/filename.hpp index 29dc37d4..e26493c9 100644 --- a/src/lib/nall/string/filename.hpp +++ b/src/lib/nall/string/filename.hpp @@ -43,6 +43,18 @@ namespace nall { } return result; } + + // "foo/bar.c" -> "c" + inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; + } } #endif diff --git a/src/lib/ruby/audio/pulseaudio.cpp b/src/lib/ruby/audio/pulseaudio.cpp index 04dbb839..bdd5f682 100644 --- a/src/lib/ruby/audio/pulseaudio.cpp +++ b/src/lib/ruby/audio/pulseaudio.cpp @@ -1,110 +1,170 @@ -/* - audio.pulseaudio (2008-10-31) - author: byuu -*/ +//audio.pulseaudio (2010-01-05) +//author: RedDwarf -#include -#include +#include namespace ruby { class pAudioPulseAudio { public: struct { - pa_simple *handle; + pa_mainloop *mainloop; + pa_context *context; + pa_stream *stream; pa_sample_spec spec; + pa_buffer_attr buffer_attr; + bool first; } device; struct { uint32_t *data; + size_t size; unsigned offset; } buffer; struct { + bool synchronize; unsigned frequency; + unsigned latency; } settings; bool cap(const string& name) { + if(name == Audio::Synchronize) return true; if(name == Audio::Frequency) return true; - return false; + if(name == Audio::Latency) return true; } any get(const string& name) { + if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Frequency) return settings.frequency; - return false; + if(name == Audio::Latency) return settings.latency; } bool set(const string& name, const any& value) { - if(name == Audio::Frequency) { - settings.frequency = any_cast(value); - if(device.handle) init(); + if(name == Audio::Synchronize) { + settings.synchronize = any_cast(value); return true; } - return false; + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(device.stream) { + pa_operation_unref(pa_stream_update_sample_rate(device.stream, settings.frequency, NULL, NULL)); + } + return true; + } + + if(name == Audio::Latency) { + settings.latency = any_cast(value); + if(device.stream) { + device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec); + pa_stream_set_buffer_attr(device.stream, &device.buffer_attr, NULL, NULL); + } + return true; + } } void sample(uint16_t left, uint16_t right) { - if(!device.handle) return; - + pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size); buffer.data[buffer.offset++] = left + (right << 16); - if(buffer.offset >= 64) { - int error; - pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error); - buffer.offset = 0; + if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return; + + while(true) { + if(device.first) { + device.first = false; + pa_mainloop_iterate(device.mainloop, 0, NULL); + } else { + pa_mainloop_iterate(device.mainloop, 1, NULL); + } + unsigned length = pa_stream_writable_size(device.stream); + if(length >= buffer.offset * pa_frame_size(&device.spec)) break; + if(settings.synchronize == false) { + buffer.offset = 0; + return; + } } + + pa_stream_write(device.stream, (const void*)buffer.data, buffer.offset * pa_frame_size(&device.spec), NULL, 0LL, PA_SEEK_RELATIVE); + buffer.data = 0; + buffer.offset = 0; } void clear() { } bool init() { - term(); + device.mainloop = pa_mainloop_new(); - device.spec.format = PA_SAMPLE_S16LE; + device.context = pa_context_new(pa_mainloop_get_api(device.mainloop), "ruby::pulseaudio"); + pa_context_connect(device.context, NULL, PA_CONTEXT_NOFLAGS, NULL); + + pa_context_state_t cstate; + do { + pa_mainloop_iterate(device.mainloop, 1, NULL); + cstate = pa_context_get_state(device.context); + if(!PA_CONTEXT_IS_GOOD(cstate)) return false; + } while(cstate != PA_CONTEXT_READY); + + device.spec.format = PA_SAMPLE_S16LE; device.spec.channels = 2; - device.spec.rate = settings.frequency; + device.spec.rate = settings.frequency; + device.stream = pa_stream_new(device.context, "audio", &device.spec, NULL); - int error = 0; - device.handle = pa_simple_new( - 0, //default server - "ruby::pulseaudio", //application name - PA_STREAM_PLAYBACK, //direction - 0, //default device - "audio", //stream description - &device.spec, //sample format - 0, //default channel map - 0, //default buffering attributes - &error //error code - ); - if(!device.handle) { - fprintf(stderr, "ruby::pulseaudio failed to initialize - %s\n", pa_strerror(error)); - return false; - } + device.buffer_attr.maxlength = -1; + device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec); + device.buffer_attr.prebuf = -1; + device.buffer_attr.minreq = -1; + device.buffer_attr.fragsize = -1; - buffer.data = new uint32_t[64]; + pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_VARIABLE_RATE); + pa_stream_connect_playback(device.stream, NULL, &device.buffer_attr, flags, NULL, NULL); + + pa_stream_state_t sstate; + do { + pa_mainloop_iterate(device.mainloop, 1, NULL); + sstate = pa_stream_get_state(device.stream); + if(!PA_STREAM_IS_GOOD(sstate)) return false; + } while(sstate != PA_STREAM_READY); + + buffer.size = 960; buffer.offset = 0; + device.first = true; + return true; } void term() { - if(device.handle) { - int error; - pa_simple_flush(device.handle, &error); - pa_simple_free(device.handle); - device.handle = 0; + if(buffer.data) { + pa_stream_cancel_write(device.stream); + buffer.data = 0; } - if(buffer.data) { - delete[] buffer.data; - buffer.data = 0; + if(device.stream) { + pa_stream_disconnect(device.stream); + pa_stream_unref(device.stream); + device.stream = 0; + } + + if(device.context) { + pa_context_disconnect(device.context); + pa_context_unref(device.context); + device.context = 0; + } + + if(device.mainloop) { + pa_mainloop_free(device.mainloop); + device.mainloop = 0; } } pAudioPulseAudio() { - device.handle = 0; + device.mainloop = 0; + device.context = 0; + device.stream = 0; buffer.data = 0; + settings.synchronize = false; settings.frequency = 22050; + settings.latency = 60; } ~pAudioPulseAudio() { @@ -114,4 +174,4 @@ public: DeclareAudio(PulseAudio) -}; +} diff --git a/src/lib/ruby/audio/pulseaudiosimple.cpp b/src/lib/ruby/audio/pulseaudiosimple.cpp new file mode 100644 index 00000000..cdd6e438 --- /dev/null +++ b/src/lib/ruby/audio/pulseaudiosimple.cpp @@ -0,0 +1,115 @@ +//audio.pulseaudiosimple (2010-01-05) +//author: byuu + +#include +#include + +namespace ruby { + +class pAudioPulseAudioSimple { +public: + struct { + pa_simple *handle; + pa_sample_spec spec; + } device; + + struct { + uint32_t *data; + unsigned offset; + } buffer; + + struct { + unsigned frequency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Frequency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(device.handle) init(); + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + if(!device.handle) return; + + buffer.data[buffer.offset++] = left + (right << 16); + if(buffer.offset >= 64) { + int error; + pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error); + buffer.offset = 0; + } + } + + void clear() { + } + + bool init() { + term(); + + device.spec.format = PA_SAMPLE_S16LE; + device.spec.channels = 2; + device.spec.rate = settings.frequency; + + int error = 0; + device.handle = pa_simple_new( + 0, //default server + "ruby::pulseaudiosimple", //application name + PA_STREAM_PLAYBACK, //direction + 0, //default device + "audio", //stream description + &device.spec, //sample format + 0, //default channel map + 0, //default buffering attributes + &error //error code + ); + if(!device.handle) { + fprintf(stderr, "ruby::pulseaudiosimple failed to initialize - %s\n", pa_strerror(error)); + return false; + } + + buffer.data = new uint32_t[64]; + buffer.offset = 0; + return true; + } + + void term() { + if(device.handle) { + int error; + pa_simple_flush(device.handle, &error); + pa_simple_free(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioPulseAudioSimple() { + device.handle = 0; + buffer.data = 0; + settings.frequency = 22050; + } + + ~pAudioPulseAudioSimple() { + term(); + } +}; + +DeclareAudio(PulseAudioSimple) + +}; diff --git a/src/lib/ruby/ruby.cpp b/src/lib/ruby/ruby.cpp index 2e1e1f33..52395973 100644 --- a/src/lib/ruby/ruby.cpp +++ b/src/lib/ruby/ruby.cpp @@ -190,6 +190,10 @@ void AudioInterface::driver(const char *driver) { else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio(); #endif + #ifdef AUDIO_PULSEAUDIOSIMPLE + else if(!strcmp(driver, "PulseAudioSimple")) p = new AudioPulseAudioSimple(); + #endif + else p = new Audio(); } @@ -203,6 +207,8 @@ const char* AudioInterface::default_driver() { return "OpenAL"; #elif defined(AUDIO_PULSEAUDIO) return "PulseAudio"; + #elif defined(AUDIO_PULSEAUDIOSIMPLE) + return "PulseAudioSimple"; #elif defined(AUDIO_AO) return "libao"; #elif defined(AUDIO_OSS) @@ -240,6 +246,10 @@ const char* AudioInterface::driver_list() { "PulseAudio;" #endif + #if defined(AUDIO_PULSEAUDIOSIMPLE) + "PulseAudioSimple;" + #endif + #if defined(AUDIO_AO) "libao;" #endif diff --git a/src/lib/ruby/ruby_impl.cpp b/src/lib/ruby/ruby_impl.cpp index e5fd21bf..d5be3c3e 100644 --- a/src/lib/ruby/ruby_impl.cpp +++ b/src/lib/ruby/ruby_impl.cpp @@ -128,6 +128,10 @@ #include #endif +#ifdef AUDIO_PULSEAUDIOSIMPLE + #include +#endif + /* Input */ #define DeclareInput(Name) \ diff --git a/src/lib/ruby/video/glx.cpp b/src/lib/ruby/video/glx.cpp index 900107e3..3e1c6014 100644 --- a/src/lib/ruby/video/glx.cpp +++ b/src/lib/ruby/video/glx.cpp @@ -2,7 +2,7 @@ video.glx author: byuu license: public domain - last updated: 2009-12-08 + last updated: 2010-01-05 Design notes: SGI's GLX is the X11/Xlib interface to OpenGL. @@ -188,7 +188,6 @@ public: settings.height = 256; //vertical synchronization - if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalEXT"); if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); if( glSwapInterval) glSwapInterval(settings.synchronize); diff --git a/src/lib/ruby/video/sdl.cpp b/src/lib/ruby/video/sdl.cpp index 88b9719c..8f70342e 100644 --- a/src/lib/ruby/video/sdl.cpp +++ b/src/lib/ruby/video/sdl.cpp @@ -110,7 +110,7 @@ public: display = XOpenDisplay(0); char env[512]; - sprintf(env, "SDL_WINDOWID=%ld", settings.handle); + sprintf(env, "SDL_WINDOWID=%ld", (long int)settings.handle); putenv(env); SDL_InitSubSystem(SDL_INIT_VIDEO); diff --git a/src/lib/ruby/video/xv.cpp b/src/lib/ruby/video/xv.cpp index d4822344..10d51647 100644 --- a/src/lib/ruby/video/xv.cpp +++ b/src/lib/ruby/video/xv.cpp @@ -37,6 +37,9 @@ public: XvImage *image; XvFormat format; uint32_t fourcc; + + unsigned width; + unsigned height; } device; struct { @@ -81,11 +84,33 @@ public: return false; } - bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { - settings.width = width; - settings.height = height; + void resize(unsigned width, unsigned height) { + if(device.width >= width && device.height >= height) return; + device.width = max(width, device.width); + device.height = max(height, device.height); - pitch = 1024 * 4; + XShmDetach(device.display, &device.shminfo); + shmdt(device.shminfo.shmaddr); + shmctl(device.shminfo.shmid, IPC_RMID, NULL); + XFree(device.image); + delete[] buffer; + + device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo); + + device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777); + device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0); + device.shminfo.readOnly = false; + XShmAttach(device.display, &device.shminfo); + + buffer = new uint32_t[device.width * device.height]; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + if(width != settings.width || height != settings.height) { + resize(settings.width = width, settings.height = height); + } + + pitch = device.width * 4; return data = buffer; } @@ -93,7 +118,7 @@ public: } void clear() { - memset(buffer, 0, 1024 * 1024 * sizeof(uint32_t)); + memset(buffer, 0, device.width * device.height * sizeof(uint32_t)); //clear twice in case video is double buffered ... refresh(); refresh(); @@ -269,7 +294,10 @@ public: return false; } - device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, 1024, 1024, &device.shminfo); + device.width = 256; + device.height = 256; + + device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo); if(!device.image) { fprintf(stderr, "VideoXv: XShmCreateImage failed.\n"); return false; @@ -283,7 +311,7 @@ public: return false; } - buffer = new uint32_t[1024 * 1024]; + buffer = new uint32_t[device.width * device.height]; settings.width = 256; settings.height = 256; init_yuv_tables(); @@ -319,8 +347,8 @@ public: for(unsigned y = 0; y < height; y++) { memcpy(output, input, width * 4); - input += 1024; - output += 1024; + input += device.width; + output += device.width; } } @@ -336,8 +364,8 @@ public: *output++ = p >> 16; } - input += (1024 - width); - output += (1024 - width) * 3; + input += (device.width - width); + output += (device.width - width) * 3; } } @@ -351,8 +379,8 @@ public: *output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16 } - input += 1024 - width; - output += 1024 - width; + input += device.width - width; + output += device.width - width; } } @@ -366,8 +394,8 @@ public: *output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15 } - input += 1024 - width; - output += 1024 - width; + input += device.width - width; + output += device.width - width; } } @@ -389,8 +417,8 @@ public: *output++ = (v << 8) | ytable[p1]; } - input += 1024 - width; - output += 1024 - width; + input += device.width - width; + output += device.width - width; } } @@ -412,8 +440,8 @@ public: *output++ = (ytable[p1] << 8) | v; } - input += 1024 - width; - output += 1024 - width; + input += device.width - width; + output += device.width - width; } } diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index e6ac104a..17c59963 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -5,7 +5,7 @@ namespace SNES { #if defined(DEBUGGER) #include "debugger/debugger.cpp" - bPPUDebug ppu; + bPPUDebugger ppu; #else bPPU ppu; #endif @@ -17,7 +17,9 @@ namespace SNES { void bPPU::enter() { while(true) { - if(scheduler.sync == Scheduler::SyncAll) scheduler.exit(); + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } //H = 0 (initialize) scanline(); diff --git a/src/ppu/bppu/bppu.hpp b/src/ppu/bppu/bppu.hpp index 6a0c129e..7a4860cf 100644 --- a/src/ppu/bppu/bppu.hpp +++ b/src/ppu/bppu/bppu.hpp @@ -59,7 +59,7 @@ public: #if defined(DEBUGGER) #include "debugger/debugger.hpp" - extern bPPUDebug ppu; + extern bPPUDebugger ppu; #else extern bPPU ppu; #endif diff --git a/src/ppu/bppu/debugger/debugger.cpp b/src/ppu/bppu/debugger/debugger.cpp index 083d24f5..2d015e9f 100644 --- a/src/ppu/bppu/debugger/debugger.cpp +++ b/src/ppu/bppu/debugger/debugger.cpp @@ -1,36 +1,290 @@ #ifdef BPPU_CPP -uint8 bPPUDebug::vram_mmio_read(uint16 addr) { +#include "render.cpp" + +uint8 bPPUDebugger::vram_mmio_read(uint16 addr) { uint8 data = bPPU::vram_mmio_read(addr); debugger.breakpoint_test(Debugger::Breakpoint::VRAM, Debugger::Breakpoint::Read, addr, data); return data; } -void bPPUDebug::vram_mmio_write(uint16 addr, uint8 data) { +void bPPUDebugger::vram_mmio_write(uint16 addr, uint8 data) { bPPU::vram_mmio_write(addr, data); debugger.breakpoint_test(Debugger::Breakpoint::VRAM, Debugger::Breakpoint::Write, addr, data); } -uint8 bPPUDebug::oam_mmio_read(uint16 addr) { +uint8 bPPUDebugger::oam_mmio_read(uint16 addr) { uint8 data = bPPU::oam_mmio_read(addr); debugger.breakpoint_test(Debugger::Breakpoint::OAM, Debugger::Breakpoint::Read, addr, data); return data; } -void bPPUDebug::oam_mmio_write(uint16 addr, uint8 data) { +void bPPUDebugger::oam_mmio_write(uint16 addr, uint8 data) { bPPU::oam_mmio_write(addr, data); debugger.breakpoint_test(Debugger::Breakpoint::OAM, Debugger::Breakpoint::Write, addr, data); } -uint8 bPPUDebug::cgram_mmio_read(uint16 addr) { +uint8 bPPUDebugger::cgram_mmio_read(uint16 addr) { uint8 data = bPPU::cgram_mmio_read(addr); debugger.breakpoint_test(Debugger::Breakpoint::CGRAM, Debugger::Breakpoint::Read, addr, data); return data; } -void bPPUDebug::cgram_mmio_write(uint16 addr, uint8 data) { +void bPPUDebugger::cgram_mmio_write(uint16 addr, uint8 data) { bPPU::cgram_mmio_write(addr, data); debugger.breakpoint_test(Debugger::Breakpoint::CGRAM, Debugger::Breakpoint::Write, addr, data); } +bPPUDebugger::bPPUDebugger() { + bg1_enabled[0] = bg1_enabled[1] = true; + bg2_enabled[0] = bg2_enabled[1] = true; + bg3_enabled[0] = bg3_enabled[1] = true; + bg4_enabled[0] = bg4_enabled[1] = true; + oam_enabled[0] = oam_enabled[1] = oam_enabled[2] = oam_enabled[3] = true; +} + +//=========== +//PPUDebugger +//=========== + +//internal +unsigned bPPUDebugger::ppu1_mdr() { return regs.ppu1_mdr; } +unsigned bPPUDebugger::ppu2_mdr() { return regs.ppu2_mdr; } + +//$2100 +bool bPPUDebugger::display_disable() { return regs.display_disabled; } +unsigned bPPUDebugger::display_brightness() { return regs.display_brightness; } + +//$2101 +unsigned bPPUDebugger::oam_base_size() { return regs.oam_basesize; } +unsigned bPPUDebugger::oam_name_select() { return regs.oam_nameselect; } +unsigned bPPUDebugger::oam_name_base_address() { return regs.oam_tdaddr; } + +//$2102-$2103 +unsigned bPPUDebugger::oam_base_address() { return regs.oam_baseaddr; } +bool bPPUDebugger::oam_priority() { return regs.oam_priority; } + +//$2105 +bool bPPUDebugger::bg1_tile_size() { return regs.bg_tilesize[BG1]; } +bool bPPUDebugger::bg2_tile_size() { return regs.bg_tilesize[BG2]; } +bool bPPUDebugger::bg3_tile_size() { return regs.bg_tilesize[BG3]; } +bool bPPUDebugger::bg4_tile_size() { return regs.bg_tilesize[BG4]; } +bool bPPUDebugger::bg3_priority() { return regs.bg3_priority; } +unsigned bPPUDebugger::bg_mode() { return regs.bg_mode; } + +//$2106 +unsigned bPPUDebugger::mosaic_size() { return regs.mosaic_size; } +bool bPPUDebugger::bg1_mosaic_enable() { return regs.mosaic_enabled[BG1]; } +bool bPPUDebugger::bg2_mosaic_enable() { return regs.mosaic_enabled[BG2]; } +bool bPPUDebugger::bg3_mosaic_enable() { return regs.mosaic_enabled[BG3]; } +bool bPPUDebugger::bg4_mosaic_enable() { return regs.mosaic_enabled[BG4]; } + +//$2107 +unsigned bPPUDebugger::bg1_screen_address() { return regs.bg_scaddr[BG1]; } +unsigned bPPUDebugger::bg1_screen_size() { return regs.bg_scsize[BG1]; } + +//$2108 +unsigned bPPUDebugger::bg2_screen_address() { return regs.bg_scaddr[BG2]; } +unsigned bPPUDebugger::bg2_screen_size() { return regs.bg_scsize[BG2]; } + +//$2109 +unsigned bPPUDebugger::bg3_screen_address() { return regs.bg_scaddr[BG3]; } +unsigned bPPUDebugger::bg3_screen_size() { return regs.bg_scsize[BG3]; } + +//$210a +unsigned bPPUDebugger::bg4_screen_address() { return regs.bg_scaddr[BG4]; } +unsigned bPPUDebugger::bg4_screen_size() { return regs.bg_scsize[BG4]; } + +//$210b +unsigned bPPUDebugger::bg1_name_base_address() { return regs.bg_tdaddr[BG1]; } +unsigned bPPUDebugger::bg2_name_base_address() { return regs.bg_tdaddr[BG2]; } + +//$210c +unsigned bPPUDebugger::bg3_name_base_address() { return regs.bg_tdaddr[BG3]; } +unsigned bPPUDebugger::bg4_name_base_address() { return regs.bg_tdaddr[BG4]; } + +//$210d +unsigned bPPUDebugger::mode7_hoffset() { return regs.m7_hofs & 0x1fff; } +unsigned bPPUDebugger::bg1_hoffset() { return regs.bg_hofs[BG1] & 0x03ff; } + +//$210e +unsigned bPPUDebugger::mode7_voffset() { return regs.m7_vofs & 0x1fff; } +unsigned bPPUDebugger::bg1_voffset() { return regs.bg_vofs[BG1] & 0x03ff; } + +//$210f +unsigned bPPUDebugger::bg2_hoffset() { return regs.bg_hofs[BG2] & 0x03ff; } + +//$2110 +unsigned bPPUDebugger::bg2_voffset() { return regs.bg_vofs[BG2] & 0x03ff; } + +//$2111 +unsigned bPPUDebugger::bg3_hoffset() { return regs.bg_hofs[BG3] & 0x03ff; } + +//$2112 +unsigned bPPUDebugger::bg3_voffset() { return regs.bg_vofs[BG3] & 0x03ff; } + +//$2113 +unsigned bPPUDebugger::bg4_hoffset() { return regs.bg_hofs[BG4] & 0x03ff; } + +//$2114 +unsigned bPPUDebugger::bg4_voffset() { return regs.bg_vofs[BG4] & 0x03ff; } + +//$2115 +bool bPPUDebugger::vram_increment_mode() { return regs.vram_incmode; } +unsigned bPPUDebugger::vram_increment_formation() { return regs.vram_mapping; } +unsigned bPPUDebugger::vram_increment_size() { return regs.vram_incsize; } + +//$2116-$2117 +unsigned bPPUDebugger::vram_address() { return regs.vram_addr; } + +//$211a +unsigned bPPUDebugger::mode7_repeat() { return regs.mode7_repeat; } +bool bPPUDebugger::mode7_vflip() { return regs.mode7_vflip; } +bool bPPUDebugger::mode7_hflip() { return regs.mode7_hflip; } + +//$211b +unsigned bPPUDebugger::mode7_a() { return regs.m7a; } + +//$211c +unsigned bPPUDebugger::mode7_b() { return regs.m7b; } + +//$211d +unsigned bPPUDebugger::mode7_c() { return regs.m7c; } + +//$211e +unsigned bPPUDebugger::mode7_d() { return regs.m7d; } + +//$211f +unsigned bPPUDebugger::mode7_x() { return regs.m7x; } + +//$2120 +unsigned bPPUDebugger::mode7_y() { return regs.m7y; } + +//$2121 +unsigned bPPUDebugger::cgram_address() { return regs.cgram_addr; } + +//$2123 +bool bPPUDebugger::bg1_window1_enable() { return regs.window1_enabled[BG1]; } +bool bPPUDebugger::bg1_window1_invert() { return regs.window1_invert [BG1]; } +bool bPPUDebugger::bg1_window2_enable() { return regs.window2_enabled[BG1]; } +bool bPPUDebugger::bg1_window2_invert() { return regs.window2_invert [BG1]; } +bool bPPUDebugger::bg2_window1_enable() { return regs.window1_enabled[BG2]; } +bool bPPUDebugger::bg2_window1_invert() { return regs.window1_invert [BG2]; } +bool bPPUDebugger::bg2_window2_enable() { return regs.window2_enabled[BG2]; } +bool bPPUDebugger::bg2_window2_invert() { return regs.window2_invert [BG2]; } + +//$2124 +bool bPPUDebugger::bg3_window1_enable() { return regs.window1_enabled[BG3]; } +bool bPPUDebugger::bg3_window1_invert() { return regs.window1_invert [BG3]; } +bool bPPUDebugger::bg3_window2_enable() { return regs.window2_enabled[BG3]; } +bool bPPUDebugger::bg3_window2_invert() { return regs.window2_invert [BG3]; } +bool bPPUDebugger::bg4_window1_enable() { return regs.window1_enabled[BG4]; } +bool bPPUDebugger::bg4_window1_invert() { return regs.window1_invert [BG4]; } +bool bPPUDebugger::bg4_window2_enable() { return regs.window2_enabled[BG4]; } +bool bPPUDebugger::bg4_window2_invert() { return regs.window2_invert [BG4]; } + +//$2125 +bool bPPUDebugger::oam_window1_enable() { return regs.window1_enabled[OAM]; } +bool bPPUDebugger::oam_window1_invert() { return regs.window1_invert [OAM]; } +bool bPPUDebugger::oam_window2_enable() { return regs.window2_enabled[OAM]; } +bool bPPUDebugger::oam_window2_invert() { return regs.window2_invert [OAM]; } +bool bPPUDebugger::color_window1_enable() { return regs.window1_enabled[COL]; } +bool bPPUDebugger::color_window1_invert() { return regs.window1_invert [COL]; } +bool bPPUDebugger::color_window2_enable() { return regs.window2_enabled[COL]; } +bool bPPUDebugger::color_window2_invert() { return regs.window2_enabled[COL]; } + +//$2126 +unsigned bPPUDebugger::window1_left() { return regs.window1_left; } + +//$2127 +unsigned bPPUDebugger::window1_right() { return regs.window1_right; } + +//$2128 +unsigned bPPUDebugger::window2_left() { return regs.window2_left; } + +//$2129 +unsigned bPPUDebugger::window2_right() { return regs.window2_right; } + +//$212a +unsigned bPPUDebugger::bg1_window_mask() { return regs.window_mask[BG1]; } +unsigned bPPUDebugger::bg2_window_mask() { return regs.window_mask[BG2]; } +unsigned bPPUDebugger::bg3_window_mask() { return regs.window_mask[BG3]; } +unsigned bPPUDebugger::bg4_window_mask() { return regs.window_mask[BG4]; } + +//$212b +unsigned bPPUDebugger::oam_window_mask() { return regs.window_mask[OAM]; } +unsigned bPPUDebugger::color_window_mask() { return regs.window_mask[COL]; } + +//$212c +bool bPPUDebugger::bg1_mainscreen_enable() { return regs.bg_enabled[BG1]; } +bool bPPUDebugger::bg2_mainscreen_enable() { return regs.bg_enabled[BG2]; } +bool bPPUDebugger::bg3_mainscreen_enable() { return regs.bg_enabled[BG3]; } +bool bPPUDebugger::bg4_mainscreen_enable() { return regs.bg_enabled[BG4]; } +bool bPPUDebugger::oam_mainscreen_enable() { return regs.bg_enabled[OAM]; } + +//$212d +bool bPPUDebugger::bg1_subscreen_enable() { return regs.bgsub_enabled[BG1]; } +bool bPPUDebugger::bg2_subscreen_enable() { return regs.bgsub_enabled[BG2]; } +bool bPPUDebugger::bg3_subscreen_enable() { return regs.bgsub_enabled[BG3]; } +bool bPPUDebugger::bg4_subscreen_enable() { return regs.bgsub_enabled[BG4]; } +bool bPPUDebugger::oam_subscreen_enable() { return regs.bgsub_enabled[OAM]; } + +//$212e +bool bPPUDebugger::bg1_mainscreen_window_enable() { return regs.window_enabled[BG1]; } +bool bPPUDebugger::bg2_mainscreen_window_enable() { return regs.window_enabled[BG2]; } +bool bPPUDebugger::bg3_mainscreen_window_enable() { return regs.window_enabled[BG3]; } +bool bPPUDebugger::bg4_mainscreen_window_enable() { return regs.window_enabled[BG4]; } +bool bPPUDebugger::oam_mainscreen_window_enable() { return regs.window_enabled[OAM]; } + +//$212f +bool bPPUDebugger::bg1_subscreen_window_enable() { return regs.sub_window_enabled[BG1]; } +bool bPPUDebugger::bg2_subscreen_window_enable() { return regs.sub_window_enabled[BG2]; } +bool bPPUDebugger::bg3_subscreen_window_enable() { return regs.sub_window_enabled[BG3]; } +bool bPPUDebugger::bg4_subscreen_window_enable() { return regs.sub_window_enabled[BG4]; } +bool bPPUDebugger::oam_subscreen_window_enable() { return regs.sub_window_enabled[OAM]; } + +//$2130 +unsigned bPPUDebugger::color_mainscreen_window_mask() { return regs.color_mask; } +unsigned bPPUDebugger::color_subscreen_window_mask() { return regs.colorsub_mask; } +bool bPPUDebugger::color_add_subtract_mode() { return regs.addsub_mode; } +bool bPPUDebugger::direct_color() { return regs.direct_color; } + +//$2131 +bool bPPUDebugger::color_mode() { return regs.color_mode; } +bool bPPUDebugger::color_halve() { return regs.color_halve; } +bool bPPUDebugger::bg1_color_enable() { return regs.color_enabled[BG1]; } +bool bPPUDebugger::bg2_color_enable() { return regs.color_enabled[BG2]; } +bool bPPUDebugger::bg3_color_enable() { return regs.color_enabled[BG3]; } +bool bPPUDebugger::bg4_color_enable() { return regs.color_enabled[BG4]; } +bool bPPUDebugger::oam_color_enable() { return regs.color_enabled[OAM]; } +bool bPPUDebugger::back_color_enable() { return regs.color_enabled[BACK]; } + +//$2132 +unsigned bPPUDebugger::color_constant_blue() { return regs.color_b; } +unsigned bPPUDebugger::color_constant_green() { return regs.color_g; } +unsigned bPPUDebugger::color_constant_red() { return regs.color_r; } + +//$2133 +bool bPPUDebugger::mode7_extbg() { return regs.mode7_extbg; } +bool bPPUDebugger::pseudo_hires() { return regs.pseudo_hires; } +bool bPPUDebugger::overscan() { return regs.overscan; } +bool bPPUDebugger::oam_interlace() { return regs.oam_interlace; } +bool bPPUDebugger::interlace() { return regs.interlace; } + +//$213c +unsigned bPPUDebugger::hcounter() { return bPPU::hcounter(); } + +//$213d +unsigned bPPUDebugger::vcounter() { return bPPU::vcounter(); } + +//$213e +bool bPPUDebugger::range_over() { return regs.range_over; } +bool bPPUDebugger::time_over() { return regs.time_over; } +unsigned bPPUDebugger::ppu1_version() { return PPU::ppu1_version; } + +//$213f +bool bPPUDebugger::field() { return cpu.field(); } +bool bPPUDebugger::region() { return bPPU::region; } +unsigned bPPUDebugger::ppu2_version() { return PPU::ppu2_version; } + #endif diff --git a/src/ppu/bppu/debugger/debugger.hpp b/src/ppu/bppu/debugger/debugger.hpp index b1192edb..8d254c5c 100644 --- a/src/ppu/bppu/debugger/debugger.hpp +++ b/src/ppu/bppu/debugger/debugger.hpp @@ -1,5 +1,11 @@ -class bPPUDebug : public bPPU { +class bPPUDebugger : public bPPU, public PPUDebugger { public: + bool bg1_enabled[2]; + bool bg2_enabled[2]; + bool bg3_enabled[2]; + bool bg4_enabled[2]; + bool oam_enabled[4]; + uint8 vram_mmio_read(uint16 addr); void vram_mmio_write(uint16 addr, uint8 data); @@ -8,4 +14,259 @@ public: uint8 cgram_mmio_read(uint16 addr); void cgram_mmio_write(uint16 addr, uint8 data); + + void render_line_mode0(); + void render_line_mode1(); + void render_line_mode2(); + void render_line_mode3(); + void render_line_mode4(); + void render_line_mode5(); + void render_line_mode6(); + void render_line_mode7(); + + bPPUDebugger(); + + //=========== + //PPUDebugger + //=========== + + //internal + unsigned ppu1_mdr(); + unsigned ppu2_mdr(); + + //$2100 + bool display_disable(); + unsigned display_brightness(); + + //$2101 + unsigned oam_base_size(); + unsigned oam_name_select(); + unsigned oam_name_base_address(); + + //$2102-$2103 + unsigned oam_base_address(); + bool oam_priority(); + + //$2105 + bool bg1_tile_size(); + bool bg2_tile_size(); + bool bg3_tile_size(); + bool bg4_tile_size(); + bool bg3_priority(); + unsigned bg_mode(); + + //$2106 + unsigned mosaic_size(); + bool bg1_mosaic_enable(); + bool bg2_mosaic_enable(); + bool bg3_mosaic_enable(); + bool bg4_mosaic_enable(); + + //$2107 + unsigned bg1_screen_address(); + unsigned bg1_screen_size(); + + //$2108 + unsigned bg2_screen_address(); + unsigned bg2_screen_size(); + + //$2109 + unsigned bg3_screen_address(); + unsigned bg3_screen_size(); + + //$210a + unsigned bg4_screen_address(); + unsigned bg4_screen_size(); + + //$210b + unsigned bg1_name_base_address(); + unsigned bg2_name_base_address(); + + //$210c + unsigned bg3_name_base_address(); + unsigned bg4_name_base_address(); + + //$210d + unsigned mode7_hoffset(); + unsigned bg1_hoffset(); + + //$210e + unsigned mode7_voffset(); + unsigned bg1_voffset(); + + //$210f + unsigned bg2_hoffset(); + + //$2110 + unsigned bg2_voffset(); + + //$2111 + unsigned bg3_hoffset(); + + //$2112 + unsigned bg3_voffset(); + + //$2113 + unsigned bg4_hoffset(); + + //$2114 + unsigned bg4_voffset(); + + //$2115 + bool vram_increment_mode(); + unsigned vram_increment_formation(); + unsigned vram_increment_size(); + + //$2116-$2117 + unsigned vram_address(); + + //$211a + unsigned mode7_repeat(); + bool mode7_vflip(); + bool mode7_hflip(); + + //$211b + unsigned mode7_a(); + + //$211c + unsigned mode7_b(); + + //$211d + unsigned mode7_c(); + + //$211e + unsigned mode7_d(); + + //$211f + unsigned mode7_x(); + + //$2120 + unsigned mode7_y(); + + //$2121 + unsigned cgram_address(); + + //$2123 + bool bg1_window1_enable(); + bool bg1_window1_invert(); + bool bg1_window2_enable(); + bool bg1_window2_invert(); + bool bg2_window1_enable(); + bool bg2_window1_invert(); + bool bg2_window2_enable(); + bool bg2_window2_invert(); + + //$2124 + bool bg3_window1_enable(); + bool bg3_window1_invert(); + bool bg3_window2_enable(); + bool bg3_window2_invert(); + bool bg4_window1_enable(); + bool bg4_window1_invert(); + bool bg4_window2_enable(); + bool bg4_window2_invert(); + + //$2125 + bool oam_window1_enable(); + bool oam_window1_invert(); + bool oam_window2_enable(); + bool oam_window2_invert(); + bool color_window1_enable(); + bool color_window1_invert(); + bool color_window2_enable(); + bool color_window2_invert(); + + //$2126 + unsigned window1_left(); + + //$2127 + unsigned window1_right(); + + //$2128 + unsigned window2_left(); + + //$2129 + unsigned window2_right(); + + //$212a + unsigned bg1_window_mask(); + unsigned bg2_window_mask(); + unsigned bg3_window_mask(); + unsigned bg4_window_mask(); + + //$212b + unsigned oam_window_mask(); + unsigned color_window_mask(); + + //$212c + bool bg1_mainscreen_enable(); + bool bg2_mainscreen_enable(); + bool bg3_mainscreen_enable(); + bool bg4_mainscreen_enable(); + bool oam_mainscreen_enable(); + + //$212d + bool bg1_subscreen_enable(); + bool bg2_subscreen_enable(); + bool bg3_subscreen_enable(); + bool bg4_subscreen_enable(); + bool oam_subscreen_enable(); + + //$212e + bool bg1_mainscreen_window_enable(); + bool bg2_mainscreen_window_enable(); + bool bg3_mainscreen_window_enable(); + bool bg4_mainscreen_window_enable(); + bool oam_mainscreen_window_enable(); + + //$212f + bool bg1_subscreen_window_enable(); + bool bg2_subscreen_window_enable(); + bool bg3_subscreen_window_enable(); + bool bg4_subscreen_window_enable(); + bool oam_subscreen_window_enable(); + + //$2130 + unsigned color_mainscreen_window_mask(); + unsigned color_subscreen_window_mask(); + bool color_add_subtract_mode(); + bool direct_color(); + + //$2131 + bool color_mode(); + bool color_halve(); + bool bg1_color_enable(); + bool bg2_color_enable(); + bool bg3_color_enable(); + bool bg4_color_enable(); + bool oam_color_enable(); + bool back_color_enable(); + + //$2132 + unsigned color_constant_blue(); + unsigned color_constant_green(); + unsigned color_constant_red(); + + //$2133 + bool mode7_extbg(); + bool pseudo_hires(); + bool overscan(); + bool oam_interlace(); + bool interlace(); + + //$213c + unsigned hcounter(); + + //$213d + unsigned vcounter(); + + //$213e + bool range_over(); + bool time_over(); + unsigned ppu1_version(); + + //$213f + bool field(); + bool region(); + unsigned ppu2_version(); }; diff --git a/src/ppu/bppu/debugger/render.cpp b/src/ppu/bppu/debugger/render.cpp new file mode 100644 index 00000000..7d2e26a8 --- /dev/null +++ b/src/ppu/bppu/debugger/render.cpp @@ -0,0 +1,194 @@ +#ifdef BPPU_CPP + +//render_line_modeN() taken from src/ppu/bppu/render/render.cpp +//modified to support layer disable; accomplished by setting priority to zero +//a priority of zero won't override the back layer, effectively nullifying it +//for speed, rendering loop is skipped entirely if all priorities are disabled +// +//note: render_line_(bg|oam|mode7) cannot be virtualized as they are templates + +void bPPUDebugger::render_line_mode0() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 8 : 0; + pri1 = bg1_enabled[1] ? 11 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG1, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 7 : 0; + pri1 = bg2_enabled[1] ? 10 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 2 : 0; + pri1 = bg3_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg4_enabled[0] ? 1 : 0; + pri1 = bg4_enabled[1] ? 4 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG4, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 3 : 0; + pri1 = oam_enabled[1] ? 6 : 0; + pri2 = oam_enabled[2] ? 9 : 0; + pri3 = oam_enabled[3] ? 12 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode1() { + unsigned pri0, pri1, pri2, pri3; + + if(regs.bg3_priority) { + pri0 = bg1_enabled[0] ? 5 : 0; + pri1 = bg1_enabled[1] ? 8 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 4 : 0; + pri1 = bg2_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 1 : 0; + pri1 = bg3_enabled[1] ? 10 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 9 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } else { + pri0 = bg1_enabled[0] ? 6 : 0; + pri1 = bg1_enabled[1] ? 9 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 5 : 0; + pri1 = bg2_enabled[1] ? 8 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 1 : 0; + pri1 = bg3_enabled[1] ? 3 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 7 : 0; + pri3 = oam_enabled[3] ? 10 : 0; + bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } +} + +void bPPUDebugger::render_line_mode2() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<2, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<2, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode3() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<3, BG1, COLORDEPTH_256>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<3, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode4() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<4, BG1, COLORDEPTH_256>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<4, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode5() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<5, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<5, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode6() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 2 : 0; + pri1 = bg1_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<6, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 1 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 4 : 0; + pri3 = oam_enabled[3] ? 6 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode7() { + unsigned pri0, pri1, pri2, pri3; + + if(regs.mode7_extbg == false) { + pri0 = bg1_enabled[0] ? 2 : 0; + pri1 = bg1_enabled[1] ? 2 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = oam_enabled[0] ? 1 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 4 : 0; + pri3 = oam_enabled[3] ? 5 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } else { + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 3 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 7 : 0; + bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } +} + +#endif diff --git a/src/ppu/bppu/render/bg.cpp b/src/ppu/bppu/render/bg.cpp index bc1b5475..f3282777 100644 --- a/src/ppu/bppu/render/bg.cpp +++ b/src/ppu/bppu/render/bg.cpp @@ -55,10 +55,6 @@ template void bPPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) { if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; - if(render_enabled(bg, 0) == false) pri0_pos = 0; - if(render_enabled(bg, 1) == false) pri1_pos = 0; - if(pri0_pos == 0 && pri1_pos == 0) return; - const bool bg_enabled = regs.bg_enabled[bg]; const bool bgsub_enabled = regs.bgsub_enabled[bg]; diff --git a/src/ppu/bppu/render/mode7.cpp b/src/ppu/bppu/render/mode7.cpp index 8ae995c7..e012a108 100644 --- a/src/ppu/bppu/render/mode7.cpp +++ b/src/ppu/bppu/render/mode7.cpp @@ -16,10 +16,6 @@ template void bPPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) { if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; - if(render_enabled(bg, 0) == false) pri0_pos = 0; - if(render_enabled(bg, 1) == false) pri1_pos = 1; - if(pri0_pos == 0 && pri1_pos == 0) return; - int32 px, py; int32 tx, ty, tile, palette; diff --git a/src/ppu/bppu/render/oam.cpp b/src/ppu/bppu/render/oam.cpp index e7ed1be6..59c7845d 100644 --- a/src/ppu/bppu/render/oam.cpp +++ b/src/ppu/bppu/render/oam.cpp @@ -189,12 +189,6 @@ void bPPU::render_line_oam_rto() { void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return; - if(render_enabled(OAM, 0) == false) pri0_pos = 0; - if(render_enabled(OAM, 1) == false) pri1_pos = 0; - if(render_enabled(OAM, 2) == false) pri2_pos = 0; - if(render_enabled(OAM, 3) == false) pri3_pos = 0; - if(pri0_pos == 0 && pri1_pos == 0 && pri2_pos == 0 && pri3_pos == 0) return; - for(unsigned s = 0; s < 34; s++) { if(oam_tilelist[s].tile == 0xffff) continue; render_oam_tile(s); diff --git a/src/ppu/bppu/render/render.cpp b/src/ppu/bppu/render/render.cpp index dc36584c..46679871 100644 --- a/src/ppu/bppu/render/render.cpp +++ b/src/ppu/bppu/render/render.cpp @@ -8,17 +8,6 @@ #include "addsub.cpp" #include "line.cpp" -bool bPPU::render_enabled(unsigned bg, unsigned pri) const { - switch(bg) { - case BG1: return config.ppu.bg1_enabled[pri]; - case BG2: return config.ppu.bg2_enabled[pri]; - case BG3: return config.ppu.bg3_enabled[pri]; - case BG4: return config.ppu.bg4_enabled[pri]; - case OAM: return config.ppu.oam_enabled[pri]; - } - return true; -} - //Mode 0: -> // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 // BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 @@ -41,12 +30,12 @@ void bPPU::render_line_mode1() { if(regs.bg3_priority) { render_line_bg<1, BG1, COLORDEPTH_16>(5, 8); render_line_bg<1, BG2, COLORDEPTH_16>(4, 7); - render_line_bg<1, BG3, COLORDEPTH_4 >( 1, 10); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 10); render_line_oam(2, 3, 6, 9); } else { render_line_bg<1, BG1, COLORDEPTH_16>(6, 9); render_line_bg<1, BG2, COLORDEPTH_16>(5, 8); - render_line_bg<1, BG3, COLORDEPTH_4 >( 1, 3); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 3); render_line_oam(2, 4, 7, 10); } } diff --git a/src/ppu/bppu/render/render.hpp b/src/ppu/bppu/render/render.hpp index c6ff6b36..6de43e41 100644 --- a/src/ppu/bppu/render/render.hpp +++ b/src/ppu/bppu/render/render.hpp @@ -1,14 +1,12 @@ //render.cpp -bool render_enabled(unsigned bg, unsigned pri) const; - -inline void render_line_mode0(); -inline void render_line_mode1(); -inline void render_line_mode2(); -inline void render_line_mode3(); -inline void render_line_mode4(); -inline void render_line_mode5(); -inline void render_line_mode6(); -inline void render_line_mode7(); +debugvirtual inline void render_line_mode0(); +debugvirtual inline void render_line_mode1(); +debugvirtual inline void render_line_mode2(); +debugvirtual inline void render_line_mode3(); +debugvirtual inline void render_line_mode4(); +debugvirtual inline void render_line_mode5(); +debugvirtual inline void render_line_mode6(); +debugvirtual inline void render_line_mode7(); //cache.cpp enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 }; diff --git a/src/ppu/ppu-debugger.cpp b/src/ppu/ppu-debugger.cpp new file mode 100644 index 00000000..df06f222 --- /dev/null +++ b/src/ppu/ppu-debugger.cpp @@ -0,0 +1,305 @@ +#ifdef PPU_CPP + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-PPU1 MDR"; value = string::printf("0x%.2x", ppu1_mdr()); return true; } + if(id == n++) { name = "S-PPU2 MDR"; value = string::printf("0x%.2x", ppu2_mdr()); return true; } + + //$2100 + if(id == n++) { name = "$2100"; value = ""; return true; } + if(id == n++) { name = "Display Disable"; value = display_disable(); return true; } + if(id == n++) { name = "Display Brightness"; value = display_brightness(); return true; } + + //$2101 + if(id == n++) { name = "$2101"; value = ""; return true; } + if(id == n++) { name = "OAM Base Size"; value = oam_base_size(); return true; } + if(id == n++) { name = "OAM Name Select"; value = oam_name_select(); return true; } + if(id == n++) { name = "OAM Name Base Address"; value = string::printf("0x%.4x", oam_name_base_address()); return true; } + + //$2102-$2103 + if(id == n++) { name = "$2102-$2103"; value = ""; return true; } + if(id == n++) { name = "OAM Base Address"; value = string::printf("0x%.4x", oam_base_address()); return true; } + if(id == n++) { name = "OAM Priority"; value = oam_priority(); return true; } + + //$2105 + if(id == n++) { name = "$2105"; value = ""; return true; } + if(id == n++) { name = "BG1 Tile Size"; value = bg1_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG2 Tile Size"; value = bg2_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Tile Size"; value = bg3_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG4 Tile Size"; value = bg4_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Priority"; value = bg3_priority(); return true; } + if(id == n++) { name = "BG Mode"; value = bg_mode(); return true; } + + //$2106 + if(id == n++) { name = "$2106"; value = ""; return true; } + if(id == n++) { name = "Mosaic Size"; value = mosaic_size(); return true; } + if(id == n++) { name = "BG1 Mosaic Enable"; value = bg1_mosaic_enable(); return true; } + if(id == n++) { name = "BG2 Mosaic Enable"; value = bg2_mosaic_enable(); return true; } + if(id == n++) { name = "BG3 Mosaic Enable"; value = bg3_mosaic_enable(); return true; } + if(id == n++) { name = "BG4 Mosaic Enable"; value = bg4_mosaic_enable(); return true; } + + static char screen_size[4][8] = { "32x32", "32x64", "64x32", "64x64" }; + + //$2107 + if(id == n++) { name = "$2107"; value = ""; return true; } + if(id == n++) { name = "BG1 Screen Address"; value = string::printf("0x%.4x", bg1_screen_address()); return true; } + if(id == n++) { name = "BG1 Screen Size"; value = screen_size[bg1_screen_size()]; return true; } + + //$2108 + if(id == n++) { name = "$2108"; value = ""; return true; } + if(id == n++) { name = "BG2 Screen Address"; value = string::printf("0x%.4x", bg2_screen_address()); return true; } + if(id == n++) { name = "BG2 Screen Size"; value = screen_size[bg2_screen_size()]; return true; } + + //$2109 + if(id == n++) { name = "$2109"; value = ""; return true; } + if(id == n++) { name = "BG3 Screen Address"; value = string::printf("0x%.4x", bg3_screen_address()); return true; } + if(id == n++) { name = "BG3 Screen Size"; value = screen_size[bg3_screen_size()]; return true; } + + //$210a + if(id == n++) { name = "$210a"; value = ""; return true; } + if(id == n++) { name = "BG4 Screen Address"; value = string::printf("0x%.4x", bg4_screen_address()); return true; } + if(id == n++) { name = "BG4 Screen Size"; value = screen_size[bg4_screen_size()]; return true; } + + //$210b + if(id == n++) { name = "$210b"; value = ""; return true; } + if(id == n++) { name = "BG1 Name Base Address"; value = string::printf("0x%.4x", bg1_name_base_address()); return true; } + if(id == n++) { name = "BG2 Name Base Address"; value = string::printf("0x%.4x", bg2_name_base_address()); return true; } + + //$210c + if(id == n++) { name = "$210c"; value = ""; return true; } + if(id == n++) { name = "BG3 Name Base Address"; value = string::printf("0x%.4x", bg3_name_base_address()); return true; } + if(id == n++) { name = "BG4 Name Base Address"; value = string::printf("0x%.4x", bg4_name_base_address()); return true; } + + //$210d + if(id == n++) { name = "$210d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll H-offset"; value = mode7_hoffset(); return true; } + if(id == n++) { name = "BG1 Scroll H-offset"; value = bg1_hoffset(); return true; } + + //$210e + if(id == n++) { name = "$210e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll V-offset"; value = mode7_voffset(); return true; } + if(id == n++) { name = "BG1 Scroll V-offset"; value = bg1_voffset(); return true; } + + //$210f + if(id == n++) { name = "$210f"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll H-offset"; value = bg2_hoffset(); return true; } + + //$2110 + if(id == n++) { name = "$2110"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll V-offset"; value = bg2_voffset(); return true; } + + //$2111 + if(id == n++) { name = "$2111"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll H-offset"; value = bg3_hoffset(); return true; } + + //$2112 + if(id == n++) { name = "$2112"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll V-offset"; value = bg3_voffset(); return true; } + + //$2113 + if(id == n++) { name = "$2113"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll H-offset"; value = bg4_hoffset(); return true; } + + //$2114 + if(id == n++) { name = "$2114"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll V-offset"; value = bg4_voffset(); return true; } + + //$2115 + if(id == n++) { name = "$2115"; value = ""; return true; } + if(id == n++) { name = "VRAM Increment Mode"; value = (unsigned)vram_increment_mode(); return true; } + if(id == n++) { name = "VRAM Increment Formation"; value = vram_increment_formation(); return true; } + if(id == n++) { name = "VRAM Increment Size"; value = vram_increment_size(); return true; } + + //$2116-$2117 + if(id == n++) { name = "$2116-$2117"; value = ""; return true; } + if(id == n++) { name = "VRAM Address"; value = string::printf("0x%.4x", vram_address()); return true; } + + //$211a + if(id == n++) { name = "$211a"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Repeat"; value = mode7_repeat(); return true; } + if(id == n++) { name = "Mode 7 V-flip"; value = mode7_vflip(); return true; } + if(id == n++) { name = "Mode 7 H-flip"; value = mode7_hflip(); return true; } + + //$211b + if(id == n++) { name = "$211b"; value = ""; return true; } + if(id == n++) { name = "Mode 7 A"; value = mode7_a(); return true; } + + //$211c + if(id == n++) { name = "$211c"; value = ""; return true; } + if(id == n++) { name = "Mode 7 B"; value = mode7_b(); return true; } + + //$211d + if(id == n++) { name = "$211d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 C"; value = mode7_c(); return true; } + + //$211e + if(id == n++) { name = "$211e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 D"; value = mode7_d(); return true; } + + //$211f + if(id == n++) { name = "$211f"; value = ""; return true; } + if(id == n++) { name = "Mode 7 X"; value = mode7_x(); return true; } + + //$2120 + if(id == n++) { name = "$2120"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Y"; value = mode7_y(); return true; } + + //$2121 + if(id == n++) { name = "$2121"; value = ""; return true; } + if(id == n++) { name = "CGRAM Address"; value = string::printf("0x%.4x", cgram_address()); return true; } + + //$2123 + if(id == n++) { name = "$2123"; value = ""; return true; } + if(id == n++) { name = "BG1 Window 1 Enable"; value = bg1_window1_enable(); return true; } + if(id == n++) { name = "BG1 Window 1 Invert"; value = bg1_window1_invert(); return true; } + if(id == n++) { name = "BG1 Window 2 Enable"; value = bg1_window2_enable(); return true; } + if(id == n++) { name = "BG1 Window 2 Invert"; value = bg1_window2_invert(); return true; } + if(id == n++) { name = "BG2 Window 1 Enable"; value = bg2_window1_enable(); return true; } + if(id == n++) { name = "BG2 Window 1 Invert"; value = bg2_window1_invert(); return true; } + if(id == n++) { name = "BG2 Window 2 Enable"; value = bg2_window2_enable(); return true; } + if(id == n++) { name = "BG2 Window 2 Invert"; value = bg2_window2_invert(); return true; } + + //$2124 + if(id == n++) { name = "$2124"; value = ""; return true; } + if(id == n++) { name = "BG3 Window 1 Enable"; value = bg3_window1_enable(); return true; } + if(id == n++) { name = "BG3 Window 1 Invert"; value = bg3_window1_invert(); return true; } + if(id == n++) { name = "BG3 Window 2 Enable"; value = bg3_window2_enable(); return true; } + if(id == n++) { name = "BG3 Window 2 Invert"; value = bg3_window2_invert(); return true; } + if(id == n++) { name = "BG4 Window 1 Enable"; value = bg4_window1_enable(); return true; } + if(id == n++) { name = "BG4 Window 1 Invert"; value = bg4_window1_invert(); return true; } + if(id == n++) { name = "BG4 Window 2 Enable"; value = bg4_window2_enable(); return true; } + if(id == n++) { name = "BG4 Window 2 Invert"; value = bg4_window2_invert(); return true; } + + //$2125 + if(id == n++) { name = "$2125"; value = ""; return true; } + if(id == n++) { name = "OAM Window 1 Enable"; value = oam_window1_enable(); return true; } + if(id == n++) { name = "OAM Window 1 Invert"; value = oam_window1_invert(); return true; } + if(id == n++) { name = "OAM Window 2 Enable"; value = oam_window2_enable(); return true; } + if(id == n++) { name = "OAM Window 2 Invert"; value = oam_window2_invert(); return true; } + if(id == n++) { name = "Color Window 1 Enable"; value = color_window1_enable(); return true; } + if(id == n++) { name = "Color Window 1 Invert"; value = color_window1_invert(); return true; } + if(id == n++) { name = "Color Window 2 Enable"; value = color_window2_enable(); return true; } + if(id == n++) { name = "Color Window 2 Invert"; value = color_window2_invert(); return true; } + + //$2126 + if(id == n++) { name = "$2126"; value = ""; return true; } + if(id == n++) { name = "Window 1 Left"; value = window1_left(); return true; } + + //$2127 + if(id == n++) { name = "$2127"; value = ""; return true; } + if(id == n++) { name = "Window 1 Right"; value = window1_right(); return true; } + + //$2128 + if(id == n++) { name = "$2128"; value = ""; return true; } + if(id == n++) { name = "Window 2 Left"; value = window2_left(); return true; } + + //$2129 + if(id == n++) { name = "$2129"; value = ""; return true; } + if(id == n++) { name = "Window 2 Right"; value = window2_right(); return true; } + + static char window_mask_mode[4][8] = { "OR", "AND", "XOR", "XNOR" }; + + //$212a + if(id == n++) { name = "$212a"; value = ""; return true; } + if(id == n++) { name = "BG1 Window Mask"; value = window_mask_mode[bg1_window_mask()]; return true; } + if(id == n++) { name = "BG2 Window Mask"; value = window_mask_mode[bg2_window_mask()]; return true; } + if(id == n++) { name = "BG3 Window Mask"; value = window_mask_mode[bg3_window_mask()]; return true; } + if(id == n++) { name = "BG4 Window Mask"; value = window_mask_mode[bg4_window_mask()]; return true; } + + //$212b + if(id == n++) { name = "$212b"; value = ""; return true; } + if(id == n++) { name = "OAM Window Mask"; value = window_mask_mode[oam_window_mask()]; return true; } + if(id == n++) { name = "Color Window Mask"; value = window_mask_mode[color_window_mask()]; return true; } + + //$212c + if(id == n++) { name = "$212c"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Enable"; value = bg1_mainscreen_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Enable"; value = bg2_mainscreen_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Enable"; value = bg3_mainscreen_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Enable"; value = bg4_mainscreen_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Enable"; value = oam_mainscreen_enable(); return true; } + + //$212d + if(id == n++) { name = "$212d"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Enable"; value = bg1_subscreen_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Enable"; value = bg2_subscreen_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Enable"; value = bg3_subscreen_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Enable"; value = bg4_subscreen_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Enable"; value = oam_subscreen_enable(); return true; } + + //$212e + if(id == n++) { name = "$212e"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Window Enable"; value = bg1_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Window Enable"; value = bg2_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Window Enable"; value = bg3_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Window Enable"; value = bg4_mainscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Window Enable"; value = oam_mainscreen_window_enable(); return true; } + + //$212f + if(id == n++) { name = "$212f"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Window Enable"; value = bg1_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Window Enable"; value = bg2_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Window Enable"; value = bg3_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Window Enable"; value = bg4_subscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Window Enable"; value = oam_subscreen_window_enable(); return true; } + + static char color_window_mask_mode[4][32] = { "Always", "Never", "Inside Window Only", "Outside Window Only" }; + + //$2130 + if(id == n++) { name = "$2130"; value = ""; return true; } + if(id == n++) { name = "Color Mainscreen Window Mask"; value = color_window_mask_mode[color_mainscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Subscreen Window Mask"; value = color_window_mask_mode[color_subscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Add/Subtract Mode"; value = !color_add_subtract_mode() ? "Fixed Color" : "Subscreen"; return true; } + if(id == n++) { name = "Direct Color"; value = direct_color(); return true; } + + //$2131 + if(id == n++) { name = "$2131"; value = ""; return true; } + if(id == n++) { name = "Color Mode"; value = !color_mode() ? "Add" : "Subtract"; return true; } + if(id == n++) { name = "Color Halve"; value = color_halve(); return true; } + if(id == n++) { name = "BG1 Color Enable"; value = bg1_color_enable(); return true; } + if(id == n++) { name = "BG2 Color Enable"; value = bg2_color_enable(); return true; } + if(id == n++) { name = "BG3 Color Enable"; value = bg3_color_enable(); return true; } + if(id == n++) { name = "BG4 Color Enable"; value = bg4_color_enable(); return true; } + if(id == n++) { name = "OAM Color Enable"; value = oam_color_enable(); return true; } + if(id == n++) { name = "Back Color Enable"; value = back_color_enable(); return true; } + + //$2132 + if(id == n++) { name = "$2132"; value = ""; return true; } + if(id == n++) { name = "Color Constant - Blue"; value = color_constant_blue(); return true; } + if(id == n++) { name = "Color Constant - Green"; value = color_constant_green(); return true; } + if(id == n++) { name = "Color Constant - Red"; value = color_constant_red(); return true; } + + //$2133 + if(id == n++) { name = "$2133"; value = ""; return true; } + if(id == n++) { name = "Mode 7 EXTBG"; value = mode7_extbg(); return true; } + if(id == n++) { name = "Pseudo Hires"; value = pseudo_hires(); return true; } + if(id == n++) { name = "Overscan"; value = overscan(); return true; } + if(id == n++) { name = "OAM Interlace"; value = oam_interlace(); return true; } + if(id == n++) { name = "Interlace"; value = interlace(); return true; } + + //$213c + if(id == n++) { name = "$213c"; value = ""; return true; } + if(id == n++) { name = "H-counter"; value = hcounter(); return true; } + + //$213d + if(id == n++) { name = "$213d"; value = ""; return true; } + if(id == n++) { name = "V-counter"; value = vcounter(); return true; } + + //$213e + if(id == n++) { name = "$213e"; value = ""; return true; } + if(id == n++) { name = "Range Over"; value = range_over(); return true; } + if(id == n++) { name = "Time Over"; value = time_over(); return true; } + if(id == n++) { name = "S-PPU1 Version"; value = ppu1_version(); return true; } + + //$213f + if(id == n++) { name = "$213f"; value = ""; return true; } + if(id == n++) { name = "Field"; value = field(); return true; } + if(id == n++) { name = "Region"; value = !region() ? "NTSC" : "PAL"; return true; } + if(id == n++) { name = "S-PPU2 Version"; value = ppu2_version(); return true; } + + return false; +} + +#endif diff --git a/src/ppu/ppu-debugger.hpp b/src/ppu/ppu-debugger.hpp new file mode 100644 index 00000000..800bedd1 --- /dev/null +++ b/src/ppu/ppu-debugger.hpp @@ -0,0 +1,243 @@ +struct PPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned ppu1_mdr() { return 0; } + virtual unsigned ppu2_mdr() { return 0; } + + //$2100 + virtual bool display_disable() { return 0; } + virtual unsigned display_brightness() { return 0; } + + //$2101 + virtual unsigned oam_base_size() { return 0; } + virtual unsigned oam_name_select() { return 0; } + virtual unsigned oam_name_base_address() { return 0; } + + //$2102-$2103 + virtual unsigned oam_base_address() { return 0; } + virtual bool oam_priority() { return 0; } + + //$2105 + virtual bool bg1_tile_size() { return 0; } + virtual bool bg2_tile_size() { return 0; } + virtual bool bg3_tile_size() { return 0; } + virtual bool bg4_tile_size() { return 0; } + virtual bool bg3_priority() { return 0; } + virtual unsigned bg_mode() { return 0; } + + //$2106 + virtual unsigned mosaic_size() { return 0; } + virtual bool bg1_mosaic_enable() { return 0; } + virtual bool bg2_mosaic_enable() { return 0; } + virtual bool bg3_mosaic_enable() { return 0; } + virtual bool bg4_mosaic_enable() { return 0; } + + //$2107 + virtual unsigned bg1_screen_address() { return 0; } + virtual unsigned bg1_screen_size() { return 0; } + + //$2108 + virtual unsigned bg2_screen_address() { return 0; } + virtual unsigned bg2_screen_size() { return 0; } + + //$2109 + virtual unsigned bg3_screen_address() { return 0; } + virtual unsigned bg3_screen_size() { return 0; } + + //$210a + virtual unsigned bg4_screen_address() { return 0; } + virtual unsigned bg4_screen_size() { return 0; } + + //$210b + virtual unsigned bg1_name_base_address() { return 0; } + virtual unsigned bg2_name_base_address() { return 0; } + + //$210c + virtual unsigned bg3_name_base_address() { return 0; } + virtual unsigned bg4_name_base_address() { return 0; } + + //$210d + virtual unsigned mode7_hoffset() { return 0; } + virtual unsigned bg1_hoffset() { return 0; } + + //$210e + virtual unsigned mode7_voffset() { return 0; } + virtual unsigned bg1_voffset() { return 0; } + + //$210f + virtual unsigned bg2_hoffset() { return 0; } + + //$2110 + virtual unsigned bg2_voffset() { return 0; } + + //$2111 + virtual unsigned bg3_hoffset() { return 0; } + + //$2112 + virtual unsigned bg3_voffset() { return 0; } + + //$2113 + virtual unsigned bg4_hoffset() { return 0; } + + //$2114 + virtual unsigned bg4_voffset() { return 0; } + + //$2115 + virtual bool vram_increment_mode() { return 0; } + virtual unsigned vram_increment_formation() { return 0; } + virtual unsigned vram_increment_size() { return 0; } + + //$2116-$2117 + virtual unsigned vram_address() { return 0; } + + //$211a + virtual unsigned mode7_repeat() { return 0; } + virtual bool mode7_vflip() { return 0; } + virtual bool mode7_hflip() { return 0; } + + //$211b + virtual unsigned mode7_a() { return 0; } + + //$211c + virtual unsigned mode7_b() { return 0; } + + //$211d + virtual unsigned mode7_c() { return 0; } + + //$211e + virtual unsigned mode7_d() { return 0; } + + //$211f + virtual unsigned mode7_x() { return 0; } + + //$2120 + virtual unsigned mode7_y() { return 0; } + + //$2121 + virtual unsigned cgram_address() { return 0; } + + //$2123 + virtual bool bg1_window1_enable() { return 0; } + virtual bool bg1_window1_invert() { return 0; } + virtual bool bg1_window2_enable() { return 0; } + virtual bool bg1_window2_invert() { return 0; } + virtual bool bg2_window1_enable() { return 0; } + virtual bool bg2_window1_invert() { return 0; } + virtual bool bg2_window2_enable() { return 0; } + virtual bool bg2_window2_invert() { return 0; } + + //$2124 + virtual bool bg3_window1_enable() { return 0; } + virtual bool bg3_window1_invert() { return 0; } + virtual bool bg3_window2_enable() { return 0; } + virtual bool bg3_window2_invert() { return 0; } + virtual bool bg4_window1_enable() { return 0; } + virtual bool bg4_window1_invert() { return 0; } + virtual bool bg4_window2_enable() { return 0; } + virtual bool bg4_window2_invert() { return 0; } + + //$2125 + virtual bool oam_window1_enable() { return 0; } + virtual bool oam_window1_invert() { return 0; } + virtual bool oam_window2_enable() { return 0; } + virtual bool oam_window2_invert() { return 0; } + virtual bool color_window1_enable() { return 0; } + virtual bool color_window1_invert() { return 0; } + virtual bool color_window2_enable() { return 0; } + virtual bool color_window2_invert() { return 0; } + + //$2126 + virtual unsigned window1_left() { return 0; } + + //$2127 + virtual unsigned window1_right() { return 0; } + + //$2128 + virtual unsigned window2_left() { return 0; } + + //$2129 + virtual unsigned window2_right() { return 0; } + + //$212a + virtual unsigned bg1_window_mask() { return 0; } + virtual unsigned bg2_window_mask() { return 0; } + virtual unsigned bg3_window_mask() { return 0; } + virtual unsigned bg4_window_mask() { return 0; } + + //$212b + virtual unsigned oam_window_mask() { return 0; } + virtual unsigned color_window_mask() { return 0; } + + //$212c + virtual bool bg1_mainscreen_enable() { return 0; } + virtual bool bg2_mainscreen_enable() { return 0; } + virtual bool bg3_mainscreen_enable() { return 0; } + virtual bool bg4_mainscreen_enable() { return 0; } + virtual bool oam_mainscreen_enable() { return 0; } + + //$212d + virtual bool bg1_subscreen_enable() { return 0; } + virtual bool bg2_subscreen_enable() { return 0; } + virtual bool bg3_subscreen_enable() { return 0; } + virtual bool bg4_subscreen_enable() { return 0; } + virtual bool oam_subscreen_enable() { return 0; } + + //$212e + virtual bool bg1_mainscreen_window_enable() { return 0; } + virtual bool bg2_mainscreen_window_enable() { return 0; } + virtual bool bg3_mainscreen_window_enable() { return 0; } + virtual bool bg4_mainscreen_window_enable() { return 0; } + virtual bool oam_mainscreen_window_enable() { return 0; } + + //$212f + virtual bool bg1_subscreen_window_enable() { return 0; } + virtual bool bg2_subscreen_window_enable() { return 0; } + virtual bool bg3_subscreen_window_enable() { return 0; } + virtual bool bg4_subscreen_window_enable() { return 0; } + virtual bool oam_subscreen_window_enable() { return 0; } + + //$2130 + virtual unsigned color_mainscreen_window_mask() { return 0; } + virtual unsigned color_subscreen_window_mask() { return 0; } + virtual bool color_add_subtract_mode() { return 0; } + virtual bool direct_color() { return 0; } + + //$2131 + virtual bool color_mode() { return 0; } + virtual bool color_halve() { return 0; } + virtual bool bg1_color_enable() { return 0; } + virtual bool bg2_color_enable() { return 0; } + virtual bool bg3_color_enable() { return 0; } + virtual bool bg4_color_enable() { return 0; } + virtual bool oam_color_enable() { return 0; } + virtual bool back_color_enable() { return 0; } + + //$2132 + virtual unsigned color_constant_blue() { return 0; } + virtual unsigned color_constant_green() { return 0; } + virtual unsigned color_constant_red() { return 0; } + + //$2133 + virtual bool mode7_extbg() { return 0; } + virtual bool pseudo_hires() { return 0; } + virtual bool overscan() { return 0; } + virtual bool oam_interlace() { return 0; } + virtual bool interlace() { return 0; } + + //$213c + virtual unsigned hcounter() { return 0; } + + //$213d + virtual unsigned vcounter() { return 0; } + + //$213e + virtual bool range_over() { return 0; } + virtual bool time_over() { return 0; } + virtual unsigned ppu1_version() { return 0; } + + //$213f + virtual bool field() { return 0; } + virtual bool region() { return 0; } + virtual unsigned ppu2_version() { return 0; } +}; diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index a4ae5e80..c327601e 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -3,6 +3,10 @@ #define PPU_CPP namespace SNES { +#if defined(DEBUGGER) + #include "ppu-debugger.cpp" +#endif + #include "serialization.cpp" void PPU::enable_renderer(bool r) { status.render_output = r; } diff --git a/src/ppu/ppu.hpp b/src/ppu/ppu.hpp index cce262eb..c4470b93 100644 --- a/src/ppu/ppu.hpp +++ b/src/ppu/ppu.hpp @@ -1,3 +1,7 @@ +#if defined(DEBUGGER) + #include "ppu-debugger.hpp" +#endif + //PPUcounter emulates the H/V latch counters of the S-PPU2. // //real hardware has the S-CPU maintain its own copy of these counters that are diff --git a/src/smp/core/core.cpp b/src/smp/core/core.cpp index c44bf50f..fe6b1f52 100644 --- a/src/smp/core/core.cpp +++ b/src/smp/core/core.cpp @@ -5,7 +5,7 @@ namespace SNES { #include "serialization.cpp" #include "algorithms.cpp" -#include "disasm/disasm.cpp" +#include "disassembler/disassembler.cpp" #define A 0 #define X 1 diff --git a/src/smp/core/core.hpp b/src/smp/core/core.hpp index 98f2a357..85e8c74a 100644 --- a/src/smp/core/core.hpp +++ b/src/smp/core/core.hpp @@ -2,7 +2,7 @@ class SMPcore { public: #include "registers.hpp" #include "memory.hpp" - #include "disasm/disasm.hpp" + #include "disassembler/disassembler.hpp" regs_t regs; uint16 dp, sp, rd, wr, bit, ya; diff --git a/src/smp/core/disasm/disasm.cpp b/src/smp/core/disassembler/disassembler.cpp similarity index 100% rename from src/smp/core/disasm/disasm.cpp rename to src/smp/core/disassembler/disassembler.cpp diff --git a/src/smp/core/disasm/disasm.hpp b/src/smp/core/disassembler/disassembler.hpp similarity index 100% rename from src/smp/core/disasm/disasm.hpp rename to src/smp/core/disassembler/disassembler.hpp diff --git a/src/smp/core/opcode_misc.cpp b/src/smp/core/opcode_misc.cpp index f21307e5..0f4d4ce3 100644 --- a/src/smp/core/opcode_misc.cpp +++ b/src/smp/core/opcode_misc.cpp @@ -1,3 +1,5 @@ +#ifdef SMPCORE_CPP + void SMPcore::op_nop() { op_io(); } @@ -142,3 +144,5 @@ void SMPcore::op_div_ya_x() { regs.p.n = !!(regs.a & 0x80); regs.p.z = (regs.a == 0); } + +#endif diff --git a/src/smp/core/opcode_mov.cpp b/src/smp/core/opcode_mov.cpp index 9cfce616..e21593c8 100644 --- a/src/smp/core/opcode_mov.cpp +++ b/src/smp/core/opcode_mov.cpp @@ -1,3 +1,5 @@ +#ifdef SMPCORE_CPP + template void SMPcore::op_mov_reg_reg() { op_io(); regs.r[to] = regs.r[from]; @@ -194,3 +196,5 @@ void SMPcore::op_mov1_bit_c() { op_io(); op_writeaddr(dp, rd); } + +#endif diff --git a/src/smp/core/opcode_pc.cpp b/src/smp/core/opcode_pc.cpp index 7cbc4b98..b69d8416 100644 --- a/src/smp/core/opcode_pc.cpp +++ b/src/smp/core/opcode_pc.cpp @@ -1,3 +1,5 @@ +#ifdef SMPCORE_CPP + void SMPcore::op_bra() { rd = op_readpc(); op_io(); @@ -146,3 +148,5 @@ void SMPcore::op_reti() { op_io(); regs.pc = rd; } + +#endif diff --git a/src/smp/core/opcode_read.cpp b/src/smp/core/opcode_read.cpp index 811841a5..2058cff2 100644 --- a/src/smp/core/opcode_read.cpp +++ b/src/smp/core/opcode_read.cpp @@ -1,3 +1,5 @@ +#ifdef SMPCORE_CPP + template void SMPcore::op_read_reg_const() { rd = op_readpc(); @@ -148,3 +150,5 @@ template void SMPcore::op_or1_bit() { op_io(); regs.p.c = regs.p.c | ((bool)(rd & (1 << bit)) ^ op); } + +#endif diff --git a/src/smp/core/opcode_rmw.cpp b/src/smp/core/opcode_rmw.cpp index 384a84ab..103bd2db 100644 --- a/src/smp/core/opcode_rmw.cpp +++ b/src/smp/core/opcode_rmw.cpp @@ -1,3 +1,5 @@ +#ifdef SMPCORE_CPP + template void SMPcore::op_adjust_reg() { op_io(); @@ -52,3 +54,5 @@ void SMPcore::op_adjustw_dp() { regs.p.n = (rd & 0x8000); regs.p.z = (rd == 0); } + +#endif diff --git a/src/smp/ssmp/debugger/debugger.cpp b/src/smp/ssmp/debugger/debugger.cpp index 2e69f9af..6db906cb 100644 --- a/src/smp/ssmp/debugger/debugger.cpp +++ b/src/smp/ssmp/debugger/debugger.cpp @@ -1,43 +1,43 @@ #ifdef SSMP_CPP -void sSMPDebug::op_step() { +void sSMPDebugger::op_step() { bool break_event = false; - if(debugger.step_smp) { - debugger.break_event = Debugger::SMPStep; - scheduler.exit(); - } else { - debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Exec, regs.pc, 0x00); - } - usage[regs.pc] |= UsageExec; opcode_pc = regs.pc; + if(debugger.step_smp) { + debugger.break_event = Debugger::SMPStep; + scheduler.exit(Scheduler::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Exec, regs.pc, 0x00); + } + if(step_event) step_event(); sSMP::op_step(); scheduler.sync_smpcpu(); } -uint8 sSMPDebug::op_read(uint16 addr) { +uint8 sSMPDebugger::op_read(uint16 addr) { uint8 data = sSMP::op_read(addr); usage[addr] |= UsageRead; debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Read, addr, data); return data; } -void sSMPDebug::op_write(uint16 addr, uint8 data) { +void sSMPDebugger::op_write(uint16 addr, uint8 data) { sSMP::op_write(addr, data); usage[addr] |= UsageWrite; usage[addr] &= ~UsageExec; debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Write, addr, data); } -sSMPDebug::sSMPDebug() { +sSMPDebugger::sSMPDebugger() { usage = new uint8[1 << 16](); opcode_pc = 0xffc0; } -sSMPDebug::~sSMPDebug() { +sSMPDebugger::~sSMPDebugger() { delete[] usage; } diff --git a/src/smp/ssmp/debugger/debugger.hpp b/src/smp/ssmp/debugger/debugger.hpp index 384863c8..b67be979 100644 --- a/src/smp/ssmp/debugger/debugger.hpp +++ b/src/smp/ssmp/debugger/debugger.hpp @@ -1,4 +1,4 @@ -class sSMPDebug : public sSMP { +class sSMPDebugger : public sSMP { public: function step_event; @@ -14,6 +14,6 @@ public: uint8 op_read(uint16 addr); void op_write(uint16 addr, uint8 data); - sSMPDebug(); - ~sSMPDebug(); + sSMPDebugger(); + ~sSMPDebugger(); }; diff --git a/src/smp/ssmp/ssmp.cpp b/src/smp/ssmp/ssmp.cpp index ed76787e..fb4575f5 100644 --- a/src/smp/ssmp/ssmp.cpp +++ b/src/smp/ssmp/ssmp.cpp @@ -5,7 +5,7 @@ namespace SNES { #if defined(DEBUGGER) #include "debugger/debugger.cpp" - sSMPDebug smp; + sSMPDebugger smp; #else sSMP smp; #endif @@ -16,7 +16,10 @@ namespace SNES { void sSMP::enter() { while(true) { - if(scheduler.sync == Scheduler::SyncAll) scheduler.exit(); + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + op_step(); } } diff --git a/src/smp/ssmp/ssmp.hpp b/src/smp/ssmp/ssmp.hpp index 00facf41..1a83c1b6 100644 --- a/src/smp/ssmp/ssmp.hpp +++ b/src/smp/ssmp/ssmp.hpp @@ -43,7 +43,7 @@ public: #if defined(DEBUGGER) #include "debugger/debugger.hpp" - extern sSMPDebug smp; + extern sSMPDebugger smp; #else extern sSMP smp; #endif diff --git a/src/system/config/config.cpp b/src/system/config/config.cpp index 062d7e3a..5c6624cc 100644 --- a/src/system/config/config.cpp +++ b/src/system/config/config.cpp @@ -18,12 +18,6 @@ Configuration::Configuration() { smp.ntsc_clock_rate = 24607104; //32040.5 * 768 smp.pal_clock_rate = 24607104; - ppu.bg1_enabled[0] = ppu.bg1_enabled[1] = true; - ppu.bg2_enabled[0] = ppu.bg2_enabled[1] = true; - ppu.bg3_enabled[0] = ppu.bg3_enabled[1] = true; - ppu.bg4_enabled[0] = ppu.bg4_enabled[1] = true; - ppu.oam_enabled[0] = ppu.oam_enabled[1] = ppu.oam_enabled[2] = ppu.oam_enabled[3] = true; - ppu1.version = 1; ppu2.version = 3; diff --git a/src/system/config/config.hpp b/src/system/config/config.hpp index 9e5ad7a5..d28f8928 100644 --- a/src/system/config/config.hpp +++ b/src/system/config/config.hpp @@ -18,14 +18,6 @@ struct Configuration { unsigned pal_clock_rate; } smp; - struct PPU { - bool bg1_enabled[2]; - bool bg2_enabled[2]; - bool bg3_enabled[2]; - bool bg4_enabled[2]; - bool oam_enabled[4]; - } ppu; - struct PPU1 { unsigned version; } ppu1; diff --git a/src/system/debugger/debugger.cpp b/src/system/debugger/debugger.cpp index 2a5482de..a1bb2a0e 100644 --- a/src/system/debugger/debugger.cpp +++ b/src/system/debugger/debugger.cpp @@ -5,7 +5,18 @@ Debugger debugger; void Debugger::breakpoint_test(Debugger::Breakpoint::Source source, Debugger::Breakpoint::Mode mode, unsigned addr, uint8 data) { for(unsigned i = 0; i < Breakpoints; i++) { if(breakpoint[i].enabled == false) continue; - if(breakpoint[i].addr != addr) continue; + + //shadow S-CPU WRAM addresses ($00-3f|80-bf:0000-1fff mirrors $7e:0000-1fff) + if(source == Debugger::Breakpoint::CPUBus && ( + ((breakpoint[i].addr & 0x40e000) == 0x000000) || //$00-3f|80-bf:0000-1fff + ((breakpoint[i].addr & 0xffe000) == 0x7e0000) //$7e:0000-1fff + ) + ) { + if((breakpoint[i].addr & 0x1fff) != (addr & 0x1fff)) continue; + } else { + if(breakpoint[i].addr != addr) continue; + } + if(breakpoint[i].data != -1 && breakpoint[i].data != data) continue; if(breakpoint[i].source != source) continue; if(breakpoint[i].mode != mode) continue; @@ -13,7 +24,7 @@ void Debugger::breakpoint_test(Debugger::Breakpoint::Source source, Debugger::Br breakpoint[i].counter++; breakpoint_hit = i; break_event = BreakpointHit; - scheduler.exit(); + scheduler.exit(Scheduler::DebuggerEvent); break; } } diff --git a/src/system/scheduler/scheduler.cpp b/src/system/scheduler/scheduler.cpp index cf521f91..9c4ddce1 100644 --- a/src/system/scheduler/scheduler.cpp +++ b/src/system/scheduler/scheduler.cpp @@ -12,10 +12,15 @@ void Scheduler::enter() { co_switch(thread_active); } -void Scheduler::exit() { +void Scheduler::exit(ExitReason reason) { + exit_reason_ = reason; co_switch(thread_snes); } +Scheduler::ExitReason Scheduler::exit_reason() const { + return exit_reason_; +} + void Scheduler::init() { clock.cpu_freq = system.region() == System::NTSC ? config.cpu.ntsc_clock_rate @@ -55,6 +60,8 @@ Scheduler::Scheduler() { thread_ppu = 0; thread_dsp = 0; thread_active = 0; + + exit_reason_ = UnknownEvent; } #endif diff --git a/src/system/scheduler/scheduler.hpp b/src/system/scheduler/scheduler.hpp index 950e4dcf..ac7b8dfe 100644 --- a/src/system/scheduler/scheduler.hpp +++ b/src/system/scheduler/scheduler.hpp @@ -125,11 +125,18 @@ public: clock.smpdsp += clocks; } + enum ExitReason { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent }; + void enter(); - void exit(); + void exit(ExitReason); + ExitReason exit_reason() const; + void init(); Scheduler(); + +private: + ExitReason exit_reason_; }; extern Scheduler scheduler; diff --git a/src/system/serialization.cpp b/src/system/serialization.cpp index a439b298..8423664f 100644 --- a/src/system/serialization.cpp +++ b/src/system/serialization.cpp @@ -1,11 +1,7 @@ #ifdef SYSTEM_CPP -unsigned System::serialize_size() const { - return serializer_size; -} - serializer System::serialize() { - serializer s(serializer_size); + serializer s(serialize_size); unsigned signature = 0x31545342, version = bsnesSerializerVersion, crc32 = cartridge.crc32(); char description[512]; @@ -43,8 +39,8 @@ bool System::unserialize(serializer &s) { //======== void System::serialize(serializer &s) { - s.integer(snes_region); - s.integer(snes_expansion); + s.integer((unsigned&)region); + s.integer((unsigned&)expansion); s.integer(scheduler.clock.cpu_freq); s.integer(scheduler.clock.smp_freq); @@ -66,6 +62,8 @@ void System::serialize_all(serializer &s) { if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.serialize(s); + if(cartridge.has_superfx()) superfx.serialize(s); + if(cartridge.has_sa1()) sa1.serialize(s); if(cartridge.has_srtc()) srtc.serialize(s); if(cartridge.has_sdd1()) sdd1.serialize(s); if(cartridge.has_spc7110()) spc7110.serialize(s); @@ -92,7 +90,7 @@ void System::serialize_init() { s.array(description); serialize_all(s); - serializer_size = s.size(); + serialize_size = s.size(); } #endif diff --git a/src/system/system.cpp b/src/system/system.cpp index 7cc5302a..a4ea1fab 100644 --- a/src/system/system.cpp +++ b/src/system/system.cpp @@ -21,6 +21,10 @@ void System::coprocessor_enter() { if(cartridge.has_21fx()) s21fx.enter(); while(true) { + if(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + scheduler.addclocks_cop(64 * 1024 * 1024); scheduler.sync_copcpu(); } @@ -30,39 +34,49 @@ void System::run() { scheduler.sync = Scheduler::SyncNone; scheduler.enter(); - input.update(); - video.update(); + if(scheduler.exit_reason() == Scheduler::FrameEvent) { + input.update(); + video.update(); + } } void System::runtosave() { scheduler.sync = Scheduler::SyncCpu; + runthreadtosave(); - while(true) { - scheduler.enter(); - if(scheduler.sync == Scheduler::SyncAll) break; - input.update(); - video.update(); - } + scheduler.thread_active = scheduler.thread_cop; + runthreadtosave(); scheduler.thread_active = scheduler.thread_smp; - scheduler.enter(); + runthreadtosave(); scheduler.thread_active = scheduler.thread_ppu; - scheduler.enter(); + runthreadtosave(); #if !defined(DSP_STATE_MACHINE) scheduler.thread_active = scheduler.thread_dsp; - scheduler.enter(); + runthreadtosave(); #endif } +void System::runthreadtosave() { + while(true) { + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::SynchronizeEvent) break; + if(scheduler.exit_reason() == Scheduler::FrameEvent) { + input.update(); + video.update(); + } + } +} + void System::init(Interface *interface_) { interface = interface_; assert(interface != 0); supergameboy.init(); - sa1.init(); superfx.init(); + sa1.init(); bsxbase.init(); bsxcart.init(); bsxflash.init(); @@ -89,11 +103,11 @@ void System::term() { } void System::power() { - snes_region = max(0, min(2, config.region)); - snes_expansion = max(0, min(1, config.expansion_port)); + region = max(0, min(2, config.region)); + expansion = max(0, min(1, config.expansion_port)); - if(snes_region == Autodetect) { - snes_region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL); + if(region == Autodetect) { + region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL); } audio.coprocessor_enable(false); @@ -203,21 +217,15 @@ void System::unload() { void System::scanline() { video.scanline(); - if(cpu.vcounter() == 241) scheduler.exit(); + if(cpu.vcounter() == 241) scheduler.exit(Scheduler::FrameEvent); } void System::frame() { } -System::Region System::region() const { - return (System::Region)snes_region; -} - -System::ExpansionPortDevice System::expansion() const { - return (System::ExpansionPortDevice)snes_expansion; -} - -System::System() : interface(0), snes_region(NTSC), snes_expansion(ExpansionNone) { +System::System() : interface(0) { + region = NTSC; + expansion = ExpansionNone; } }; diff --git a/src/system/system.hpp b/src/system/system.hpp index 98ec48b1..406277e7 100644 --- a/src/system/system.hpp +++ b/src/system/system.hpp @@ -7,15 +7,12 @@ #include "audio/audio.hpp" #include "input/input.hpp" -class System { +class System : property { public: - void coprocessor_enter(); - enum Region { NTSC = 0, PAL = 1 }; enum RegionAutodetect { Autodetect = 2 }; enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; - //system functions void run(); void runtosave(); @@ -28,12 +25,11 @@ public: void frame(); void scanline(); - //return *active* region / expansion port device information - //settings cached upon power-on - Region region() const; - ExpansionPortDevice expansion() const; + //return *active* system information (settings are cached upon power-on) + readonly region; + readonly expansion; + readonly serialize_size; - unsigned serialize_size() const; serializer serialize(); bool unserialize(serializer&); @@ -41,20 +37,20 @@ public: virtual ~System() {} private: - unsigned serializer_size; + Interface *interface; + void coprocessor_enter(); + void runthreadtosave(); + void serialize(serializer&); void serialize_all(serializer&); void serialize_init(); - Interface *interface; - unsigned snes_region; - unsigned snes_expansion; - friend class Cartridge; friend class Video; friend class Audio; friend class Input; friend class StateManager; + friend void threadentry_cop(); }; extern System system; diff --git a/src/ui_qt/Makefile b/src/ui_qt/Makefile index c4969846..32f38dbe 100644 --- a/src/ui_qt/Makefile +++ b/src/ui_qt/Makefile @@ -1,4 +1,4 @@ -objects := ui-main ui-base ui-debugger ui-input ui-movie ui-settings ui-state ui-tools $(objects) +objects := ui-main ui-base ui-cartridge ui-debugger ui-input ui-movie ui-settings ui-state ui-tools $(objects) objects += $(if $(call streq,$(platform),win),resource) link += $(qtlib) @@ -24,7 +24,8 @@ $(foreach f,$(moc_objects), \ obj/ui-main.o: $(ui)/main.cpp $(headers) $(wildcard $(ui)/*.cpp) $(wildcard $(ui)/platform/*.cpp) $(wildcard $(ui)/utility/*.cpp); $(qt_compile) obj/ui-base.o: $(ui)/base/base.cpp $(headers) $(wildcard $(ui)/base/*.cpp); $(qt_compile) -obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(headers) $(wildcard $(ui)/debugger/*.cpp); $(qt_compile) +obj/ui-cartridge.o: $(ui)/cartridge/cartridge.cpp $(headers) $(wildcard $(ui)/cartridge/*.cpp); $(qt_compile) +obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(headers) $(call rwildcard,$(ui)/debugger/,%.cpp); $(qt_compile) obj/ui-input.o: $(ui)/input/input.cpp $(headers) $(wildcard $(ui)/input/*.cpp); $(qt_compile) obj/ui-movie.o: $(ui)/movie/movie.cpp $(headers) $(wildcard $(ui)/movie/*.cpp); $(qt_compile) obj/ui-settings.o: $(ui)/settings/settings.cpp $(headers) $(wildcard $(ui)/settings/*.cpp); $(qt_compile) diff --git a/src/ui_qt/application/application.cpp b/src/ui_qt/application/application.cpp index 7201a6b7..4883c69c 100644 --- a/src/ui_qt/application/application.cpp +++ b/src/ui_qt/application/application.cpp @@ -77,7 +77,7 @@ int Application::main(int &argc, char **argv) { if(argc == 2) { //if valid file was specified on the command-line, attempt to load it now - utility.loadCartridgeNormal(argv[1]); + cartridge.loadNormal(argv[1]); } timer = new QTimer(this); @@ -90,7 +90,7 @@ int Application::main(int &argc, char **argv) { windowList[i]->hide(); } - utility.unloadCartridge(); + cartridge.unload(); config().save(configFilename); return 0; } @@ -141,7 +141,7 @@ void Application::run() { if(autosaveTime >= CLOCKS_PER_SEC * 60) { //auto-save RAM once per minute in case of emulator crash autosaveTime = 0; - if(config().system.autoSaveMemory == true) utility.saveMemory(); + if(config().system.autoSaveMemory == true) cartridge.saveMemory(); } if(screensaverTime >= CLOCKS_PER_SEC * 30) { diff --git a/src/ui_qt/application/qb.cpp b/src/ui_qt/application/qb.cpp index 6ce65dee..d5447656 100644 --- a/src/ui_qt/application/qb.cpp +++ b/src/ui_qt/application/qb.cpp @@ -1,3 +1,7 @@ +void QbWindow::setCloseOnEscape(bool state) { + closeOnEscape = state; +} + void QbWindow::shrink() { if(config().video.isFullscreen == false) { for(unsigned i = 0; i < 2; i++) { @@ -45,11 +49,13 @@ void QbWindow::closeEvent(QCloseEvent *event) { } void QbWindow::keyReleaseEvent(QKeyEvent *event) { - if(event->key() == Qt::Key_Escape) close(); + if((closeOnEscape == true) && (event->key() == Qt::Key_Escape)) close(); QWidget::keyReleaseEvent(event); } QbWindow::QbWindow(string &geometryString_) : geometryString(geometryString_) { + closeOnEscape = true; + //keep track of all created windows (for geometry save on exit, always-on-top control, etc) application.windowList.add(this); } diff --git a/src/ui_qt/application/qb.hpp b/src/ui_qt/application/qb.hpp index 86c01873..62f161c7 100644 --- a/src/ui_qt/application/qb.hpp +++ b/src/ui_qt/application/qb.hpp @@ -1,5 +1,6 @@ class QbWindow : public QWidget { public: + void setCloseOnEscape(bool); void shrink(); void show(); void hide(); @@ -9,6 +10,7 @@ public: private: string &geometryString; + bool closeOnEscape; }; class QbCheckAction : public QAction { diff --git a/src/ui_qt/base/diskbrowser.cpp b/src/ui_qt/base/diskbrowser.cpp index 5629adf2..f1020160 100644 --- a/src/ui_qt/base/diskbrowser.cpp +++ b/src/ui_qt/base/diskbrowser.cpp @@ -263,11 +263,15 @@ string DiskBrowser::queryImageInformation() { string text; string filename; if(currentFilename(filename) == true) { - if(striend(filename, ".sfc") || striend(filename, ".smc")) { + Cartridge::Information info; + if(cartridge.information(filename, info)) { unsigned size = file::size(filename); - text << "" << notdir(nall::basename(filename)) << ""; - text << ""; - text << ""; + text << "
ROM size:" << size * 8 / 1024 / 1024 << "mbit
"; + text << ""; + text << ""; + text << ""; + text << "" : text << "None"; text << "
Title: " << info.name << "
Region: " << info.region << "
ROM: " << info.romSize * 8 / 1024 / 1024 << "mbit
RAM: "; + info.ramSize ? text << info.ramSize * 8 / 1024 << "kbit
"; } } @@ -330,13 +334,13 @@ void DiskBrowser::loadSelected() { if(config().path.bsx == "") { loaderWindow->loadBsxCartridge("", filename); } else { - utility.loadCartridgeBsx(config().path.bsx, filename); + cartridge.loadBsx(config().path.bsx, filename); } } else if(config().path.current.filter == 2) { //"Sufami Turbo cartridges" if(config().path.st == "") { loaderWindow->loadSufamiTurboCartridge("", filename, ""); } else { - utility.loadCartridgeSufamiTurbo(config().path.st, filename, ""); + cartridge.loadSufamiTurbo(config().path.st, filename, ""); } } else if(config().path.current.filter == 3) { //"Game Boy cartridges" if(SNES::supergameboy.opened() == false) { @@ -344,10 +348,10 @@ void DiskBrowser::loadSelected() { } else if(config().path.sgb == "") { loaderWindow->loadSuperGameBoyCartridge("", filename); } else { - utility.loadCartridgeSuperGameBoy(config().path.sgb, filename); + cartridge.loadSuperGameBoy(config().path.sgb, filename); } } else { //"SNES cartridges" (0) or "All files" (4) - utility.loadCartridgeNormal(filename); + cartridge.loadNormal(filename); } } else if(browseMode == BaseCartridge) { loaderWindow->selectBaseCartridge(filename); @@ -453,13 +457,18 @@ bool DiskBrowser::currentFilename(string &filename) { filename = model->filePath(item).toUtf8().constData(); if(browseMode != Folder) { - if(model->isDir(item) == true) { - QDir directory(filename); - directory.setNameFilters(QStringList() << "*.sfc"); - QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot); - if(list.count() == 1) { - filename << "/" << list[0].toUtf8().constData(); - loadable = true; + if(model->isDir(item)) { + if(browseMode != File) { + //folders ending in ".sfc" are treated as "packages", and loaded directly + if(striend(filename, ".sfc")) { + QDir directory(filename); + directory.setNameFilters(QStringList() << "*.sfc"); + QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot); + if(list.count() == 1) { + filename << "/" << list[0].toUtf8().constData(); + loadable = true; + } + } } } else { loadable = true; @@ -469,6 +478,10 @@ bool DiskBrowser::currentFilename(string &filename) { return loadable; } +void DiskBrowser::toggleApplyPatches() { + config().file.applyPatches = applyPatch->isChecked(); +} + void DiskBrowser::toggleShowPanel() { showPanel->setChecked(!showPanel->isChecked()); config().diskBrowser.showPanel = showPanel->isChecked(); @@ -531,8 +544,7 @@ DiskBrowser::DiskBrowser() : QbWindow(config().geometry.diskBrowser) { groupLayout->addWidget(spacer); applyPatch = new QCheckBox("Apply UPS patch"); - applyPatch->setChecked(true); - applyPatch->setEnabled(false); + applyPatch->setChecked(config().file.applyPatches); groupLayout->addWidget(applyPatch); controlLayout = new QHBoxLayout; @@ -584,5 +596,6 @@ DiskBrowser::DiskBrowser() : QbWindow(config().geometry.diskBrowser) { connect(ok, SIGNAL(released()), this, SLOT(loadSelected())); connect(cancel, SIGNAL(released()), this, SLOT(close())); + connect(applyPatch, SIGNAL(stateChanged(int)), this, SLOT(toggleApplyPatches())); connect(showPanel, SIGNAL(triggered()), this, SLOT(toggleShowPanel())); } diff --git a/src/ui_qt/base/diskbrowser.moc.hpp b/src/ui_qt/base/diskbrowser.moc.hpp index adb97625..a5dbefb4 100644 --- a/src/ui_qt/base/diskbrowser.moc.hpp +++ b/src/ui_qt/base/diskbrowser.moc.hpp @@ -93,6 +93,7 @@ public slots: void changeItem(const QModelIndex&); void loadSelected(); + void toggleApplyPatches(); void toggleShowPanel(); private: diff --git a/src/ui_qt/base/loader.cpp b/src/ui_qt/base/loader.cpp index 38f1d59f..7fdcdd03 100644 --- a/src/ui_qt/base/loader.cpp +++ b/src/ui_qt/base/loader.cpp @@ -208,22 +208,22 @@ void LoaderWindow::onLoad() { switch(mode) { case SNES::Cartridge::ModeBsxSlotted: { - utility.loadCartridgeBsxSlotted(base, slot1); + cartridge.loadBsxSlotted(base, slot1); } break; case SNES::Cartridge::ModeBsx: { config().path.bsx = base; - utility.loadCartridgeBsx(base, slot1); + cartridge.loadBsx(base, slot1); } break; case SNES::Cartridge::ModeSufamiTurbo: { config().path.st = base; - utility.loadCartridgeSufamiTurbo(base, slot1, slot2); + cartridge.loadSufamiTurbo(base, slot1, slot2); } break; case SNES::Cartridge::ModeSuperGameBoy: { config().path.sgb = base; - utility.loadCartridgeSuperGameBoy(base, slot1); + cartridge.loadSuperGameBoy(base, slot1); } break; } } diff --git a/src/ui_qt/base/main.cpp b/src/ui_qt/base/main.cpp index fbddda59..7864b1d2 100644 --- a/src/ui_qt/base/main.cpp +++ b/src/ui_qt/base/main.cpp @@ -4,6 +4,7 @@ MainWindow *mainWindow; MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) { setObjectName("main-window"); setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion); + setCloseOnEscape(false); //menu bar #if defined(PLATFORM_OSX) @@ -165,15 +166,15 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) { tools->addSeparator(); + tools_dialog = tools->addAction("&Tools Dialog ..."); + tools_dialog->setIcon(QIcon(":/16x16/preferences-desktop.png")); + tools_debugger = tools->addAction("&Debugger ..."); tools_debugger->setIcon(QIcon(":/16x16/utilities-terminal.png")); #if !defined(DEBUGGER) tools_debugger->setVisible(false); #endif - tools_dialog = tools->addAction("&Tools Dialog ..."); - tools_dialog->setIcon(QIcon(":/16x16/preferences-desktop.png")); - help = menuBar->addMenu("&Help"); help_documentation = help->addAction("&Documentation ..."); @@ -344,7 +345,7 @@ void MainWindow::syncUi() { settings_emulationSpeed_syncAudio->setChecked(config().audio.synchronize); //movies contian save states to synchronize playback to recorded input - tools_movies->setEnabled(SNES::cartridge.loaded() && utility.saveStatesSupported()); + tools_movies->setEnabled(SNES::cartridge.loaded() && cartridge.saveStatesSupported()); if(tools_movies->isEnabled()) { tools_movies_play->setEnabled(movie.state == Movie::Inactive); tools_movies_stop->setEnabled(movie.state != Movie::Inactive); @@ -597,7 +598,7 @@ void CanvasObject::dragEnterEvent(QDragEnterEvent *event) { void CanvasObject::dropEvent(QDropEvent *event) { if(event->mimeData()->hasUrls()) { QList list = event->mimeData()->urls(); - if(list.count() == 1) utility.loadCartridgeNormal(list.at(0).toLocalFile().toUtf8().constData()); + if(list.count() == 1) cartridge.loadNormal(list.at(0).toLocalFile().toUtf8().constData()); } } diff --git a/src/ui_qt/base/main.moc.hpp b/src/ui_qt/base/main.moc.hpp index 406f8962..2a9441af 100644 --- a/src/ui_qt/base/main.moc.hpp +++ b/src/ui_qt/base/main.moc.hpp @@ -77,8 +77,8 @@ public: QAction *tools_movies_recordFromPowerOn; QAction *tools_movies_recordFromHere; QAction *tools_captureScreenshot; - QAction *tools_debugger; QAction *tools_dialog; + QAction *tools_debugger; QMenu *help; QAction *help_documentation; QAction *help_license; diff --git a/src/ui_qt/cartridge/cartridge.cpp b/src/ui_qt/cartridge/cartridge.cpp new file mode 100644 index 00000000..227effce --- /dev/null +++ b/src/ui_qt/cartridge/cartridge.cpp @@ -0,0 +1,378 @@ +#include "../ui-base.hpp" +Cartridge cartridge; + +//================ +//public functions +//================ + +bool Cartridge::information(const char *filename, Cartridge::Information &info) { + if(extension(filename) != "sfc") return false; //do not parse compressed images + + file fp; + if(fp.open(filename, file::mode_read) == false) return false; + + unsigned offset = 0; + if((fp.size() & 0x7fff) == 512) offset = 512; + + uint16_t complement, checksum; + + fp.seek(0x7fdc + offset); + complement = fp.readl(2); + checksum = fp.readl(2); + + unsigned header = offset + (complement + checksum == 65535 ? 0x7fb0 : 0xffb0); + + fp.seek(header + 0x10); + char name[22]; + fp.read((uint8_t*)name, 21); + name[21] = 0; + info.name = decodeShiftJIS(name); + + fp.seek(header + 0x29); + uint8_t region = fp.read(); + info.region = (region <= 1 || region >= 13) ? "NTSC" : "PAL"; + + info.romSize = fp.size() & ~0x7fff; + + fp.seek(header + 0x28); + info.ramSize = fp.readl(1); + if(info.ramSize) info.ramSize = 1024 << (info.ramSize & 7); + + fp.close(); + return true; +} + +bool Cartridge::saveStatesSupported() { + if(SNES::cartridge.mode() == SNES::Cartridge::ModeBsx) return false; + + if(SNES::cartridge.has_dsp3()) return false; + if(SNES::cartridge.has_dsp4()) return false; + if(SNES::cartridge.has_st011()) return false; + if(SNES::cartridge.has_st018()) return false; + + return true; +} + +bool Cartridge::loadNormal(const char *base) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + SNES::s21fx.base(dir(baseName)); + SNES::cartridge.load(SNES::Cartridge::ModeNormal); + + loadMemory(baseName, ".srm", SNES::memory::cartram); + loadMemory(baseName, ".rtc", SNES::memory::cartrtc); + + fileName = baseName; + name = notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsxSlotted(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::bsxflash); + SNES::cartridge.load(SNES::Cartridge::ModeBsxSlotted); + + loadMemory(baseName, ".srm", SNES::memory::cartram); + loadMemory(baseName, ".rtc", SNES::memory::cartrtc); + + fileName = baseName; + name = notdir(nall::basename(baseName)); + if(*slot) name << " + " << notdir(nall::basename(slotAName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsx(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::bsxflash); + SNES::cartridge.load(SNES::Cartridge::ModeBsx); + + loadMemory(baseName, ".srm", SNES::memory::bsxram ); + loadMemory(baseName, ".psr", SNES::memory::bsxpram); + + fileName = slotAName; + name = *slot + ? notdir(nall::basename(slotAName)) + : notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSufamiTurbo(const char *base, const char *slotA, const char *slotB) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slotA, SNES::memory::stArom); + loadCartridge(slotBName = slotB, SNES::memory::stBrom); + SNES::cartridge.load(SNES::Cartridge::ModeSufamiTurbo); + + loadMemory(slotAName, ".srm", SNES::memory::stAram); + loadMemory(slotBName, ".srm", SNES::memory::stBram); + + fileName = slotAName; + if(!*slotA && !*slotB) name = notdir(nall::basename(baseName)); + else if(!*slotB) name = notdir(nall::basename(slotAName)); + else if(!*slotA) name = notdir(nall::basename(slotBName)); + else name = notdir(nall::basename(slotAName)) << " + " << notdir(nall::basename(slotBName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSuperGameBoy(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::gbrom); + SNES::cartridge.load(SNES::Cartridge::ModeSuperGameBoy); + + loadMemory(slotAName, ".sav", SNES::memory::gbram); + loadMemory(slotBName, ".rtc", SNES::memory::gbrtc); + + fileName = slotAName; + name = *slot + ? notdir(nall::basename(slotAName)) + : notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +void Cartridge::saveMemory() { + if(SNES::cartridge.loaded() == false) return; + + switch(SNES::cartridge.mode()) { + case SNES::Cartridge::ModeNormal: + case SNES::Cartridge::ModeBsxSlotted: { + saveMemory(baseName, ".srm", SNES::memory::cartram); + saveMemory(baseName, ".rtc", SNES::memory::cartrtc); + } break; + + case SNES::Cartridge::ModeBsx: { + saveMemory(baseName, ".srm", SNES::memory::bsxram ); + saveMemory(baseName, ".psr", SNES::memory::bsxpram); + } break; + + case SNES::Cartridge::ModeSufamiTurbo: { + saveMemory(slotAName, ".srm", SNES::memory::stAram); + saveMemory(slotBName, ".srm", SNES::memory::stBram); + } break; + + case SNES::Cartridge::ModeSuperGameBoy: { + saveMemory(slotAName, ".sav", SNES::memory::gbram); + saveMemory(slotAName, ".rtc", SNES::memory::gbrtc); + } break; + } +} + +void Cartridge::unload() { + if(SNES::cartridge.loaded() == false) return; + utility.modifySystemState(Utility::UnloadCartridge); +} + +void Cartridge::loadCheats() { + string name; + name << filepath(nall::basename(baseName), config().path.cheat); + name << ".cht"; + cheatEditorWindow->load(name); +} + +void Cartridge::saveCheats() { + string name; + name << filepath(nall::basename(baseName), config().path.cheat); + name << ".cht"; + cheatEditorWindow->save(name); +} + +//================= +//private functions +//================= + +bool Cartridge::loadCartridge(string &filename, SNES::MappedRAM &memory) { + if(file::exists(filename) == false) return false; + + uint8_t *data; + unsigned size; + audio.clear(); + if(reader.load(filename, data, size) == false) return false; + + patchApplied = false; + string name; + name << filepath(nall::basename(filename), config().path.patch); + name << ".ups"; + + file fp; + if(config().file.applyPatches && fp.open(name, file::mode_read)) { + unsigned patchsize = fp.size(); + uint8_t *patchdata = new uint8_t[patchsize]; + fp.read(patchdata, patchsize); + fp.close(); + + uint8_t *outdata = 0; + unsigned outsize = 0; + ups patcher; + ups::result result = patcher.apply(patchdata, patchsize, data, size, outdata, outsize); + delete[] patchdata; + + bool apply = false; + if(result == ups::ok) apply = true; + if(config().file.bypass_patch_crc32) { + if(result == ups::input_crc32_invalid ) apply = true; + if(result == ups::output_crc32_invalid) apply = true; + } + + if(apply == true) { + delete[] data; + data = outdata; + size = outsize; + patchApplied = true; + } else { + delete[] outdata; + } + } + + memory.copy(data, size); + delete[] data; + return true; +} + +bool Cartridge::loadMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { + if(memory.size() == 0 || memory.size() == -1U) return false; + + string name; + name << filepath(nall::basename(filename), config().path.save); + name << extension; + + file fp; + if(fp.open(name, file::mode_read) == false) return false; + + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + + memory.copy(data, size); + delete[] data; + return true; +} + +bool Cartridge::saveMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { + if(memory.size() == 0 || memory.size() == -1U) return false; + + string name; + name << filepath(nall::basename(filename), config().path.save); + name << extension; + + file fp; + if(fp.open(name, file::mode_write) == false) return false; + + fp.write(memory.data(), memory.size()); + fp.close(); + return true; +} + +string Cartridge::decodeShiftJIS(const char *text) { + unsigned length = strlen(text), offset = 0; + string output; + + for(unsigned i = 0; i < length;) { + unsigned code = 0; + uint8_t n = text[i++]; + + if(n == 0x00) { + //string terminator + break; + } else if(n >= 0x20 && n <= 0x7f) { + //ASCII + code = n; + } else if(n >= 0xa0 && n <= 0xdf) { + //ShiftJIS half-width katakana + unsigned dakuten = 0, handakuten = 0; + + switch(n) { + case 0xa1: code = 0xe38082; break; //(period) + case 0xa2: code = 0xe3808c; break; //(open quote) + case 0xa3: code = 0xe3808d; break; //(close quote) + case 0xa4: code = 0xe38081; break; //(comma) + case 0xa5: code = 0xe383bb; break; //(separator) + case 0xa6: code = 0xe383b2; break; //wo + case 0xa7: code = 0xe382a1; break; //la + case 0xa8: code = 0xe382a3; break; //li + case 0xa9: code = 0xe382a5; break; //lu + case 0xaa: code = 0xe382a7; break; //le + case 0xab: code = 0xe382a9; break; //lo + case 0xac: code = 0xe383a3; break; //lya + case 0xad: code = 0xe383a5; break; //lyu + case 0xae: code = 0xe383a7; break; //lyo + case 0xaf: code = 0xe38383; break; //ltsu + case 0xb0: code = 0xe383bc; break; //- + case 0xb1: code = 0xe382a2; break; //a + case 0xb2: code = 0xe382a4; break; //i + case 0xb3: code = 0xe382a6; break; //u + case 0xb4: code = 0xe382a8; break; //e + case 0xb5: code = 0xe382aa; break; //o + case 0xb6: code = 0xe382ab; dakuten = 0xe382ac; break; //ka, ga + case 0xb7: code = 0xe382ad; dakuten = 0xe382ae; break; //ki, gi + case 0xb8: code = 0xe382af; dakuten = 0xe382b0; break; //ku, gu + case 0xb9: code = 0xe382b1; dakuten = 0xe382b2; break; //ke, ge + case 0xba: code = 0xe382b3; dakuten = 0xe382b4; break; //ko, go + case 0xbb: code = 0xe382b5; dakuten = 0xe382b6; break; //sa, za + case 0xbc: code = 0xe382b7; dakuten = 0xe382b8; break; //shi, zi + case 0xbd: code = 0xe382b9; dakuten = 0xe382ba; break; //su, zu + case 0xbe: code = 0xe382bb; dakuten = 0xe382bc; break; //se, ze + case 0xbf: code = 0xe382bd; dakuten = 0xe382be; break; //so, zo + case 0xc0: code = 0xe382bf; dakuten = 0xe38380; break; //ta, da + case 0xc1: code = 0xe38381; dakuten = 0xe38382; break; //chi, di + case 0xc2: code = 0xe38384; dakuten = 0xe38385; break; //tsu, du + case 0xc3: code = 0xe38386; dakuten = 0xe38387; break; //te, de + case 0xc4: code = 0xe38388; dakuten = 0xe38389; break; //to, do + case 0xc5: code = 0xe3838a; break; //na + case 0xc6: code = 0xe3838b; break; //ni + case 0xc7: code = 0xe3838c; break; //nu + case 0xc8: code = 0xe3838d; break; //ne + case 0xc9: code = 0xe3838e; break; //no + case 0xca: code = 0xe3838f; dakuten = 0xe38390; handakuten = 0xe38391; break; //ha, ba, pa + case 0xcb: code = 0xe38392; dakuten = 0xe38393; handakuten = 0xe38394; break; //hi, bi, pi + case 0xcc: code = 0xe38395; dakuten = 0xe38396; handakuten = 0xe38397; break; //fu, bu, pu + case 0xcd: code = 0xe38398; dakuten = 0xe38399; handakuten = 0xe3839a; break; //he, be, pe + case 0xce: code = 0xe3839b; dakuten = 0xe3839c; handakuten = 0xe3839d; break; //ho, bo, po + case 0xcf: code = 0xe3839e; break; //ma + case 0xd0: code = 0xe3839f; break; //mi + case 0xd1: code = 0xe383a0; break; //mu + case 0xd2: code = 0xe383a1; break; //me + case 0xd3: code = 0xe383a2; break; //mo + case 0xd4: code = 0xe383a4; break; //ya + case 0xd5: code = 0xe383a6; break; //yu + case 0xd6: code = 0xe383a8; break; //yo + case 0xd7: code = 0xe383a9; break; //ra + case 0xd8: code = 0xe383aa; break; //ri + case 0xd9: code = 0xe383ab; break; //ru + case 0xda: code = 0xe383ac; break; //re + case 0xdb: code = 0xe383ad; break; //ro + case 0xdc: code = 0xe383af; break; //wa + case 0xdd: code = 0xe383b3; break; //n + } + + if(dakuten && ((uint8_t)text[i] == 0xde)) { + code = dakuten; + i++; + } else if(handakuten && ((uint8_t)text[i] == 0xdf)) { + code = handakuten; + i++; + } + } + + if(code) { + if((uint8_t)(code >> 16)) output[offset++] = (char)(code >> 16); + if((uint8_t)(code >> 8)) output[offset++] = (char)(code >> 8); + if((uint8_t)(code >> 0)) output[offset++] = (char)(code >> 0); + } + } + + output[offset] = 0; + return output; +} diff --git a/src/ui_qt/cartridge/cartridge.hpp b/src/ui_qt/cartridge/cartridge.hpp new file mode 100644 index 00000000..12545233 --- /dev/null +++ b/src/ui_qt/cartridge/cartridge.hpp @@ -0,0 +1,38 @@ +class Cartridge { +public: + string name; //printable name + string fileName; //ideal file name for saving data to disk + string baseName; //physical cartridge file name + string slotAName; //Sufami Turbo slot A file name or BS-X slot file name + string slotBName; //Sufami Turbo slot B file name + bool patchApplied; //true if UPS patch was applied to image + + struct Information { + string name; + string region; + unsigned romSize; + unsigned ramSize; + }; + + bool information(const char*, Information&); + bool saveStatesSupported(); + + bool loadNormal(const char*); + bool loadBsxSlotted(const char*, const char*); + bool loadBsx(const char*, const char*); + bool loadSufamiTurbo(const char*, const char *, const char*); + bool loadSuperGameBoy(const char*, const char*); + void saveMemory(); + void unload(); + + void loadCheats(); + void saveCheats(); + +private: + bool loadCartridge(string&, SNES::MappedRAM&); + bool loadMemory(const char*, const char*, SNES::MappedRAM&); + bool saveMemory(const char*, const char*, SNES::MappedRAM&); + string decodeShiftJIS(const char*); +}; + +extern Cartridge cartridge; diff --git a/src/ui_qt/config.cpp b/src/ui_qt/config.cpp index c8912552..c0b09e19 100644 --- a/src/ui_qt/config.cpp +++ b/src/ui_qt/config.cpp @@ -55,6 +55,7 @@ Configuration::Configuration() { attach(diskBrowser.showPanel = true, "diskBrowser.showPanel"); attach(file.autodetect_type = false, "file.autodetectType"); + attach(file.applyPatches = true, "file.applyPatches"); attach(file.bypass_patch_crc32 = false, "file.bypassPatchCrc32"); attach(path.rom = "", "path.rom"); @@ -118,6 +119,8 @@ Configuration::Configuration() { attach(input.focusPolicy = Input::FocusPolicyIgnoreInput, "input.focusPolicy"); attach(input.allowInvalidInput = false, "input.allowInvalidInput", "Allow up+down / left+right combinations; may trigger bugs in some games"); + attach(debugger.cacheUsageToDisk = false, "debugger.cacheUsageToDisk"); + attach(geometry.mainWindow = "", "geometry.mainWindow"); attach(geometry.loaderWindow = "", "geometry.loaderWindow"); attach(geometry.htmlViewerWindow = "", "geometry.htmlViewerWindow"); @@ -126,9 +129,15 @@ Configuration::Configuration() { attach(geometry.folderCreator = "", "geometry.folderCreator"); attach(geometry.settingsWindow = "", "geometry.settingsWindow"); attach(geometry.toolsWindow = "", "geometry.toolsWindow"); + attach(geometry.debugger = "", "geometry.debugger"); attach(geometry.disassembler = "", "geometry.disassembler"); attach(geometry.breakpointEditor = "", "geometry.breakpointEditor"); attach(geometry.memoryEditor = "", "geometry.memoryEditor"); + attach(geometry.propertiesViewer = "", "geometry.propertiesViewer"); + attach(geometry.layerToggle = "", "geometry.layerToggle"); attach(geometry.vramViewer = "", "geometry.vramViewer"); + attach(geometry.oamViewer = "", "geometry.oamViewer"); + attach(geometry.cgramViewer = "", "geometry.cgramViewer"); + attach(geometry.debuggerOptions = "", "geometry.debuggerOptions"); } diff --git a/src/ui_qt/config.hpp b/src/ui_qt/config.hpp index bc0e184c..41edd9fa 100644 --- a/src/ui_qt/config.hpp +++ b/src/ui_qt/config.hpp @@ -15,6 +15,7 @@ public: struct File { bool autodetect_type; + bool applyPatches; bool bypass_patch_crc32; } file; @@ -64,6 +65,10 @@ public: bool allowInvalidInput; } input; + struct Debugger { + bool cacheUsageToDisk; + } debugger; + struct Geometry { string mainWindow; string loaderWindow; @@ -73,11 +78,17 @@ public: string folderCreator; string settingsWindow; string toolsWindow; + string debugger; string disassembler; string breakpointEditor; string memoryEditor; + string propertiesViewer; + string layerToggle; string vramViewer; + string oamViewer; + string cgramViewer; + string debuggerOptions; } geometry; bool load(const char *filename); diff --git a/src/ui_qt/debugger/debugger.cpp b/src/ui_qt/debugger/debugger.cpp index ca4e1c88..29890d40 100644 --- a/src/ui_qt/debugger/debugger.cpp +++ b/src/ui_qt/debugger/debugger.cpp @@ -6,12 +6,20 @@ Debugger *debugger; #include "hexeditor.cpp" -#include "disassembler.cpp" -#include "breakpoint.cpp" -#include "memory.cpp" -#include "vramviewer.cpp" #include "tracer.cpp" +#include "tools/disassembler.cpp" +#include "tools/breakpoint.cpp" +#include "tools/memory.cpp" +#include "tools/properties.cpp" + +#include "ppu/layer-toggle.cpp" +#include "ppu/vram-viewer.cpp" +#include "ppu/oam-viewer.cpp" +#include "ppu/cgram-viewer.cpp" + +#include "misc/debugger-options.cpp" + Debugger::Debugger() : QbWindow(config().geometry.debugger) { setObjectName("debugger"); setWindowTitle("Debugger"); @@ -24,14 +32,21 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) { menu = new QMenuBar; layout->setMenuBar(menu); - tools = menu->addMenu("Tools"); - tools_disassembler = tools->addAction("Disassembler ..."); - tools_breakpoint = tools->addAction("Breakpoint Editor ..."); - tools_memory = tools->addAction("Memory Editor ..."); - tools_vramViewer = tools->addAction("Video RAM Viewer ..."); + menu_tools = menu->addMenu("Tools"); + menu_tools_disassembler = menu_tools->addAction("Disassembler ..."); + menu_tools_breakpoint = menu_tools->addAction("Breakpoint Editor ..."); + menu_tools_memory = menu_tools->addAction("Memory Editor ..."); + menu_tools_propertiesViewer = menu_tools->addAction("Properties Viewer ..."); - miscOptions = menu->addMenu("Misc"); - miscOptions_clear = miscOptions->addAction("Clear Console"); + menu_ppu = menu->addMenu("S-PPU"); + menu_ppu_layerToggle = menu_ppu->addAction("Layer Toggle ..."); + menu_ppu_vramViewer = menu_ppu->addAction("Video RAM Viewer ..."); + menu_ppu_oamViewer = menu_ppu->addAction("Sprite Viewer ..."); + menu_ppu_cgramViewer = menu_ppu->addAction("Palette Viewer ..."); + + menu_misc = menu->addMenu("Misc"); + menu_misc_clear = menu_misc->addAction("Clear Console"); + menu_misc_options = menu_misc->addAction("Options ..."); console = new QTextEdit; console->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -77,17 +92,29 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) { spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); controlLayout->addWidget(spacer); + tracer = new Tracer; disassembler = new Disassembler; breakpointEditor = new BreakpointEditor; memoryEditor = new MemoryEditor; + propertiesViewer = new PropertiesViewer; + layerToggle = new LayerToggle; vramViewer = new VramViewer; - tracer = new Tracer; + oamViewer = new OamViewer; + cgramViewer = new CgramViewer; + debuggerOptions = new DebuggerOptions; - connect(tools_disassembler, SIGNAL(triggered()), this, SLOT(showDisassembler())); - connect(tools_breakpoint, SIGNAL(triggered()), this, SLOT(showBreakpointEditor())); - connect(tools_memory, SIGNAL(triggered()), this, SLOT(showMemoryEditor())); - connect(tools_vramViewer, SIGNAL(triggered()), this, SLOT(showVramViewer())); - connect(miscOptions_clear, SIGNAL(triggered()), this, SLOT(clear())); + connect(menu_tools_disassembler, SIGNAL(triggered()), disassembler, SLOT(show())); + connect(menu_tools_breakpoint, SIGNAL(triggered()), breakpointEditor, SLOT(show())); + connect(menu_tools_memory, SIGNAL(triggered()), memoryEditor, SLOT(show())); + connect(menu_tools_propertiesViewer, SIGNAL(triggered()), propertiesViewer, SLOT(show())); + + connect(menu_ppu_layerToggle, SIGNAL(triggered()), layerToggle, SLOT(show())); + connect(menu_ppu_vramViewer, SIGNAL(triggered()), vramViewer, SLOT(show())); + connect(menu_ppu_oamViewer, SIGNAL(triggered()), oamViewer, SLOT(show())); + connect(menu_ppu_cgramViewer, SIGNAL(triggered()), cgramViewer, SLOT(show())); + + connect(menu_misc_clear, SIGNAL(triggered()), this, SLOT(clear())); + connect(menu_misc_options, SIGNAL(triggered()), debuggerOptions, SLOT(show())); connect(runBreak, SIGNAL(released()), this, SLOT(toggleRunStatus())); connect(stepInstruction, SIGNAL(released()), this, SLOT(stepAction())); @@ -103,11 +130,12 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) { } void Debugger::modifySystemState(unsigned state) { - string usagefile = string() << dir(utility.cartridge.baseName) << "usage.bin"; + string usagefile = filepath(nall::basename(cartridge.fileName), config().path.data); + usagefile << "-usage.bin"; file fp; if(state == Utility::LoadCartridge) { - if(fp.open(usagefile, file::mode_read)) { + if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_read)) { fp.read(SNES::cpu.usage, 1 << 24); fp.read(SNES::smp.usage, 1 << 16); fp.close(); @@ -118,7 +146,7 @@ void Debugger::modifySystemState(unsigned state) { } if(state == Utility::UnloadCartridge) { - if(fp.open(usagefile, file::mode_write)) { + if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_write)) { fp.write(SNES::cpu.usage, 1 << 24); fp.write(SNES::smp.usage, 1 << 16); fp.close(); @@ -144,23 +172,6 @@ void Debugger::clear() { console->setHtml(""); } -void Debugger::showDisassembler() { - disassembler->show(); -} - -void Debugger::showBreakpointEditor() { - breakpointEditor->show(); -} - -void Debugger::showMemoryEditor() { - memoryEditor->show(); -} - -void Debugger::showVramViewer() { - vramViewer->show(); - vramViewer->refresh(); -} - void Debugger::toggleRunStatus() { application.debug = !application.debug; if(!application.debug) application.debugrun = false; @@ -214,15 +225,24 @@ void Debugger::event() { disassembler->refresh(Disassembler::SMP, SNES::smp.regs.pc); } break; } + + autoUpdate(); } //called once every time a video frame is rendered, used to update "auto refresh" tool windows void Debugger::frameTick() { if(++frameCounter >= (SNES::system.region() == SNES::System::NTSC ? 60 : 50)) { frameCounter = 0; - memoryEditor->autoUpdate(); - vramViewer->autoUpdate(); + autoUpdate(); } } +void Debugger::autoUpdate() { + memoryEditor->autoUpdate(); + propertiesViewer->autoUpdate(); + vramViewer->autoUpdate(); + oamViewer->autoUpdate(); + cgramViewer->autoUpdate(); +} + #endif diff --git a/src/ui_qt/debugger/debugger.moc.hpp b/src/ui_qt/debugger/debugger.moc.hpp index f7ea2a44..448dce23 100644 --- a/src/ui_qt/debugger/debugger.moc.hpp +++ b/src/ui_qt/debugger/debugger.moc.hpp @@ -3,13 +3,19 @@ class Debugger : public QbWindow { public: QMenuBar *menu; - QMenu *tools; - QAction *tools_disassembler; - QAction *tools_breakpoint; - QAction *tools_memory; - QAction *tools_vramViewer; - QMenu *miscOptions; - QAction *miscOptions_clear; + QMenu *menu_tools; + QAction *menu_tools_disassembler; + QAction *menu_tools_breakpoint; + QAction *menu_tools_memory; + QAction *menu_tools_propertiesViewer; + QMenu *menu_ppu; + QAction *menu_ppu_layerToggle; + QAction *menu_ppu_vramViewer; + QAction *menu_ppu_oamViewer; + QAction *menu_ppu_cgramViewer; + QMenu *menu_misc; + QAction *menu_misc_clear; + QAction *menu_misc_options; QHBoxLayout *layout; QTextEdit *console; @@ -28,13 +34,10 @@ public: void echo(const char *message); void event(); void frameTick(); + void autoUpdate(); Debugger(); public slots: - void showDisassembler(); - void showBreakpointEditor(); - void showMemoryEditor(); - void showVramViewer(); void clear(); void synchronize(); diff --git a/src/ui_qt/debugger/disassembler.moc.hpp b/src/ui_qt/debugger/disassembler.moc.hpp deleted file mode 100644 index 72f52242..00000000 --- a/src/ui_qt/debugger/disassembler.moc.hpp +++ /dev/null @@ -1,18 +0,0 @@ -class Disassembler : public QbWindow { - Q_OBJECT - -public: - enum Source { CPU, SMP }; - - QHBoxLayout *layout; - QTextEdit *view; - QVBoxLayout *controlLayout; - QLabel *sourceLabel; - QRadioButton *sourceCPU; - QRadioButton *sourceSMP; - - void refresh(Source, unsigned); - Disassembler(); -}; - -extern Disassembler *disassembler; diff --git a/src/ui_qt/debugger/hexeditor.cpp b/src/ui_qt/debugger/hexeditor.cpp index 052b022e..fe65824e 100644 --- a/src/ui_qt/debugger/hexeditor.cpp +++ b/src/ui_qt/debugger/hexeditor.cpp @@ -51,8 +51,10 @@ void HexEditor::keyPressEvent(QKeyEvent *event) { } void HexEditor::setOffset(unsigned newOffset) { + slotLock = true; hexOffset = newOffset; scrollbar->setSliderPosition(hexOffset / 16); + slotLock = false; } void HexEditor::setSize(unsigned newSize) { @@ -84,12 +86,16 @@ void HexEditor::update() { } void HexEditor::sliderMoved() { + if(slotLock) return; unsigned offset = scrollbar->sliderPosition(); hexOffset = offset * 16; update(); } HexEditor::HexEditor() { + hexOffset = 0; + hexSize = 0; + QFont font(Style::Monospace); setFont(font); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -109,5 +115,6 @@ HexEditor::HexEditor() { scrollbar->setPageStep(16); layout->addWidget(scrollbar); + slotLock = false; connect(scrollbar, SIGNAL(actionTriggered(int)), this, SLOT(sliderMoved())); } diff --git a/src/ui_qt/debugger/hexeditor.moc.hpp b/src/ui_qt/debugger/hexeditor.moc.hpp index 914e22db..1bc4b69c 100644 --- a/src/ui_qt/debugger/hexeditor.moc.hpp +++ b/src/ui_qt/debugger/hexeditor.moc.hpp @@ -17,6 +17,9 @@ public: void update(); HexEditor(); -public slots: +private slots: void sliderMoved(); + +private: + bool slotLock; }; diff --git a/src/ui_qt/debugger/misc/debugger-options.cpp b/src/ui_qt/debugger/misc/debugger-options.cpp new file mode 100644 index 00000000..9ee43f34 --- /dev/null +++ b/src/ui_qt/debugger/misc/debugger-options.cpp @@ -0,0 +1,27 @@ +#include "debugger-options.moc" +DebuggerOptions *debuggerOptions; + +DebuggerOptions::DebuggerOptions() : QbWindow(config().geometry.debuggerOptions) { + setObjectName("debugger-options"); + setWindowTitle("Debugger Options"); + + layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + cacheUsageBox = new QCheckBox("Cache memory usage table to disk"); + layout->addWidget(cacheUsageBox); + + synchronize(); + connect(cacheUsageBox, SIGNAL(stateChanged(int)), this, SLOT(toggleCacheUsage())); +} + +void DebuggerOptions::synchronize() { + cacheUsageBox->setChecked(config().debugger.cacheUsageToDisk); +} + +void DebuggerOptions::toggleCacheUsage() { + config().debugger.cacheUsageToDisk = cacheUsageBox->isChecked(); +} diff --git a/src/ui_qt/debugger/misc/debugger-options.moc.hpp b/src/ui_qt/debugger/misc/debugger-options.moc.hpp new file mode 100644 index 00000000..0efa8a72 --- /dev/null +++ b/src/ui_qt/debugger/misc/debugger-options.moc.hpp @@ -0,0 +1,15 @@ +class DebuggerOptions : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QCheckBox *cacheUsageBox; + + void synchronize(); + DebuggerOptions(); + +public slots: + void toggleCacheUsage(); +}; + +extern DebuggerOptions *debuggerOptions; diff --git a/src/ui_qt/debugger/ppu/cgram-viewer.cpp b/src/ui_qt/debugger/ppu/cgram-viewer.cpp new file mode 100644 index 00000000..869ef85f --- /dev/null +++ b/src/ui_qt/debugger/ppu/cgram-viewer.cpp @@ -0,0 +1,124 @@ +#include "cgram-viewer.moc" +CgramViewer *cgramViewer; + +void CgramViewer::show() { + QbWindow::show(); + refresh(); +} + +void CgramViewer::refresh() { + if(SNES::cartridge.loaded() == false) { + canvas->image->fill(0x000000); + colorInfo->setText(""); + canvas->update(); + return; + } + + uint32_t *buffer = (uint32_t*)canvas->image->bits(); + for(unsigned i = 0; i < 256; i++) { + unsigned x = i % 16; + unsigned y = i / 16; + + uint16_t color = SNES::memory::cgram[i * 2 + 0]; + color |= SNES::memory::cgram[i * 2 + 1] << 8; + + uint8_t r = (color >> 0) & 31; + uint8_t g = (color >> 5) & 31; + uint8_t b = (color >> 10) & 31; + + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + + uint32_t output = (r << 16) | (g << 8) | (b << 0); + + for(unsigned py = 0; py < 16; py++) { + for(unsigned px = 0; px < 16; px++) { + buffer[(y * 16 + py) * (16 * 16) + (x * 16 + px)] = output; + } + } + + if(i != currentSelection) continue; + + //draw a dotted box black-and-white around the selected color + for(unsigned py = 0; py < 16; py++) { + for(unsigned px = 0; px < 16; px++) { + if(py == 0 || py == 15 || px == 0 || px == 15) { + uint32_t color = ((px + py) & 2) ? 0xffffff : 0x000000; + buffer[(y * 16 + py) * (16 * 16) + (x * 16 + px)] = color; + } + } + } + + string text; + char temp[256]; + text << ""; + text << ""; + sprintf(temp, "%.4x", color); + text << ""; + text << ""; + text << ""; + text << ""; + text << "
Index:" << currentSelection << "
Value:0x" << temp << "
Red:" << (unsigned)(r >> 3) << "
Green:" << (unsigned)(g >> 3) << "
Blue:" << (unsigned)(b >> 3) << "
"; + colorInfo->setText(text); + } + + canvas->update(); +} + +void CgramViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +void CgramViewer::setSelection(unsigned index) { + currentSelection = index; + refresh(); +} + +CgramViewer::CgramViewer() : QbWindow(config().geometry.cgramViewer) { + currentSelection = 0; + + setObjectName("cgram-viewer"); + setWindowTitle("Palette Viewer"); + + layout = new QHBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + canvas = new Canvas; + canvas->setFixedSize(16 * 16, 16 * 16); + layout->addWidget(canvas); + + controlLayout = new QVBoxLayout; + controlLayout->setAlignment(Qt::AlignTop); + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + controlLayout->addSpacing(Style::WidgetSpacing); + + colorInfo = new QLabel; + controlLayout->addWidget(colorInfo); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} + +void CgramViewer::Canvas::mousePressEvent(QMouseEvent *event) { + cgramViewer->setSelection((event->y() / 16) * 16 + (event->x() / 16)); +} + +void CgramViewer::Canvas::paintEvent(QPaintEvent*) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} + +CgramViewer::Canvas::Canvas() { + image = new QImage(16 * 16, 16 * 16, QImage::Format_RGB32); + image->fill(0x000000); +} diff --git a/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp b/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp new file mode 100644 index 00000000..34b59d1e --- /dev/null +++ b/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp @@ -0,0 +1,29 @@ +class CgramViewer : public QbWindow { + Q_OBJECT + +public: + QHBoxLayout *layout; + struct Canvas : public QWidget { + QImage *image; + void mousePressEvent(QMouseEvent*); + void paintEvent(QPaintEvent*); + Canvas(); + } *canvas; + QVBoxLayout *controlLayout; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + QLabel *colorInfo; + + void setSelection(unsigned); + void autoUpdate(); + CgramViewer(); + +public slots: + void show(); + void refresh(); + +private: + unsigned currentSelection; +}; + +extern CgramViewer *cgramViewer; diff --git a/src/ui_qt/debugger/ppu/layer-toggle.cpp b/src/ui_qt/debugger/ppu/layer-toggle.cpp new file mode 100644 index 00000000..462ac3bc --- /dev/null +++ b/src/ui_qt/debugger/ppu/layer-toggle.cpp @@ -0,0 +1,105 @@ +#include "layer-toggle.moc" +LayerToggle *layerToggle; + +LayerToggle::LayerToggle() : QbWindow(config().geometry.layerToggle) { + setObjectName("layer-toggle"); + setWindowTitle("S-PPU Layer Toggle"); + + layout = new QGridLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setHorizontalSpacing(Style::WidgetSpacing); + layout->setVerticalSpacing(0); + setLayout(layout); + + bg1Label = new QLabel("BG1:"); + layout->addWidget(bg1Label, 0, 0); + + bg1pri0 = new QCheckBox("Priority 0"); + bg1pri0->setChecked(true); + layout->addWidget(bg1pri0, 0, 1); + + bg1pri1 = new QCheckBox("Priority 1"); + bg1pri1->setChecked(true); + layout->addWidget(bg1pri1, 0, 2); + + bg2Label = new QLabel("BG2:"); + layout->addWidget(bg2Label, 1, 0); + + bg2pri0 = new QCheckBox("Priority 0"); + bg2pri0->setChecked(true); + layout->addWidget(bg2pri0, 1, 1); + + bg2pri1 = new QCheckBox("Priority 1"); + bg2pri1->setChecked(true); + layout->addWidget(bg2pri1, 1, 2); + + bg3Label = new QLabel("BG3:"); + layout->addWidget(bg3Label, 2, 0); + + bg3pri0 = new QCheckBox("Priority 0"); + bg3pri0->setChecked(true); + layout->addWidget(bg3pri0, 2, 1); + + bg3pri1 = new QCheckBox("Priority 1"); + bg3pri1->setChecked(true); + layout->addWidget(bg3pri1, 2, 2); + + bg4Label = new QLabel("BG4:"); + layout->addWidget(bg4Label, 3, 0); + + bg4pri0 = new QCheckBox("Priority 0"); + bg4pri0->setChecked(true); + layout->addWidget(bg4pri0, 3, 1); + + bg4pri1 = new QCheckBox("Priority 1"); + bg4pri1->setChecked(true); + layout->addWidget(bg4pri1, 3, 2); + + oamLabel = new QLabel("OAM:"); + layout->addWidget(oamLabel, 4, 0); + + oampri0 = new QCheckBox("Priority 0"); + oampri0->setChecked(true); + layout->addWidget(oampri0, 4, 1); + + oampri1 = new QCheckBox("Priority 1"); + oampri1->setChecked(true); + layout->addWidget(oampri1, 4, 2); + + oampri2 = new QCheckBox("Priority 2"); + oampri2->setChecked(true); + layout->addWidget(oampri2, 4, 3); + + oampri3 = new QCheckBox("Priority 3"); + oampri3->setChecked(true); + layout->addWidget(oampri3, 4, 4); + + connect(bg1pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg1pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg2pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg2pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg3pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg3pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg4pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg4pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri2, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri3, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); +} + +void LayerToggle::toggleLayer() { + if(sender() == bg1pri0) SNES::ppu.bg1_enabled[0] = bg1pri0->isChecked(); + if(sender() == bg1pri1) SNES::ppu.bg1_enabled[1] = bg1pri1->isChecked(); + if(sender() == bg2pri0) SNES::ppu.bg2_enabled[0] = bg2pri0->isChecked(); + if(sender() == bg2pri1) SNES::ppu.bg2_enabled[1] = bg2pri1->isChecked(); + if(sender() == bg3pri0) SNES::ppu.bg3_enabled[0] = bg3pri0->isChecked(); + if(sender() == bg3pri1) SNES::ppu.bg3_enabled[1] = bg3pri1->isChecked(); + if(sender() == bg4pri0) SNES::ppu.bg4_enabled[0] = bg4pri0->isChecked(); + if(sender() == bg4pri1) SNES::ppu.bg4_enabled[1] = bg4pri1->isChecked(); + if(sender() == oampri0) SNES::ppu.oam_enabled[0] = oampri0->isChecked(); + if(sender() == oampri1) SNES::ppu.oam_enabled[1] = oampri1->isChecked(); + if(sender() == oampri2) SNES::ppu.oam_enabled[2] = oampri2->isChecked(); + if(sender() == oampri3) SNES::ppu.oam_enabled[3] = oampri3->isChecked(); +} diff --git a/src/ui_qt/tools/layertoggle.moc.hpp b/src/ui_qt/debugger/ppu/layer-toggle.moc.hpp similarity index 64% rename from src/ui_qt/tools/layertoggle.moc.hpp rename to src/ui_qt/debugger/ppu/layer-toggle.moc.hpp index d1b2606e..ff3c50a7 100644 --- a/src/ui_qt/tools/layertoggle.moc.hpp +++ b/src/ui_qt/debugger/ppu/layer-toggle.moc.hpp @@ -1,10 +1,8 @@ -class LayerToggleWindow : public QWidget { +class LayerToggle : public QbWindow { Q_OBJECT public: - QVBoxLayout *layout; - QLabel *title; - QGridLayout *gridLayout; + QGridLayout *layout; QLabel *bg1Label; QCheckBox *bg1pri0; QCheckBox *bg1pri1; @@ -22,12 +20,11 @@ public: QCheckBox *oampri1; QCheckBox *oampri2; QCheckBox *oampri3; - QWidget *spacer; - LayerToggleWindow(); + LayerToggle(); public slots: - void stateChanged(); + void toggleLayer(); }; -extern LayerToggleWindow *layerToggleWindow; +extern LayerToggle *layerToggle; diff --git a/src/ui_qt/debugger/ppu/oam-viewer.cpp b/src/ui_qt/debugger/ppu/oam-viewer.cpp new file mode 100644 index 00000000..9459c6aa --- /dev/null +++ b/src/ui_qt/debugger/ppu/oam-viewer.cpp @@ -0,0 +1,124 @@ +#include "oam-viewer.moc" +OamViewer *oamViewer; + +void OamViewer::show() { + QbWindow::show(); + refresh(); +} + +void OamViewer::refresh() { + canvas->image->fill(0x000000); + + QList items = list->findItems("", Qt::MatchContains); + for(unsigned v = 0; v < items.count(); v++) { + QTreeWidgetItem *item = items[v]; + unsigned i = item->data(0, Qt::UserRole).toUInt(); + + uint8_t d0 = SNES::memory::oam[(i << 2) + 0]; + uint8_t d1 = SNES::memory::oam[(i << 2) + 1]; + uint8_t d2 = SNES::memory::oam[(i << 2) + 2]; + uint8_t d3 = SNES::memory::oam[(i << 2) + 3]; + uint8_t d4 = SNES::memory::oam[512 + (i >> 2)]; + bool x = d4 & (1 << ((i & 3) << 1)); + bool size = d4 & (2 << ((i & 3) << 1)); + + unsigned width, height; + switch(SNES::ppu.oam_base_size()) { default: + case 0: width = !size ? 8 : 16; height = !size ? 8 : 16; break; + case 1: width = !size ? 8 : 32; height = !size ? 8 : 32; break; + case 2: width = !size ? 8 : 64; height = !size ? 8 : 64; break; + case 3: width = !size ? 16 : 32; height = !size ? 16 : 32; break; + case 4: width = !size ? 16 : 64; height = !size ? 16 : 64; break; + case 5: width = !size ? 32 : 64; height = !size ? 32 : 64; break; + case 6: width = !size ? 16 : 32; height = !size ? 32 : 64; break; + case 7: width = !size ? 16 : 32; height = !size ? 32 : 32; break; + } + + signed xpos = (x << 8) + d0; + if(xpos > 256) xpos = sclip<9>(xpos); + unsigned ypos = d1; + unsigned character = d2; + unsigned priority = (d3 >> 4) & 3; + unsigned palette = (d3 >> 1) & 7; + string flags; + if(d3 & 0x80) flags << "V"; + if(d3 & 0x40) flags << "H"; + if(d3 & 0x01) flags << "N"; + + item->setText(1, string() << width << "x" << height); + item->setText(2, string() << xpos); + item->setText(3, string() << ypos); + item->setText(4, string() << character); + item->setText(5, string() << priority); + item->setText(6, string() << palette); + item->setText(7, flags); + } + + for(unsigned i = 0; i <= 7; i++) list->resizeColumnToContents(i); + canvas->update(); +} + +void OamViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +OamViewer::OamViewer() : QbWindow(config().geometry.oamViewer) { + setObjectName("oam-viewer"); + setWindowTitle("Sprite Viewer"); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignLeft); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(8); + list->setHeaderLabels(QStringList() << "#" << "Size" << "X" << "Y" << "Char" << "Pri" << "Pal" << "Flags"); + list->setAllColumnsShowFocus(true); + list->setAlternatingRowColors(true); + list->setRootIsDecorated(false); + list->setSortingEnabled(false); + layout->addWidget(list); + + for(unsigned i = 0; i < 128; i++) { + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(i)); + item->setTextAlignment(0, Qt::AlignHCenter); + item->setTextAlignment(1, Qt::AlignHCenter); + item->setTextAlignment(2, Qt::AlignRight); + item->setTextAlignment(3, Qt::AlignRight); + item->setTextAlignment(4, Qt::AlignRight); + item->setTextAlignment(5, Qt::AlignRight); + item->setTextAlignment(6, Qt::AlignRight); + item->setTextAlignment(7, Qt::AlignLeft); + item->setText(0, string() << i); + } + + controlLayout = new QVBoxLayout; + controlLayout->setAlignment(Qt::AlignTop); + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + canvas = new Canvas; + canvas->setFixedSize(128, 128); + controlLayout->addWidget(canvas); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} + +void OamViewer::Canvas::paintEvent(QPaintEvent*) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} + +OamViewer::Canvas::Canvas() { + image = new QImage(128, 128, QImage::Format_RGB32); + image->fill(0x000000); +} diff --git a/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp b/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp new file mode 100644 index 00000000..5d17865c --- /dev/null +++ b/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp @@ -0,0 +1,24 @@ +class OamViewer : public QbWindow { + Q_OBJECT + +public: + QHBoxLayout *layout; + QTreeWidget *list; + QVBoxLayout *controlLayout; + struct Canvas : public QWidget { + QImage *image; + void paintEvent(QPaintEvent*); + Canvas(); + } *canvas; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + + void autoUpdate(); + OamViewer(); + +public slots: + void show(); + void refresh(); +}; + +extern OamViewer *oamViewer; diff --git a/src/ui_qt/debugger/vramviewer.cpp b/src/ui_qt/debugger/ppu/vram-viewer.cpp similarity index 98% rename from src/ui_qt/debugger/vramviewer.cpp rename to src/ui_qt/debugger/ppu/vram-viewer.cpp index 81ee2b85..94126185 100644 --- a/src/ui_qt/debugger/vramviewer.cpp +++ b/src/ui_qt/debugger/ppu/vram-viewer.cpp @@ -1,4 +1,4 @@ -#include "vramviewer.moc" +#include "vram-viewer.moc" VramViewer *vramViewer; VramViewer::VramViewer() : QbWindow(config().geometry.vramViewer) { @@ -53,6 +53,11 @@ void VramViewer::autoUpdate() { if(autoUpdateBox->isChecked()) refresh(); } +void VramViewer::show() { + QbWindow::show(); + refresh(); +} + void VramViewer::refresh() { canvas->image->fill(0x800000); if(SNES::cartridge.loaded()) { diff --git a/src/ui_qt/debugger/vramviewer.moc.hpp b/src/ui_qt/debugger/ppu/vram-viewer.moc.hpp similarity index 98% rename from src/ui_qt/debugger/vramviewer.moc.hpp rename to src/ui_qt/debugger/ppu/vram-viewer.moc.hpp index 7db667fa..99656018 100644 --- a/src/ui_qt/debugger/vramviewer.moc.hpp +++ b/src/ui_qt/debugger/ppu/vram-viewer.moc.hpp @@ -20,6 +20,7 @@ public: VramViewer(); public slots: + void show(); void refresh(); void setDepth2bpp(); void setDepth4bpp(); diff --git a/src/ui_qt/debugger/breakpoint.cpp b/src/ui_qt/debugger/tools/breakpoint.cpp similarity index 100% rename from src/ui_qt/debugger/breakpoint.cpp rename to src/ui_qt/debugger/tools/breakpoint.cpp diff --git a/src/ui_qt/debugger/breakpoint.moc.hpp b/src/ui_qt/debugger/tools/breakpoint.moc.hpp similarity index 100% rename from src/ui_qt/debugger/breakpoint.moc.hpp rename to src/ui_qt/debugger/tools/breakpoint.moc.hpp diff --git a/src/ui_qt/debugger/disassembler.cpp b/src/ui_qt/debugger/tools/disassembler.cpp similarity index 66% rename from src/ui_qt/debugger/disassembler.cpp rename to src/ui_qt/debugger/tools/disassembler.cpp index 589f2d11..847203a1 100644 --- a/src/ui_qt/debugger/disassembler.cpp +++ b/src/ui_qt/debugger/tools/disassembler.cpp @@ -1,11 +1,10 @@ #include "disassembler.moc" +CPUDisassembler *cpuDisassembler; +SMPDisassembler *smpDisassembler; Disassembler *disassembler; -Disassembler::Disassembler() : QbWindow(config().geometry.disassembler) { - setObjectName("disassembler"); - setWindowTitle("Disassembler"); - - layout = new QHBoxLayout; +CPUDisassembler::CPUDisassembler() { + layout = new QVBoxLayout; layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); @@ -14,30 +13,43 @@ Disassembler::Disassembler() : QbWindow(config().geometry.disassembler) { view->setReadOnly(true); view->setFont(QFont(Style::Monospace)); view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view->setMinimumWidth((23 + 2) * view->fontMetrics().width(' ')); view->setMinimumHeight((25 + 1) * view->fontMetrics().height()); layout->addWidget(view); +} - controlLayout = new QVBoxLayout; - controlLayout->setAlignment(Qt::AlignTop); - controlLayout->setSpacing(0); - layout->addLayout(controlLayout); +SMPDisassembler::SMPDisassembler() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); - sourceLabel = new QLabel("Source:"); - controlLayout->addWidget(sourceLabel); + view = new QTextEdit; + view->setReadOnly(true); + view->setFont(QFont(Style::Monospace)); + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setMinimumHeight((25 + 1) * view->fontMetrics().height()); + layout->addWidget(view); +} - sourceCPU = new QRadioButton("S-CPU"); - sourceCPU->setChecked(true); - controlLayout->addWidget(sourceCPU); +Disassembler::Disassembler() : QbWindow(config().geometry.disassembler) { + setObjectName("disassembler"); + setWindowTitle("Disassembler"); - sourceSMP = new QRadioButton("S-SMP"); - controlLayout->addWidget(sourceSMP); + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + cpuDisassembler = new CPUDisassembler; + smpDisassembler = new SMPDisassembler; + + tab = new QTabWidget; + tab->addTab(cpuDisassembler, "S-CPU"); + tab->addTab(smpDisassembler, "S-SMP"); + layout->addWidget(tab); } void Disassembler::refresh(Source source, unsigned addr) { - if(source == CPU && !sourceCPU->isChecked()) return; - if(source == SMP && !sourceSMP->isChecked()) return; - uint8 *usage; unsigned mask; if(source == CPU) { usage = SNES::cpu.usage; mask = (1 << 24) - 1; } @@ -92,5 +104,7 @@ void Disassembler::refresh(Source source, unsigned addr) { output << ""; if(i != 24) output << "
"; } - view->setHtml(output); + + if(source == CPU) cpuDisassembler->view->setHtml(output); + if(source == SMP) smpDisassembler->view->setHtml(output); } diff --git a/src/ui_qt/debugger/tools/disassembler.moc.hpp b/src/ui_qt/debugger/tools/disassembler.moc.hpp new file mode 100644 index 00000000..d68850ad --- /dev/null +++ b/src/ui_qt/debugger/tools/disassembler.moc.hpp @@ -0,0 +1,29 @@ +class CPUDisassembler : public QWidget { +public: + QVBoxLayout *layout; + QTextEdit *view; + CPUDisassembler(); +}; + +class SMPDisassembler : public QWidget { +public: + QVBoxLayout *layout; + QTextEdit *view; + SMPDisassembler(); +}; + +class Disassembler : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tab; + + enum Source { CPU, SMP }; + void refresh(Source, unsigned); + Disassembler(); +}; + +extern CPUDisassembler *cpuDisassembler; +extern SMPDisassembler *smpDisassembler; +extern Disassembler *disassembler; diff --git a/src/ui_qt/debugger/memory.cpp b/src/ui_qt/debugger/tools/memory.cpp similarity index 75% rename from src/ui_qt/debugger/memory.cpp rename to src/ui_qt/debugger/tools/memory.cpp index 553707be..f0515a07 100644 --- a/src/ui_qt/debugger/memory.cpp +++ b/src/ui_qt/debugger/tools/memory.cpp @@ -50,7 +50,8 @@ MemoryEditor::MemoryEditor() : QbWindow(config().geometry.memoryEditor) { controlLayout->addWidget(importButton); connect(source, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceChanged(int))); - connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(refresh())); + connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(updateOffset())); + connect(addr, SIGNAL(returnPressed()), this, SLOT(updateOffset())); connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); connect(exportButton, SIGNAL(released()), this, SLOT(exportMemory())); connect(importButton, SIGNAL(released()), this, SLOT(importMemory())); @@ -81,6 +82,11 @@ void MemoryEditor::synchronize() { } } +void MemoryEditor::show() { + QbWindow::show(); + refresh(); +} + void MemoryEditor::sourceChanged(int index) { switch(index) { default: case 0: memorySource = SNES::Debugger::CPUBus; editor->setSize(16 * 1024 * 1024); break; @@ -97,35 +103,36 @@ void MemoryEditor::sourceChanged(int index) { void MemoryEditor::refresh() { if(SNES::cartridge.loaded() == false) { editor->setHtml(""); - return; + } else { + editor->update(); } +} +void MemoryEditor::updateOffset() { editor->setOffset(strhex(addr->text().toUtf8().data())); - editor->update(); + refresh(); } void MemoryEditor::exportMemory() { - string basename = config().path.data; - if(basename == "") basename = dir(utility.cartridge.fileName); + string basename = filepath(nall::basename(cartridge.fileName), config().path.data); - exportMemory(SNES::memory::cartram, string() << basename << "sram.bin"); - exportMemory(SNES::memory::wram, string() << basename << "wram.bin"); - exportMemory(SNES::memory::apuram, string() << basename << "apuram.bin"); - exportMemory(SNES::memory::vram, string() << basename << "vram.bin"); - exportMemory(SNES::memory::oam, string() << basename << "oam.bin"); - exportMemory(SNES::memory::cgram, string() << basename << "cgram.bin"); + exportMemory(SNES::memory::cartram, string() << basename << "-sram.bin"); + exportMemory(SNES::memory::wram, string() << basename << "-wram.bin"); + exportMemory(SNES::memory::apuram, string() << basename << "-apuram.bin"); + exportMemory(SNES::memory::vram, string() << basename << "-vram.bin"); + exportMemory(SNES::memory::oam, string() << basename << "-oam.bin"); + exportMemory(SNES::memory::cgram, string() << basename << "-cgram.bin"); } void MemoryEditor::importMemory() { - string basename = config().path.data; - if(basename == "") basename = dir(utility.cartridge.fileName); + string basename = filepath(nall::basename(cartridge.fileName), config().path.data); - importMemory(SNES::memory::cartram, string() << basename << "sram.bin"); - importMemory(SNES::memory::wram, string() << basename << "wram.bin"); - importMemory(SNES::memory::apuram, string() << basename << "apuram.bin"); - importMemory(SNES::memory::vram, string() << basename << "vram.bin"); - importMemory(SNES::memory::oam, string() << basename << "oam.bin"); - importMemory(SNES::memory::cgram, string() << basename << "cgram.bin"); + importMemory(SNES::memory::cartram, string() << basename << "-sram.bin"); + importMemory(SNES::memory::wram, string() << basename << "-wram.bin"); + importMemory(SNES::memory::apuram, string() << basename << "-apuram.bin"); + importMemory(SNES::memory::vram, string() << basename << "-vram.bin"); + importMemory(SNES::memory::oam, string() << basename << "-oam.bin"); + importMemory(SNES::memory::cgram, string() << basename << "-cgram.bin"); refresh(); //in case import changed values that are currently being displayed ... } diff --git a/src/ui_qt/debugger/memory.moc.hpp b/src/ui_qt/debugger/tools/memory.moc.hpp similarity index 95% rename from src/ui_qt/debugger/memory.moc.hpp rename to src/ui_qt/debugger/tools/memory.moc.hpp index 1eeb431e..d63d2ab0 100644 --- a/src/ui_qt/debugger/memory.moc.hpp +++ b/src/ui_qt/debugger/tools/memory.moc.hpp @@ -9,8 +9,8 @@ public: QLineEdit *addr; QCheckBox *autoUpdateBox; QPushButton *refreshButton; - QPushButton *exportButton; QWidget *spacer; + QPushButton *exportButton; QPushButton *importButton; void autoUpdate(); @@ -23,8 +23,10 @@ public: MemoryEditor(); public slots: + void show(); void sourceChanged(int); void refresh(); + void updateOffset(); void exportMemory(); void importMemory(); void exportMemory(SNES::Memory&, const string&) const; diff --git a/src/ui_qt/debugger/tools/properties.cpp b/src/ui_qt/debugger/tools/properties.cpp new file mode 100644 index 00000000..9faeff72 --- /dev/null +++ b/src/ui_qt/debugger/tools/properties.cpp @@ -0,0 +1,88 @@ +#include "properties.moc" +PropertiesWidget *cpuPropertiesTab; +PropertiesWidget *ppuPropertiesTab; +PropertiesViewer *propertiesViewer; + +void PropertiesWidget::refresh() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned v = 0; v < items.count(); v++) { + QTreeWidgetItem *item = items[v]; + unsigned id = item->data(0, Qt::UserRole).toUInt(); + string name, value; + object.property(id, name, value); + item->setText(1, value); + } +} + +PropertiesWidget::PropertiesWidget(SNES::ChipDebugger &object) : object(object) { + setMinimumSize(480, 240); + + layout = new QVBoxLayout; + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(2); + list->setHeaderLabels(QStringList() << "Name" << "Value"); + list->setAllColumnsShowFocus(true); + list->setAlternatingRowColors(true); + list->setRootIsDecorated(false); + list->setSortingEnabled(false); + layout->addWidget(list); + + unsigned counter = 0; + while(true) { + string name, value; + bool result = object.property(counter, name, value); + if(result == false) break; + + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(counter++)); + item->setText(0, name); + } + for(unsigned i = 0; i <= 1; i++) list->resizeColumnToContents(i); +} + +void PropertiesViewer::refresh() { + cpuPropertiesTab->refresh(); + ppuPropertiesTab->refresh(); +} + +void PropertiesViewer::show() { + QbWindow::show(); + refresh(); +} + +void PropertiesViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +PropertiesViewer::PropertiesViewer() : QbWindow(config().geometry.propertiesViewer) { + setObjectName("properties-viewer"); + setWindowTitle("Properties"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + tabWidget = new QTabWidget; + layout->addWidget(tabWidget); + + cpuPropertiesTab = new PropertiesWidget(SNES::cpu); + tabWidget->addTab(cpuPropertiesTab, "S-CPU"); + + ppuPropertiesTab = new PropertiesWidget(SNES::ppu); + tabWidget->addTab(ppuPropertiesTab, "S-PPU"); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} diff --git a/src/ui_qt/debugger/tools/properties.moc.hpp b/src/ui_qt/debugger/tools/properties.moc.hpp new file mode 100644 index 00000000..d8851006 --- /dev/null +++ b/src/ui_qt/debugger/tools/properties.moc.hpp @@ -0,0 +1,37 @@ +class PropertiesWidget : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTreeWidget *list; + + PropertiesWidget(SNES::ChipDebugger &object); + +public slots: + void refresh(); + +private: + SNES::ChipDebugger &object; +}; + +class PropertiesViewer : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tabWidget; + QHBoxLayout *controlLayout; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + + void autoUpdate(); + PropertiesViewer(); + +public slots: + void refresh(); + void show(); +}; + +extern PropertiesWidget *cpuPropertiesTab; +extern PropertiesWidget *ppuPropertiesTab; +extern PropertiesViewer *propertiesViewer; diff --git a/src/ui_qt/input/input.cpp b/src/ui_qt/input/input.cpp index f60d14c0..cfe8087a 100644 --- a/src/ui_qt/input/input.cpp +++ b/src/ui_qt/input/input.cpp @@ -31,6 +31,9 @@ void MappedInput::bind() { if(part[1] == "Hi") specifier = InputSpecifier::Hi; if(part[1] == "Trigger") specifier = InputSpecifier::Trigger; + //bypass modifier matching if scancode is itself a modifier + modifierOverride = Keyboard::isAnyModifier(scancode); + //re-encode name, in case previous name was invalid name = ""; if(modifier & InputModifier::Shift) name << "Shift+"; @@ -53,6 +56,7 @@ void MappedInput::cache() { MappedInput::MappedInput(const char *label_, const char *configName) : parent(0), label(label_) { specifier = InputSpecifier::None; + modifierOverride = false; state = 0; previousState = 0; cachedState = 0; @@ -63,7 +67,7 @@ MappedInput::MappedInput(const char *label_, const char *configName) : parent(0) void DigitalInput::poll() { previousState = state; - if(modifier == mapper().modifier) { + if(modifier == mapper().modifier || modifierOverride) { if(specifier == InputSpecifier::None) { state = mapper().state(scancode); } else if(specifier == InputSpecifier::Up) { diff --git a/src/ui_qt/input/input.hpp b/src/ui_qt/input/input.hpp index 02c7c7dd..88fe2ab6 100644 --- a/src/ui_qt/input/input.hpp +++ b/src/ui_qt/input/input.hpp @@ -11,6 +11,7 @@ struct MappedInput { unsigned specifier; unsigned modifier; unsigned scancode; + bool modifierOverride; int16_t state; int16_t previousState; int16_t cachedState; diff --git a/src/ui_qt/input/userinterface-emulationspeed.cpp b/src/ui_qt/input/userinterface-emulationspeed.cpp index 657d8e9e..0f9a1d51 100644 --- a/src/ui_qt/input/userinterface-emulationspeed.cpp +++ b/src/ui_qt/input/userinterface-emulationspeed.cpp @@ -30,7 +30,7 @@ struct Slowdown : HotkeyInput { mainWindow->syncUi(); } - Slowdown() : HotkeyInput("Slowdown", "config.userInterface.emulationSpeed.slowdown") { + Slowdown() : HotkeyInput("Slowdown", "input.userInterface.emulationSpeed.slowdown") { userInterfaceEmulationSpeed.attach(this); } } slowdown; @@ -59,7 +59,7 @@ struct Speedup : HotkeyInput { mainWindow->syncUi(); } - Speedup() : HotkeyInput("Speedup", "config.userInterface.emulationSpeed.speedup") { + Speedup() : HotkeyInput("Speedup", "input.userInterface.emulationSpeed.speedup") { name = "KB0::Tilde"; userInterfaceEmulationSpeed.attach(this); } @@ -72,7 +72,7 @@ struct Decrease : HotkeyInput { mainWindow->syncUi(); } - Decrease() : HotkeyInput("Decrease", "config.userInterface.emulationSpeed.decrease") { + Decrease() : HotkeyInput("Decrease", "input.userInterface.emulationSpeed.decrease") { name = "Control+KB0::Divide"; userInterfaceEmulationSpeed.attach(this); } @@ -85,7 +85,7 @@ struct Increase : HotkeyInput { mainWindow->syncUi(); } - Increase() : HotkeyInput("Increase", "config.userInterface.emulationSpeed.increase") { + Increase() : HotkeyInput("Increase", "input.userInterface.emulationSpeed.increase") { name = "Control+KB0::Multiply"; userInterfaceEmulationSpeed.attach(this); } @@ -98,7 +98,7 @@ struct SetSlowestSpeed : HotkeyInput { mainWindow->syncUi(); } - SetSlowestSpeed() : HotkeyInput("Set Slowest Speed", "config.userInterface.emulationSpeed.setSlowest") { + SetSlowestSpeed() : HotkeyInput("Set Slowest Speed", "input.userInterface.emulationSpeed.setSlowest") { name = "Control+KB0::Num1"; userInterfaceEmulationSpeed.attach(this); } @@ -111,7 +111,7 @@ struct SetSlowSpeed : HotkeyInput { mainWindow->syncUi(); } - SetSlowSpeed() : HotkeyInput("Set Slow Speed", "config.userInterface.emulationSpeed.setSlow") { + SetSlowSpeed() : HotkeyInput("Set Slow Speed", "input.userInterface.emulationSpeed.setSlow") { name = "Control+KB0::Num2"; userInterfaceEmulationSpeed.attach(this); } @@ -124,7 +124,7 @@ struct SetNormalSpeed : HotkeyInput { mainWindow->syncUi(); } - SetNormalSpeed() : HotkeyInput("Set Normal Speed", "config.userInterface.emulationSpeed.setNormal") { + SetNormalSpeed() : HotkeyInput("Set Normal Speed", "input.userInterface.emulationSpeed.setNormal") { name = "Control+KB0::Num3"; userInterfaceEmulationSpeed.attach(this); } @@ -137,7 +137,7 @@ struct SetFastSpeed : HotkeyInput { mainWindow->syncUi(); } - SetFastSpeed() : HotkeyInput("Set Fast Speed", "config.userInterface.emulationSpeed.setFast") { + SetFastSpeed() : HotkeyInput("Set Fast Speed", "input.userInterface.emulationSpeed.setFast") { name = "Control+KB0::Num4"; userInterfaceEmulationSpeed.attach(this); } @@ -150,7 +150,7 @@ struct SetFastestSpeed : HotkeyInput { mainWindow->syncUi(); } - SetFastestSpeed() : HotkeyInput("Set Fastest Speed", "config.userInterface.emulationSpeed.setFastest") { + SetFastestSpeed() : HotkeyInput("Set Fastest Speed", "input.userInterface.emulationSpeed.setFastest") { name = "Control+KB0::Num5"; userInterfaceEmulationSpeed.attach(this); } @@ -161,7 +161,7 @@ struct SynchronizeVideo : HotkeyInput { utility.toggleSynchronizeVideo(); } - SynchronizeVideo() : HotkeyInput("Synchronize Video", "config.userInterface.emulationSpeed.synchronizeVideo") { + SynchronizeVideo() : HotkeyInput("Synchronize Video", "input.userInterface.emulationSpeed.synchronizeVideo") { name = "Control+KB0::V"; userInterfaceEmulationSpeed.attach(this); } @@ -172,7 +172,7 @@ struct SynchronizeAudio : HotkeyInput { utility.toggleSynchronizeAudio(); } - SynchronizeAudio() : HotkeyInput("Synchronize Audio", "config.userInterface.emulationSpeed.synchronizeAudio") { + SynchronizeAudio() : HotkeyInput("Synchronize Audio", "input.userInterface.emulationSpeed.synchronizeAudio") { name = "Control+KB0::A"; userInterfaceEmulationSpeed.attach(this); } diff --git a/src/ui_qt/input/userinterface-general.cpp b/src/ui_qt/input/userinterface-general.cpp index 8296fc12..82892c7d 100644 --- a/src/ui_qt/input/userinterface-general.cpp +++ b/src/ui_qt/input/userinterface-general.cpp @@ -27,10 +27,10 @@ struct ToggleStatusbar : HotkeyInput { struct ToggleCheatSystem : HotkeyInput { void pressed() { if(SNES::cheat.enabled() == false) { - SNES::cheat.enable(); + SNES::cheat.enable(true); utility.showMessage("Cheat system enabled."); } else { - SNES::cheat.disable(); + SNES::cheat.enable(false); utility.showMessage("Cheat system disabled."); } } diff --git a/src/ui_qt/interface.cpp b/src/ui_qt/interface.cpp index 5b1680ad..7bd4a9d2 100644 --- a/src/ui_qt/interface.cpp +++ b/src/ui_qt/interface.cpp @@ -35,7 +35,7 @@ void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width saveScreenshot = false; QImage image((const unsigned char*)data, width, height, pitch, QImage::Format_RGB32); - string filename = nall::basename(notdir(utility.cartridge.fileName)); + string filename = nall::basename(cartridge.fileName); time_t systemTime = time(0); tm *currentTime = localtime(&systemTime); char t[512]; @@ -45,9 +45,7 @@ void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width ); filename << "-" << t << ".png"; - string path = config().path.data; - if(path == "") path = dir(utility.cartridge.baseName); - image.save(string() << path << filename); + image.save(filepath(filename, config().path.data)); utility.showMessage("Screenshot saved."); } diff --git a/src/ui_qt/main.cpp b/src/ui_qt/main.cpp index 10f2eacc..48a659e8 100644 --- a/src/ui_qt/main.cpp +++ b/src/ui_qt/main.cpp @@ -18,10 +18,6 @@ #include "interface.cpp" const char defaultStylesheet[] = - "QLabel.title {" - " font: bold 18px \"Georgia\";" - "}\n" - "#backdrop {" " background: #000000;" "}\n"; @@ -31,6 +27,13 @@ const char defaultStylesheet[] = #include "link/reader.cpp" #include "utility/utility.cpp" +//override filename's path with filepath, but only if filepath isn't empty +//used for GUI's "path selection" functionality +string filepath(const char *filename, const char *filepath) { + if(!filepath || !*filepath) return filename; + return string() << dir(filepath) << notdir(filename); +} + int main(int argc, char **argv) { return application.main(argc, argv); } diff --git a/src/ui_qt/movie/movie.cpp b/src/ui_qt/movie/movie.cpp index 75e45e34..49904258 100644 --- a/src/ui_qt/movie/movie.cpp +++ b/src/ui_qt/movie/movie.cpp @@ -73,9 +73,8 @@ void Movie::stop() { } string Movie::makeFilename() const { - string filename; + string filename = nall::basename(cartridge.fileName); - filename << utility.cartridge.name << "-"; time_t systemTime = time(0); tm *currentTime = localtime(&systemTime); char t[512]; @@ -83,13 +82,9 @@ string Movie::makeFilename() const { 1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday, currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec ); - filename << t << ".bsv"; + filename << "-" << t << ".bsv"; - string filepath = config().path.data; - if(filepath == "") filepath = dir(utility.cartridge.fileName); - filepath << filename; - - return filepath; + return filepath(filename, config().path.data); } int16_t Movie::read() { diff --git a/src/ui_qt/resource/resource.qrc b/src/ui_qt/resource/resource.qrc index e53bb85b..84af06de 100644 --- a/src/ui_qt/resource/resource.qrc +++ b/src/ui_qt/resource/resource.qrc @@ -3,7 +3,6 @@ ../../data/bsnes.png ../../data/logo.png - ../../data/joypad.png ../../data/documentation.html ../../data/license.html @@ -12,9 +11,12 @@ ../../data/icons-16x16/item-radio-on.png ../../data/icons-16x16/item-radio-off.png + ../../data/icons-16x16/accessories-text-editor.png ../../data/icons-16x16/applications-multimedia.png ../../data/icons-16x16/appointment-new.png + ../../data/icons-16x16/audio-volume-high.png ../../data/icons-16x16/document-open.png + ../../data/icons-16x16/folder.png ../../data/icons-16x16/folder-new.png ../../data/icons-16x16/help-browser.png ../../data/icons-16x16/image-x-generic.png @@ -23,20 +25,13 @@ ../../data/icons-16x16/media-playback-stop.png ../../data/icons-16x16/media-record.png ../../data/icons-16x16/preferences-desktop.png + ../../data/icons-16x16/preferences-system.png ../../data/icons-16x16/process-stop.png + ../../data/icons-16x16/system-file-manager.png + ../../data/icons-16x16/system-search.png ../../data/icons-16x16/text-x-generic.png ../../data/icons-16x16/utilities-terminal.png ../../data/icons-16x16/video-display.png ../../data/icons-16x16/view-refresh.png - - ../../data/icons-22x22/accessories-text-editor.png - ../../data/icons-22x22/audio-volume-high.png - ../../data/icons-22x22/folder.png - ../../data/icons-22x22/image-x-generic.png - ../../data/icons-22x22/input-gaming.png - ../../data/icons-22x22/preferences-system.png - ../../data/icons-22x22/system-file-manager.png - ../../data/icons-22x22/system-search.png - ../../data/icons-22x22/video-display.png diff --git a/src/ui_qt/settings/advanced.cpp b/src/ui_qt/settings/advanced.cpp index 42fb4c14..f28fb2f4 100644 --- a/src/ui_qt/settings/advanced.cpp +++ b/src/ui_qt/settings/advanced.cpp @@ -3,16 +3,11 @@ AdvancedSettingsWindow *advancedSettingsWindow; AdvancedSettingsWindow::AdvancedSettingsWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(0); layout->setAlignment(Qt::AlignTop); setLayout(layout); - title = new QLabel("Advanced Configuration Settings"); - title->setProperty("class", "title"); - layout->addWidget(title); - layout->addSpacing(Style::WidgetSpacing); - driverLayout = new QGridLayout; driverLayout->setHorizontalSpacing(Style::WidgetSpacing); layout->addLayout(driverLayout); @@ -44,6 +39,7 @@ AdvancedSettingsWindow::AdvancedSettingsWindow() { layout->addWidget(regionTitle); regionLayout = new QHBoxLayout; + regionLayout->setSpacing(Style::WidgetSpacing); layout->addLayout(regionLayout); layout->addSpacing(Style::WidgetSpacing); @@ -68,6 +64,7 @@ AdvancedSettingsWindow::AdvancedSettingsWindow() { layout->addWidget(portTitle); portLayout = new QHBoxLayout; + portLayout->setSpacing(Style::WidgetSpacing); layout->addLayout(portLayout); layout->addSpacing(Style::WidgetSpacing); @@ -88,6 +85,7 @@ AdvancedSettingsWindow::AdvancedSettingsWindow() { layout->addWidget(focusTitle); focusLayout = new QHBoxLayout; + focusLayout->setSpacing(Style::WidgetSpacing); layout->addLayout(focusLayout); layout->addSpacing(Style::WidgetSpacing); diff --git a/src/ui_qt/settings/advanced.moc.hpp b/src/ui_qt/settings/advanced.moc.hpp index a460dcd3..42fd41a7 100644 --- a/src/ui_qt/settings/advanced.moc.hpp +++ b/src/ui_qt/settings/advanced.moc.hpp @@ -3,7 +3,7 @@ class AdvancedSettingsWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; + QGridLayout *driverLayout; QLabel *videoLabel; QLabel *audioLabel; @@ -12,24 +12,28 @@ public: QComboBox *audioDriver; QComboBox *inputDriver; QLabel *driverInfo; + QLabel *regionTitle; QHBoxLayout *regionLayout; QButtonGroup *regionGroup; QRadioButton *regionAuto; QRadioButton *regionNTSC; QRadioButton *regionPAL; + QLabel *portTitle; QHBoxLayout *portLayout; QButtonGroup *portGroup; QRadioButton *portSatellaview; QRadioButton *portNone; QWidget *portSpacer; + QLabel *focusTitle; QHBoxLayout *focusLayout; QButtonGroup *focusButtonGroup; QRadioButton *focusPause; QRadioButton *focusIgnore; QRadioButton *focusAllow; + QLabel *rewindTitle; QCheckBox *rewindEnable; diff --git a/src/ui_qt/settings/audio.cpp b/src/ui_qt/settings/audio.cpp index 1dbbf2ef..0b4f4aae 100644 --- a/src/ui_qt/settings/audio.cpp +++ b/src/ui_qt/settings/audio.cpp @@ -3,15 +3,11 @@ AudioSettingsWindow *audioSettingsWindow; AudioSettingsWindow::AudioSettingsWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); layout->setAlignment(Qt::AlignTop); setLayout(layout); - title = new QLabel("Audio Settings"); - title->setProperty("class", "title"); - layout->addWidget(title); - boxes = new QHBoxLayout; layout->addLayout(boxes); diff --git a/src/ui_qt/settings/audio.moc.hpp b/src/ui_qt/settings/audio.moc.hpp index af80cb1b..24b42a9b 100644 --- a/src/ui_qt/settings/audio.moc.hpp +++ b/src/ui_qt/settings/audio.moc.hpp @@ -3,7 +3,6 @@ class AudioSettingsWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; QHBoxLayout *boxes; QLabel *frequencyLabel; QComboBox *frequency; diff --git a/src/ui_qt/settings/input.cpp b/src/ui_qt/settings/input.cpp index e80442ff..3698ccc3 100644 --- a/src/ui_qt/settings/input.cpp +++ b/src/ui_qt/settings/input.cpp @@ -1,420 +1,253 @@ #include "input.moc" InputSettingsWindow *inputSettingsWindow; -InputSettingsWindow::InputSettingsWindow() : activeObject(0) { +InputSettingsWindow::InputSettingsWindow() { + activeInput = 0; + layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - title = new QLabel("Input Configuration Editor"); - title->setProperty("class", "title"); - layout->addWidget(title); - - comboLayout = new QHBoxLayout; - layout->addLayout(comboLayout); - - port = new QComboBox; - port->addItem("Controller Port 1"); - port->addItem("Controller Port 2"); - port->addItem("User Interface"); - comboLayout->addWidget(port); - - device = new QComboBox; - comboLayout->addWidget(device); - - selectionWidget = new QWidget; - selectionWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - layout->addWidget(selectionWidget); - - selectionLayout = new QVBoxLayout; - selectionLayout->setMargin(0); - selectionLayout->setSpacing(Style::WidgetSpacing); - selectionWidget->setLayout(selectionLayout); - list = new QTreeWidget; - list->setColumnCount(3); - list->setHeaderLabels(QStringList() << "Hidden" << "Name" << "Assignment"); + list->setColumnCount(2); list->setAllColumnsShowFocus(true); - list->setRootIsDecorated(false); - list->hideColumn(0); //used for default sorting - selectionLayout->addWidget(list); + list->setSortingEnabled(false); + list->header()->hide(); + list->header()->setResizeMode(QHeaderView::ResizeToContents); + layout->addWidget(list); - selectionControlLayout = new QHBoxLayout; - selectionLayout->addLayout(selectionControlLayout); + controlLayout = new QHBoxLayout; + layout->addLayout(controlLayout); + + message = new QLabel; + message->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + controlLayout->addWidget(message); + + optionButton = new QPushButton("Options"); + controlLayout->addWidget(optionButton); + + optionMenu = new QMenu; + optionButton->setMenu(optionMenu); + + optionAssignModifiers = new QbCheckAction("Assign Modifiers as Keys", 0); + optionMenu->addAction(optionAssignModifiers); assignButton = new QPushButton("Assign"); - selectionControlLayout->addWidget(assignButton); - - assignAllButton = new QPushButton("Assign All"); - selectionControlLayout->addWidget(assignAllButton); + controlLayout->addWidget(assignButton); unassignButton = new QPushButton("Unassign"); - selectionControlLayout->addWidget(unassignButton); - - unassignAllButton = new QPushButton("Unassign All"); - selectionControlLayout->addWidget(unassignAllButton); - - assignmentWidget = new QWidget; - assignmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - assignmentWidget->hide(); - layout->addWidget(assignmentWidget); - - assignmentLayout = new QVBoxLayout; - assignmentLayout->setMargin(0); - assignmentLayout->setSpacing(Style::WidgetSpacing); - assignmentWidget->setLayout(assignmentLayout); - - assignmentLabel = new QLabel; - assignmentLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - assignmentLabel->setAlignment(Qt::AlignTop); - assignmentLabel->setFocusPolicy(Qt::StrongFocus); - assignmentLabel->setFrameShape(QFrame::Panel); - assignmentLabel->setFrameShadow(QFrame::Sunken); - assignmentLayout->addWidget(assignmentLabel); - - assignmentControlLayout = new QHBoxLayout; - assignmentLayout->addLayout(assignmentControlLayout); - - xaxisButton = new QPushButton("X-axis"); - assignmentControlLayout->addWidget(xaxisButton); - - yaxisButton = new QPushButton("Y-axis"); - assignmentControlLayout->addWidget(yaxisButton); - - spacer = new QWidget; - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - assignmentControlLayout->addWidget(spacer); - - helpButton = new QPushButton("Help"); - assignmentControlLayout->addWidget(helpButton); - - cancelButton = new QPushButton("Cancel Assignment"); - assignmentControlLayout->addWidget(cancelButton); - - connect(port, SIGNAL(currentIndexChanged(int)), this, SLOT(portChanged())); - connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); + controlLayout->addWidget(unassignButton); + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(synchronize())); connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(assign())); - connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); connect(assignButton, SIGNAL(released()), this, SLOT(assign())); - connect(assignAllButton, SIGNAL(released()), this, SLOT(assignAll())); connect(unassignButton, SIGNAL(released()), this, SLOT(unassign())); - connect(unassignAllButton, SIGNAL(released()), this, SLOT(unassignAll())); - connect(xaxisButton, SIGNAL(released()), this, SLOT(assignXaxis())); - connect(yaxisButton, SIGNAL(released()), this, SLOT(assignYaxis())); - connect(helpButton, SIGNAL(released()), this, SLOT(showHelp())); - connect(cancelButton, SIGNAL(released()), this, SLOT(cancelAssignment())); + connect(optionAssignModifiers, SIGNAL(triggered()), this, SLOT(toggleAssignModifiers())); - portChanged(); -} + //initialize list -void InputSettingsWindow::syncUi() { - QList itemList = list->selectedItems(); - assignButton->setEnabled(itemList.count() == 1); - assignAllButton->setEnabled(port->currentIndex() < 2); //only allow for controllers, not GUI hotkeys - unassignButton->setEnabled(itemList.count() == 1); -} + port1 = new QTreeWidgetItem(list); + port1->setData(0, Qt::UserRole, QVariant(-1)); + port1->setText(0, "Controller Port 1"); -//when port combobox item is changed, device list needs to be repopulated -void InputSettingsWindow::portChanged() { - disconnect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); - device->clear(); + port2 = new QTreeWidgetItem(list); + port2->setData(0, Qt::UserRole, QVariant(-1)); + port2->setText(0, "Controller Port 2"); + + userInterface = new QTreeWidgetItem(list); + userInterface->setData(0, Qt::UserRole, QVariant(-1)); + userInterface->setText(0, "User Interface"); - unsigned index = port->currentIndex(); for(unsigned i = 0; i < mapper().size(); i++) { - if(mapper()[i]->category == index) { - device->addItem(mapper()[i]->label); + InputGroup &group = *(mapper()[i]); + + QTreeWidgetItem *grandparent = 0; + if(group.category == InputCategory::Port1) { grandparent = port1; } + if(group.category == InputCategory::Port2) { grandparent = port2; } + if(group.category == InputCategory::UserInterface) { grandparent = userInterface; } + if(!grandparent) continue; + + QTreeWidgetItem *parent = new QTreeWidgetItem(grandparent); + parent->setData(0, Qt::UserRole, QVariant(-1)); + parent->setText(0, group.label); + + for(unsigned i = 0; i < group.size(); i++) { + QTreeWidgetItem *child = new QTreeWidgetItem(parent); + child->setData(0, Qt::UserRole, QVariant(inputTable.size())); + inputTable.add(group[i]); } } - reloadList(); - connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList())); + updateList(); + synchronize(); } -//when device combobox item is changed, object list needs to be repopulated -void InputSettingsWindow::reloadList() { - list->clear(); - list->setSortingEnabled(false); +void InputSettingsWindow::synchronize() { + bool enable = false; + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + signed index = item->data(0, Qt::UserRole).toInt(); + enable = (index != -1); + } + assignButton->setEnabled(enable); + unassignButton->setEnabled(enable); +} - unsigned portIndex = port->currentIndex(); - unsigned deviceIndex = device->currentIndex(); - unsigned index = 0; - for(unsigned i = 0; i < mapper().size(); i++) { - if(mapper()[i]->category == portIndex) { - if(index != deviceIndex) { - index++; - continue; +void InputSettingsWindow::updateList() { + QList all = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < all.size(); i++) { + QTreeWidgetItem *grandparent = all[i]; + for(unsigned j = 0; j < grandparent->childCount(); j++) { + QTreeWidgetItem *parent = grandparent->child(j); + for(unsigned k = 0; k < parent->childCount(); k++) { + QTreeWidgetItem *child = parent->child(k); + signed index = child->data(0, Qt::UserRole).toInt(); + if(index == -1) continue; + MappedInput *input = inputTable[index]; + child->setText(0, input->label); + child->setText(1, string() << "= " << input->name); + child->setForeground(1, QBrush(QColor(128, 128, 128))); } - - InputGroup &group = *(mapper()[i]); - for(unsigned i = 0; i < group.size(); i++) { - QTreeWidgetItem *item = new QTreeWidgetItem(list); - item->setData(0, Qt::UserRole, QVariant(i)); - item->setText(0, string() << (1000000 + i)); - item->setText(1, group[i]->label); - item->setText(2, group[i]->name); - } - - activeGroup = &group; - break; } } - - list->setSortingEnabled(true); - list->sortByColumn(0, Qt::AscendingOrder); //set default sorting on list change, overriding user setting - list->resizeColumnToContents(1); //shrink name column - syncUi(); } -void InputSettingsWindow::listChanged() { - syncUi(); +void InputSettingsWindow::setAssignment(string name) { + activeInput->name = name; + activeInput = 0; + list->setFocus(); + message->setText(""); + mapper().bind(); + updateList(); } -//=================== -//assignment routines -//=================== +void InputSettingsWindow::inputEvent(uint16_t scancode) { + if(!activeInput) return; + if(!isActiveWindow() || isMinimized()) return; + int16_t state = mapper().state(scancode); -void InputSettingsWindow::beginAssignment() { - settingsWindow->list->setEnabled(false); - port->setEnabled(false); - device->setEnabled(false); - selectionWidget->hide(); - assignmentWidget->show(); -} + if(dynamic_cast(activeInput)) { + if(Keyboard::isAnyKey(scancode) && mapper().state(scancode)) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + //don't map escape key, as it is reserved by the user interface + if(scancode == keyboard(i)[Keyboard::Escape]) return; + } -void InputSettingsWindow::showHelp() { - string message; + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } else if(Keyboard::isAnyModifier(scancode) && optionAssignModifiers->isChecked()) { + setAssignment(string() << Scancode::encode(scancode)); + } else if(Mouse::isAnyButton(scancode) && mapper().state(scancode)) { + //ensure button was clicked inside list box + unsigned wx = 0, wy = 0; + QWidget *widget = message; + while(widget) { + wx += widget->geometry().x(); + wy += widget->geometry().y(); + widget = widget->parentWidget(); + } + unsigned px = QCursor::pos().x(); + unsigned py = QCursor::pos().y(); + if(px < wx || px >= wx + message->size().width()) return; + if(py < wy || py >= wy + message->size().height()) return; - message << "Digital Button Assignment:
"; - message << "To assign, do any one of the following:
"; - message << "• press any keyboard key
"; - message << "• click any mouse button inside the capture box
"; - message << "• move any joypad hat in the desired direction
"; - message << "• move any joypad axis in the desired direction
"; - message << "• press any joypad button

"; - message << "For multi-key assignment, hold any combination of these keys down first:
"; - message << "• Shift, Control, Alt, Super"; - message << "

"; + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } else if(Joypad::isAnyHat(scancode)) { + string position; + if(state == Joypad::HatUp) position = ".Up"; + else if(state == Joypad::HatDown) position = ".Down"; + else if(state == Joypad::HatLeft) position = ".Left"; + else if(state == Joypad::HatRight) position = ".Right"; + else return; - message << "Analog Axis Assignment:
"; - message << "To assign, do any one of the following:
"; - message << "• click the desired mouse axis button
"; - message << "• move any joypad axis in either direction"; + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); + } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { + if(mapper().calibrated == false) { + MappedInput *temp = activeInput; + activeInput = 0; + mapper().calibrate(); + activeInput = temp; + } - audio.clear(); - QMessageBox::information(settingsWindow, "Input Assignment Help", message); -} + if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { + string position; + if(state < -24576) position = ".Lo"; + else if(state > +24576) position = ".Hi"; + else return; -void InputSettingsWindow::assignObject(MappedInput *object) { - //flush any pending events to prevent instantaneous assignment of eg mouse buttons - activeObject = 0; - activeMouse = 0; - mapper().poll(); + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); + } else { + if(state >= 0) return; - activeObject = object; + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << ".Trigger"); + } + } else if(Joypad::isAnyButton(scancode) && mapper().state(scancode)) { + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } + } else if(dynamic_cast(activeInput)) { + if(Mouse::isAnyButton(scancode)) { + //ensure button was clicked inside list box + unsigned wx = 0, wy = 0; + QWidget *widget = message; + while(widget) { + wx += widget->geometry().x(); + wy += widget->geometry().y(); + widget = widget->parentWidget(); + } + unsigned px = QCursor::pos().x(); + unsigned py = QCursor::pos().y(); + if(px < wx || px >= wx + message->size().width()) return; + if(py < wy || py >= wy + message->size().height()) return; - string label; - label << "" << port->currentText().toUtf8().constData() << " :: "; - label << device->currentText().toUtf8().constData() << " :: "; - label << activeObject->label << "
"; + unsigned number = Mouse::numberDecode(scancode); + unsigned button = Mouse::buttonDecode(scancode); + if(button == 0) setAssignment(string() << Scancode::encode(mouse(number).axis(0))); + if(button == 2) setAssignment(string() << Scancode::encode(mouse(number).axis(1))); + } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { + if(mapper().calibrated == false) { + MappedInput *temp = activeInput; + activeInput = 0; + mapper().calibrate(); + activeInput = temp; + } - if(dynamic_cast(activeObject)) { - xaxisButton->setVisible(false); - yaxisButton->setVisible(false); - label << "Digital Button Assignment"; - } else if(dynamic_cast(activeObject)) { - xaxisButton->setVisible(true); - yaxisButton->setVisible(true); - label << "Analog Axis Assignment"; - } - - if(dynamic_cast(activeGroup) - || dynamic_cast(activeGroup)) { - assignmentLabel->setStyleSheet( - "background-image: url(:/joypad.png);" - "background-position: bottom center;" - "background-repeat: none;" - ); - } else { - assignmentLabel->setStyleSheet(""); - } - - assignmentLabel->setText(label); - assignmentLabel->setFocus(); -} - -void InputSettingsWindow::endAssignment() { - activeObject = 0; - - if(multiAssign == false || ++multiAssignIndex >= activeGroup->size()) { - mapper().bind(); - reloadList(); - - assignmentWidget->hide(); - selectionWidget->show(); - settingsWindow->list->setEnabled(true); - port->setEnabled(true); - device->setEnabled(true); - } else { - assignObject((*activeGroup)[multiAssignIndex]); + if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { + if(state < -24576 || state > +24576) { + setAssignment(string() << Scancode::encode(scancode)); + } + } + } } } void InputSettingsWindow::assign() { - if(activeObject) return; - multiAssign = false; - QTreeWidgetItem *item = list->currentItem(); if(!item) return; - unsigned index = item->data(0, Qt::UserRole).toUInt(); + signed index = item->data(0, Qt::UserRole).toInt(); + if(index == -1) return; - beginAssignment(); - assignObject((*activeGroup)[index]); -} + //flush any pending events to prevent instantaneous assignment of scancodes + mapper().poll(); -void InputSettingsWindow::assignAll() { - if(activeObject) return; - multiAssign = true; - - beginAssignment(); - assignObject((*activeGroup)[multiAssignIndex = 0]); + activeInput = inputTable[index]; + message->setFocus(); + message->setText(string() << "Set assignment for: " << activeInput->label); } void InputSettingsWindow::unassign() { QTreeWidgetItem *item = list->currentItem(); if(!item) return; - unsigned index = item->data(0, Qt::UserRole).toUInt(); - MappedInput *object = (*activeGroup)[index]; - object->name = "None"; + signed index = item->data(0, Qt::UserRole).toInt(); + if(index == -1) return; + + MappedInput *input = inputTable[index]; + input->name = "None"; mapper().bind(); - reloadList(); + updateList(); } -void InputSettingsWindow::unassignAll() { - for(unsigned i = 0; i < activeGroup->size(); i++) { - MappedInput *object = (*activeGroup)[i]; - object->name = "None"; - } - mapper().bind(); - reloadList(); -} - -void InputSettingsWindow::cancelAssignment() { - multiAssign = false; - endAssignment(); -} - -void InputSettingsWindow::setAssignment(string name) { - if(multiAssign == true) { - //make sure this mapping was not previously used - for(unsigned i = 0; i < multiAssignIndex; i++) { - if(name == (*activeGroup)[i]->name) return; - } - } - - activeObject->name = name; - endAssignment(); -} - -void InputSettingsWindow::assignXaxis() { - setAssignment(Scancode::encode(mouse(activeMouse).axis(0))); -} - -void InputSettingsWindow::assignYaxis() { - setAssignment(Scancode::encode(mouse(activeMouse).axis(1))); -} - -void InputSettingsWindow::inputEvent(uint16_t scancode) { - if(dynamic_cast(activeObject)) { - digitalInputEvent(scancode); - } else if(dynamic_cast(activeObject)) { - analogInputEvent(scancode); - } -} - -void InputSettingsWindow::digitalInputEvent(uint16_t scancode) { - if(!isActiveWindow() || isMinimized()) return; - - if(Keyboard::isAnyKey(scancode) && mapper().state(scancode)) { - for(unsigned i = 0; i < Keyboard::Count; i++) { - //don't map escape key, as it is reserved by the user interface - if(scancode == keyboard(i)[Keyboard::Escape]) return; - } - - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); - } else if(Mouse::isAnyButton(scancode) && mapper().state(scancode)) { - //ensure button was clicked inside assignment label box - QRect windowRect = settingsWindow->geometry(); - QRect splitterRect = settingsWindow->splitter->geometry(); - QRect panelRect = settingsWindow->panel->geometry(); - QRect captureRect = assignmentWidget->geometry(); - QRect widgetRect = assignmentLabel->geometry(); - unsigned wx = windowRect.x() + splitterRect.x() + panelRect.x() + captureRect.x(); - unsigned wy = windowRect.y() + splitterRect.y() + panelRect.y() + captureRect.y(); - unsigned px = QCursor::pos().x(); - unsigned py = QCursor::pos().y(); - - if(px < wx || px >= wx + widgetRect.size().width()) return; - if(py < wy || py >= wy + widgetRect.size().height()) return; - - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); - } else if(Joypad::isAnyHat(scancode)) { - int16_t state = mapper().state(scancode); - string position; - if(state == Joypad::HatUp) position = ".Up"; - if(state == Joypad::HatDown) position = ".Down"; - if(state == Joypad::HatLeft) position = ".Left"; - if(state == Joypad::HatRight) position = ".Right"; - if(position == "") return; - - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); - } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { - if(mapper().calibrated == false) { - MappedInput *temp = activeObject; - activeObject = 0; - mapper().calibrate(); - activeObject = temp; - } - - if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { - int16_t state = mapper().state(scancode); - string position; - if(state < -24576) position = ".Lo"; - if(state > +24576) position = ".Hi"; - if(position == "") return; - - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); - } else { - int16_t state = mapper().state(scancode); - if(state >= 0) return; - - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << ".Trigger"); - } - } else if(Joypad::isAnyButton(scancode) && mapper().state(scancode)) { - setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); - } -} - -void InputSettingsWindow::analogInputEvent(uint16_t scancode) { - if(!isActiveWindow() || isMinimized()) return; - - if(Mouse::isAnyButton(scancode)) { - activeMouse = Mouse::numberDecode(scancode); - } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { - if(mapper().calibrated == false) { - MappedInput *temp = activeObject; - activeObject = 0; - mapper().calibrate(); - activeObject = temp; - } - - if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { - int16_t state = mapper().state(scancode); - if(state < -24576 || state > +24576) { - setAssignment(string() << Scancode::encode(scancode)); - } - } - } +void InputSettingsWindow::toggleAssignModifiers() { + optionAssignModifiers->setChecked(!optionAssignModifiers->isChecked()); } diff --git a/src/ui_qt/settings/input.moc.hpp b/src/ui_qt/settings/input.moc.hpp index 66705cdd..1da10acf 100644 --- a/src/ui_qt/settings/input.moc.hpp +++ b/src/ui_qt/settings/input.moc.hpp @@ -3,61 +3,31 @@ class InputSettingsWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; - QHBoxLayout *comboLayout; - QComboBox *port; - QComboBox *device; - - QWidget *selectionWidget; - QVBoxLayout *selectionLayout; QTreeWidget *list; - QHBoxLayout *selectionControlLayout; + QHBoxLayout *controlLayout; + QLabel *message; + QPushButton *optionButton; + QMenu *optionMenu; + QbCheckAction *optionAssignModifiers; QPushButton *assignButton; - QPushButton *assignAllButton; QPushButton *unassignButton; - QPushButton *unassignAllButton; - - QWidget *assignmentWidget; - QVBoxLayout *assignmentLayout; - QLabel *assignmentLabel; - QHBoxLayout *assignmentControlLayout; - QPushButton *xaxisButton; - QPushButton *yaxisButton; - QWidget *spacer; - QPushButton *helpButton; - QPushButton *cancelButton; void inputEvent(uint16_t scancode); - void syncUi(); InputSettingsWindow(); private slots: - void portChanged(); - void reloadList(); - void listChanged(); + void synchronize(); void assign(); - void assignAll(); void unassign(); - void unassignAll(); - void assignXaxis(); - void assignYaxis(); - void showHelp(); - void cancelAssignment(); + void toggleAssignModifiers(); private: - InputGroup *activeGroup; - MappedInput *activeObject; - unsigned activeMouse; - bool multiAssign; - unsigned multiAssignIndex; + QTreeWidgetItem *port1, *port2, *userInterface; + array inputTable; + MappedInput *activeInput; - void setAssignment(string name); - void digitalInputEvent(uint16_t scancode); - void analogInputEvent(uint16_t scancode); - - void beginAssignment(); - void assignObject(MappedInput *object); - void endAssignment(); + void updateList(); + void setAssignment(string); }; extern InputSettingsWindow *inputSettingsWindow; diff --git a/src/ui_qt/settings/paths.cpp b/src/ui_qt/settings/paths.cpp index 730807bf..52dbfee0 100644 --- a/src/ui_qt/settings/paths.cpp +++ b/src/ui_qt/settings/paths.cpp @@ -62,15 +62,11 @@ void PathSettingWidget::defaultPath() { PathSettingsWindow::PathSettingsWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); layout->setAlignment(Qt::AlignTop); setLayout(layout); - title = new QLabel("Default Folder Paths"); - title->setProperty("class", "title"); - layout->addWidget(title); - gamePath = new PathSettingWidget(config().path.rom, "Games:", "Remember last path", "Default Game Path"); savePath = new PathSettingWidget(config().path.save, "Save RAM:", "Same as loaded game", "Default Save RAM Path"); statePath = new PathSettingWidget(config().path.state, "Save states:", "Same as loaded game", "Default Save State Path"); diff --git a/src/ui_qt/settings/paths.moc.hpp b/src/ui_qt/settings/paths.moc.hpp index cdf924ee..f1608d9b 100644 --- a/src/ui_qt/settings/paths.moc.hpp +++ b/src/ui_qt/settings/paths.moc.hpp @@ -27,7 +27,6 @@ class PathSettingsWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; PathSettingWidget *gamePath; PathSettingWidget *savePath; PathSettingWidget *statePath; diff --git a/src/ui_qt/settings/pixelshader.cpp b/src/ui_qt/settings/pixelshader.cpp index 91b37880..437cd9b0 100644 --- a/src/ui_qt/settings/pixelshader.cpp +++ b/src/ui_qt/settings/pixelshader.cpp @@ -8,10 +8,6 @@ PixelShaderWindow::PixelShaderWindow() { layout->setAlignment(Qt::AlignTop); setLayout(layout); - title = new QLabel("Pixel Shader Selection"); - title->setProperty("class", "title"); - layout->addWidget(title); - gridLayout = new QGridLayout; gridLayout->setVerticalSpacing(0); layout->addLayout(gridLayout); diff --git a/src/ui_qt/settings/pixelshader.moc.hpp b/src/ui_qt/settings/pixelshader.moc.hpp index a764c9bd..fc487fe9 100644 --- a/src/ui_qt/settings/pixelshader.moc.hpp +++ b/src/ui_qt/settings/pixelshader.moc.hpp @@ -3,7 +3,6 @@ class PixelShaderWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; QGridLayout *gridLayout; QLabel *fragmentLabel; QLineEdit *fragmentPath; diff --git a/src/ui_qt/settings/settings.cpp b/src/ui_qt/settings/settings.cpp index c7a93f6e..0891d956 100644 --- a/src/ui_qt/settings/settings.cpp +++ b/src/ui_qt/settings/settings.cpp @@ -13,75 +13,24 @@ SettingsWindow *settingsWindow; SettingsWindow::SettingsWindow() : QbWindow(config().geometry.settingsWindow) { setObjectName("settings-window"); setWindowTitle("Configuration Settings"); - resize(625, 360); + resize(600, 360); - layout = new QHBoxLayout; + layout = new QVBoxLayout; layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - splitter = new QSplitter; - layout->addWidget(splitter); - - video = new QTreeWidgetItem(QStringList() << "Video"); - audio = new QTreeWidgetItem(QStringList() << "Audio"); - input = new QTreeWidgetItem(QStringList() << "Input"); - paths = new QTreeWidgetItem(QStringList() << "Paths"); - advanced = new QTreeWidgetItem(QStringList() << "Advanced"); - - //use QTreeWidget instead of QListWidget, as only the former has setAllColumnsShowFocus() - //this is needed to have the dotted-selection rectangle encompass the icons - list = new QTreeWidget; - list->addTopLevelItem(video); - list->addTopLevelItem(audio); - list->addTopLevelItem(input); - list->addTopLevelItem(paths); - list->addTopLevelItem(advanced); - list->setCurrentItem(input); //select most frequently used panel by default - list->setHeaderHidden(true); - list->setRootIsDecorated(false); - list->setAllColumnsShowFocus(true); - list->setIconSize(QSize(22, 22)); - - video->setIcon(0, QIcon(":/22x22/video-display.png")); - audio->setIcon(0, QIcon(":/22x22/audio-volume-high.png")); - input->setIcon(0, QIcon(":/22x22/input-gaming.png")); - paths->setIcon(0, QIcon(":/22x22/folder.png")); - advanced->setIcon(0, QIcon(":/22x22/preferences-system.png")); - - panel = new QWidget; - panel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - splitter->addWidget(list); - splitter->addWidget(panel); - splitter->setStretchFactor(0, 2); - splitter->setStretchFactor(1, 5); - videoSettingsWindow = new VideoSettingsWindow; audioSettingsWindow = new AudioSettingsWindow; inputSettingsWindow = new InputSettingsWindow; pathSettingsWindow = new PathSettingsWindow; advancedSettingsWindow = new AdvancedSettingsWindow; - panelLayout = new QStackedLayout(panel); - panelLayout->addWidget(videoSettingsWindow); - panelLayout->addWidget(audioSettingsWindow); - panelLayout->addWidget(inputSettingsWindow); - panelLayout->addWidget(pathSettingsWindow); - panelLayout->addWidget(advancedSettingsWindow); - panel->setLayout(panelLayout); - - connect(list, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(itemChanged())); - - itemChanged(); -} - -void SettingsWindow::itemChanged() { - QTreeWidgetItem *item = list->currentItem(); - - if(item == video) panelLayout->setCurrentWidget(videoSettingsWindow); - if(item == audio) panelLayout->setCurrentWidget(audioSettingsWindow); - if(item == input) panelLayout->setCurrentWidget(inputSettingsWindow); - if(item == paths) panelLayout->setCurrentWidget(pathSettingsWindow); - if(item == advanced) panelLayout->setCurrentWidget(advancedSettingsWindow); + tab = new QTabWidget; + tab->addTab(videoSettingsWindow, QIcon(":/16x16/video-display.png"), "Video"); + tab->addTab(audioSettingsWindow, QIcon(":/16x16/audio-volume-high.png"), "Audio"); + tab->addTab(inputSettingsWindow, QIcon(":/16x16/input-gaming.png"), "Input"); + tab->addTab(pathSettingsWindow, QIcon(":/16x16/folder.png"), "Paths"); + tab->addTab(advancedSettingsWindow, QIcon(":/16x16/preferences-system.png"), "Advanced"); + layout->addWidget(tab); } diff --git a/src/ui_qt/settings/settings.moc.hpp b/src/ui_qt/settings/settings.moc.hpp index fcaae68f..1d457041 100644 --- a/src/ui_qt/settings/settings.moc.hpp +++ b/src/ui_qt/settings/settings.moc.hpp @@ -2,21 +2,12 @@ class SettingsWindow : public QbWindow { Q_OBJECT public: - QHBoxLayout *layout; - QSplitter *splitter; - QTreeWidget *list; - QTreeWidgetItem *video; - QTreeWidgetItem *audio; - QTreeWidgetItem *input; - QTreeWidgetItem *paths; - QTreeWidgetItem *advanced; - QWidget *panel; - QStackedLayout *panelLayout; + QVBoxLayout *layout; + QTabWidget *tab; SettingsWindow(); public slots: - void itemChanged(); }; extern SettingsWindow *settingsWindow; diff --git a/src/ui_qt/settings/video.cpp b/src/ui_qt/settings/video.cpp index 0659325f..e60a28e8 100644 --- a/src/ui_qt/settings/video.cpp +++ b/src/ui_qt/settings/video.cpp @@ -3,15 +3,11 @@ VideoSettingsWindow *videoSettingsWindow; VideoSettingsWindow::VideoSettingsWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); layout->setAlignment(Qt::AlignTop); setLayout(layout); - title = new QLabel("Video Settings"); - title->setProperty("class", "title"); - layout->addWidget(title); - sliders = new QGridLayout; layout->addLayout(sliders); diff --git a/src/ui_qt/settings/video.moc.hpp b/src/ui_qt/settings/video.moc.hpp index a425fe63..2343bccc 100644 --- a/src/ui_qt/settings/video.moc.hpp +++ b/src/ui_qt/settings/video.moc.hpp @@ -3,7 +3,6 @@ class VideoSettingsWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; QGridLayout *sliders; QLabel *contrastLabel; QLabel *contrastValue; diff --git a/src/ui_qt/state/state.cpp b/src/ui_qt/state/state.cpp index c9a0b737..a35c9141 100644 --- a/src/ui_qt/state/state.cpp +++ b/src/ui_qt/state/state.cpp @@ -94,13 +94,11 @@ State::~State() { bool State::allowed() const { if(!SNES::cartridge.loaded() || !application.power) return false; if(movie.state != Movie::Inactive) return false; - return utility.saveStatesSupported(); + return cartridge.saveStatesSupported(); } string State::name(unsigned slot) const { - string name = config().path.state; - if(name == "") name = dir(utility.cartridge.fileName); - name << nall::basename(notdir(utility.cartridge.fileName)); + string name = filepath(nall::basename(cartridge.fileName), config().path.state); name << "-" << (slot + 1) << ".bst"; return name; } diff --git a/src/ui_qt/tools/cheateditor.cpp b/src/ui_qt/tools/cheateditor.cpp index 3e2ac563..d0ffd24e 100644 --- a/src/ui_qt/tools/cheateditor.cpp +++ b/src/ui_qt/tools/cheateditor.cpp @@ -3,216 +3,209 @@ CheatEditorWindow *cheatEditorWindow; CheatEditorWindow::CheatEditorWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - title = new QLabel("Cheat Code Editor"); - title->setProperty("class", "title"); - layout->addWidget(title); - list = new QTreeWidget; list->setColumnCount(3); list->setHeaderLabels(QStringList() << "Slot" << "Code" << "Description"); + list->setColumnWidth(1, list->fontMetrics().width(" 89AB-CDEF+... ")); list->setAllColumnsShowFocus(true); list->sortByColumn(0, Qt::AscendingOrder); list->setRootIsDecorated(false); - list->setContextMenuPolicy(Qt::CustomContextMenu); + list->setSelectionMode(QAbstractItemView::ExtendedSelection); + list->resizeColumnToContents(0); layout->addWidget(list); - controlLayout = new QGridLayout; - layout->addLayout(controlLayout); - - descLabel = new QLabel("Description:"); - descLabel->setAlignment(Qt::AlignRight); - controlLayout->addWidget(descLabel, 0, 0); - - descEdit = new QLineEdit; - controlLayout->addWidget(descEdit, 0, 1); + gridLayout = new QGridLayout; + layout->addLayout(gridLayout); codeLabel = new QLabel("Code(s):"); - codeLabel->setAlignment(Qt::AlignRight); - controlLayout->addWidget(codeLabel, 1, 0); + codeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(codeLabel, 0, 0); codeEdit = new QLineEdit; - controlLayout->addWidget(codeEdit, 1, 1); + gridLayout->addWidget(codeEdit, 0, 1); - buttonLayout = new QHBoxLayout; - layout->addLayout(buttonLayout); + descLabel = new QLabel("Description:"); + descLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(descLabel, 1, 0); - addCode = new QPushButton("Add Slot"); - buttonLayout->addWidget(addCode); + descEdit = new QLineEdit; + gridLayout->addWidget(descEdit, 1, 1); - deleteCode = new QPushButton("Delete Slot"); - buttonLayout->addWidget(deleteCode); + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); - reloadList(); + clearButton = new QPushButton("Clear Selected"); + controlLayout->addWidget(clearButton); - menu = new QMenu(list); - deleteCodeItem = menu->addAction("&Delete Selected Code"); - addCodeItem = menu->addAction("&Add New Code"); + synchronize(); - connect(list, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(popupMenu(const QPoint&))); - connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(updateCodeStatus())); connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); - connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(toggleCodeStatus())); - connect(descEdit, SIGNAL(textEdited(const QString&)), this, SLOT(textEdited())); - connect(codeEdit, SIGNAL(textEdited(const QString&)), this, SLOT(textEdited())); - connect(addCode, SIGNAL(released()), this, SLOT(addNewCode())); - connect(deleteCode, SIGNAL(released()), this, SLOT(deleteSelectedCode())); - - connect(addCodeItem, SIGNAL(triggered()), this, SLOT(addNewCode())); - connect(deleteCodeItem, SIGNAL(triggered()), this, SLOT(deleteSelectedCode())); + connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(bind())); + connect(codeEdit, SIGNAL(textEdited(const QString&)), this, SLOT(codeEdited())); + connect(descEdit, SIGNAL(textEdited(const QString&)), this, SLOT(descEdited())); + connect(clearButton, SIGNAL(released()), this, SLOT(clearSelected())); } -void CheatEditorWindow::syncUi() { +void CheatEditorWindow::synchronize() { QList items = list->selectedItems(); - descEdit->setEnabled(items.count() > 0); - codeEdit->setEnabled(items.count() > 0); - if(descEdit->isEnabled() == false) descEdit->setText(""); - if(codeEdit->isEnabled() == false) codeEdit->setText(""); - for(unsigned n = 0; n <= 2; n++) list->resizeColumnToContents(n); - - bool badCode = false; - string scode = codeEdit->text().toUtf8().data(); - if(scode.length() > 0) { - SNES::Cheat::cheat_t code; - if(SNES::cheat.decode(scode, code) == false) badCode = true; - } - - if(badCode == false) { - codeEdit->setStyleSheet(""); + if(items.count() == 1) { + descEdit->setEnabled(true); + codeEdit->setEnabled(true); } else { - codeEdit->setStyleSheet("background: #ffc0c0;"); + descEdit->setText(""); + codeEdit->setText(""); + descEdit->setEnabled(false); + codeEdit->setEnabled(false); } - - addCode->setEnabled(SNES::cartridge.loaded() == true); - deleteCode->setEnabled(list->selectedItems().count() == 1); + clearButton->setEnabled(items.count() > 0); } -void CheatEditorWindow::updateItem(QTreeWidgetItem *item) { - unsigned n = item->data(0, Qt::UserRole).toUInt(); - SNES::Cheat::cheat_t code, temp; - SNES::cheat.get(n, code); - - if(SNES::cheat.decode(code.code, temp) == false) { - item->setForeground(1, QBrush(QColor(224, 0, 0))); - } else { - lstring part; - part.split("+", code.code); - if(part.size() > 1) { - item->setForeground(1, QBrush(QColor(0, 128, 0))); - } else { - item->setForeground(1, QBrush(QColor(0, 0, 128))); - } - } - - string scode = code.code; - scode.replace(" ", ""); - lstring lcode; - lcode.split("+", scode); - if(lcode.size() > 1) lcode[0] << "+" << lcode.size() - 1; - - item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked); - item->setText(1, lcode[0]); - item->setText(2, code.desc); -} - -void CheatEditorWindow::popupMenu(const QPoint &point) { - if(SNES::cartridge.loaded() == false) return; - - QTreeWidgetItem *item = list->itemAt(point); - if(item) list->setCurrentItem(item); - deleteCodeItem->setVisible(item); - menu->popup(QCursor::pos()); -} - -void CheatEditorWindow::reloadList() { +void CheatEditorWindow::load(const char *filename) { list->clear(); list->setSortingEnabled(false); + SNES::cheat.reset(); - if(SNES::cartridge.loaded()) { - for(unsigned n = 0; n < SNES::cheat.count(); n++) { - QTreeWidgetItem *item = new QTreeWidgetItem(list); - item->setData(0, Qt::UserRole, QVariant(n)); - char slot[16]; - sprintf(slot, "%3u", n + 1); - item->setText(0, slot); - updateItem(item); + string data; + lstring line; + + if(data.readfile(filename)) { + data.replace("\r", ""); + line.split("\n", data); + } + + for(unsigned i = 0; i < 128; i++) { + lstring part; + if(line.size() > i) part.qsplit(",", line[i]); + for(unsigned n = 0; n <= 2; n++) trim(part[n], " "); + trim(part[2], "\""); + part[2].replace("\\q", "\""); + + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(i)); + item->setText(0, string::printf("%3u", i + 1)); + item->setCheckState(0, part[0] == "enabled" ? Qt::Checked : Qt::Unchecked); + item->setText(1, part[1]); + item->setText(2, part[2]); + } + + list->resizeColumnToContents(0); + list->setSortingEnabled(true); + list->header()->setSortIndicatorShown(false); + + bind(); + update(); +} + +void CheatEditorWindow::save(const char *filename) { + bool empty = true; + string data[128]; + + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + unsigned index = item->data(0, Qt::UserRole).toUInt(); + string code = item->text(1).toUtf8().constData(); + string desc = item->text(2).toUtf8().constData(); + desc.replace("\"", "\\q"); + if((code != "") || (desc != "")) empty = false; + + data[index] << (item->checkState(0) == Qt::Checked ? "enabled," : "disabled,"); + data[index] << code << ","; + data[index] << "\"" << desc << "\"\r\n"; + } + + if(empty == true) { + unlink(filename); + } else { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < 128; i++) fp.print(data[i]); + fp.close(); } } - list->setSortingEnabled(true); - list->header()->setSortIndicatorShown(false); - syncUi(); + list->clear(); + SNES::cheat.reset(); + SNES::cheat.synchronize(); +} + +void CheatEditorWindow::update() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + string code = item->text(1).toUtf8().constData(); + string desc = item->text(2).toUtf8().constData(); + if((code != "") || (desc != "")) { + item->setForeground(0, QBrush(QColor(0, 0, 0))); + } else { + //highlight empty slots in gray + item->setForeground(0, QBrush(QColor(128, 128, 128))); + } + unsigned index = item->data(0, Qt::UserRole).toUInt(); + if(SNES::cheat[index].addr.size() > 0) { + item->setForeground(1, QBrush(QColor(0, 0, 0))); + } else { + //highlight invalid codes in red + //(this will also highlight empty codes, but as there is no text, it's not an issue) + item->setForeground(1, QBrush(QColor(255, 0, 0))); + } + } +} + +void CheatEditorWindow::bind() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + unsigned index = item->data(0, Qt::UserRole).toUInt(); + SNES::cheat[index] = item->text(1).toUtf8().constData(); + SNES::cheat[index].enabled = item->checkState(0) == Qt::Checked; + } + SNES::cheat.synchronize(); } void CheatEditorWindow::listChanged() { QList items = list->selectedItems(); if(items.count() > 0) { QTreeWidgetItem *item = items[0]; - unsigned n = item->data(0, Qt::UserRole).toUInt(); - - SNES::Cheat::cheat_t code; - SNES::cheat.get(n, code); - - descEdit->setText(code.desc); - codeEdit->setText(code.code); + codeEdit->setText(item->text(1)); + descEdit->setText(item->text(2)); } - - syncUi(); + synchronize(); } -void CheatEditorWindow::textEdited() { +void CheatEditorWindow::codeEdited() { QList items = list->selectedItems(); - if(items.count() > 0) { + if(items.count() == 1) { QTreeWidgetItem *item = items[0]; - unsigned n = item->data(0, Qt::UserRole).toUInt(); - - string scode = codeEdit->text().toUtf8().data(); - string sdesc = descEdit->text().toUtf8().data(); - - SNES::Cheat::cheat_t code; - SNES::cheat.get(n, code); - SNES::cheat.edit(n, code.enabled, scode, sdesc); - - updateItem(item); - syncUi(); + item->setText(1, codeEdit->text()); } + bind(); + update(); } -//user ticked checkbox, set code enable state to checkbox state -void CheatEditorWindow::updateCodeStatus() { +void CheatEditorWindow::descEdited() { QList items = list->selectedItems(); - if(items.count() > 0) { + if(items.count() == 1) { QTreeWidgetItem *item = items[0]; - unsigned n = item->data(0, Qt::UserRole).toUInt(); - item->checkState(1) == Qt::Checked ? SNES::cheat.enable(n) : SNES::cheat.disable(n); + item->setText(2, descEdit->text()); } + update(); } -//user double-clicked line item, toggle current code enable state -void CheatEditorWindow::toggleCodeStatus() { +void CheatEditorWindow::clearSelected() { QList items = list->selectedItems(); - if(items.count() > 0) { - QTreeWidgetItem *item = items[0]; - unsigned n = item->data(0, Qt::UserRole).toUInt(); - SNES::cheat.enabled(n) == false ? SNES::cheat.enable(n) : SNES::cheat.disable(n); - item->setCheckState(1, SNES::cheat.enabled(n) ? Qt::Checked : Qt::Unchecked); - } -} - -void CheatEditorWindow::addNewCode() { - SNES::cheat.add(false, "", ""); - reloadList(); -} - -void CheatEditorWindow::deleteSelectedCode() { - QList items = list->selectedItems(); - if(items.count() > 0) { - QTreeWidgetItem *item = items[0]; - unsigned n = item->data(0, Qt::UserRole).toUInt(); - SNES::cheat.remove(n); - reloadList(); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + item->setText(1, ""); + item->setText(2, ""); } + codeEdit->setText(""); + descEdit->setText(""); + bind(); + update(); } diff --git a/src/ui_qt/tools/cheateditor.moc.hpp b/src/ui_qt/tools/cheateditor.moc.hpp index 9f684a1f..61ce1109 100644 --- a/src/ui_qt/tools/cheateditor.moc.hpp +++ b/src/ui_qt/tools/cheateditor.moc.hpp @@ -3,34 +3,28 @@ class CheatEditorWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; QTreeWidget *list; - QGridLayout *controlLayout; - QLabel *descLabel; - QLineEdit *descEdit; + QGridLayout *gridLayout; QLabel *codeLabel; QLineEdit *codeEdit; - QHBoxLayout *buttonLayout; - QPushButton *addCode; - QPushButton *deleteCode; + QLabel *descLabel; + QLineEdit *descEdit; + QHBoxLayout *controlLayout; + QPushButton *clearButton; - QMenu *menu; - QAction *deleteCodeItem; - QAction *addCodeItem; + void load(const char *filename); + void save(const char *filename); + void update(); - void syncUi(); - void updateItem(QTreeWidgetItem*); + void synchronize(); CheatEditorWindow(); -public slots: - void popupMenu(const QPoint&); - void reloadList(); +private slots: + void bind(); void listChanged(); - void textEdited(); - void updateCodeStatus(); - void toggleCodeStatus(); - void addNewCode(); - void deleteSelectedCode(); + void codeEdited(); + void descEdited(); + void clearSelected(); }; extern CheatEditorWindow *cheatEditorWindow; diff --git a/src/ui_qt/tools/cheatfinder.cpp b/src/ui_qt/tools/cheatfinder.cpp index a08d2532..03a4d7ab 100644 --- a/src/ui_qt/tools/cheatfinder.cpp +++ b/src/ui_qt/tools/cheatfinder.cpp @@ -3,14 +3,10 @@ CheatFinderWindow *cheatFinderWindow; CheatFinderWindow::CheatFinderWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - title = new QLabel("Cheat Code Finder"); - title->setProperty("class", "title"); - layout->addWidget(title); - list = new QTreeWidget; list->setColumnCount(3); list->setHeaderLabels(QStringList() << "Address" << "Current Value" << "Previous Value"); @@ -20,10 +16,10 @@ CheatFinderWindow::CheatFinderWindow() { layout->addWidget(list); controlLayout = new QGridLayout; - controlLayout->setSpacing(0); + controlLayout->setVerticalSpacing(0); layout->addLayout(controlLayout); - sizeLabel = new QLabel("Data size: "); + sizeLabel = new QLabel("Data size:"); controlLayout->addWidget(sizeLabel, 0, 0); sizeGroup = new QButtonGroup(this); @@ -45,7 +41,7 @@ CheatFinderWindow::CheatFinderWindow() { sizeGroup->addButton(size32bit); controlLayout->addWidget(size32bit, 0, 4); - compareLabel = new QLabel("Compare mode: "); + compareLabel = new QLabel("Compare mode:"); controlLayout->addWidget(compareLabel, 1, 0); compareGroup = new QButtonGroup(this); @@ -67,7 +63,7 @@ CheatFinderWindow::CheatFinderWindow() { compareGroup->addButton(compareGreaterThan); controlLayout->addWidget(compareGreaterThan, 1, 4); - valueLabel = new QLabel("Search value: "); + valueLabel = new QLabel("Search value:"); controlLayout->addWidget(valueLabel, 2, 0); actionLayout = new QHBoxLayout; diff --git a/src/ui_qt/tools/cheatfinder.moc.hpp b/src/ui_qt/tools/cheatfinder.moc.hpp index 8d11730c..69222cec 100644 --- a/src/ui_qt/tools/cheatfinder.moc.hpp +++ b/src/ui_qt/tools/cheatfinder.moc.hpp @@ -3,7 +3,6 @@ class CheatFinderWindow : public QWidget { public: QVBoxLayout *layout; - QLabel *title; QTreeWidget *list; QGridLayout *controlLayout; QLabel *sizeLabel; diff --git a/src/ui_qt/tools/layertoggle.cpp b/src/ui_qt/tools/layertoggle.cpp deleted file mode 100644 index 6db59605..00000000 --- a/src/ui_qt/tools/layertoggle.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "layertoggle.moc" -LayerToggleWindow *layerToggleWindow; - -LayerToggleWindow::LayerToggleWindow() { - layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(Style::WidgetSpacing); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - - title = new QLabel("Layer Toggle Utility"); - title->setProperty("class", "title"); - layout->addWidget(title); - - gridLayout = new QGridLayout; - gridLayout->setVerticalSpacing(0); - layout->addLayout(gridLayout); - - bg1Label = new QLabel("BG1:"); - gridLayout->addWidget(bg1Label, 0, 0); - - bg1pri0 = new QCheckBox("Priority 0"); - bg1pri0->setChecked(true); - gridLayout->addWidget(bg1pri0, 0, 1); - - bg1pri1 = new QCheckBox("Priority 1"); - bg1pri1->setChecked(true); - gridLayout->addWidget(bg1pri1, 0, 2); - - bg2Label = new QLabel("BG2:"); - gridLayout->addWidget(bg2Label, 1, 0); - - bg2pri0 = new QCheckBox("Priority 0"); - bg2pri0->setChecked(true); - gridLayout->addWidget(bg2pri0, 1, 1); - - bg2pri1 = new QCheckBox("Priority 1"); - bg2pri1->setChecked(true); - gridLayout->addWidget(bg2pri1, 1, 2); - - bg3Label = new QLabel("BG3:"); - gridLayout->addWidget(bg3Label, 2, 0); - - bg3pri0 = new QCheckBox("Priority 0"); - bg3pri0->setChecked(true); - gridLayout->addWidget(bg3pri0, 2, 1); - - bg3pri1 = new QCheckBox("Priority 1"); - bg3pri1->setChecked(true); - gridLayout->addWidget(bg3pri1, 2, 2); - - bg4Label = new QLabel("BG4:"); - gridLayout->addWidget(bg4Label, 3, 0); - - bg4pri0 = new QCheckBox("Priority 0"); - bg4pri0->setChecked(true); - gridLayout->addWidget(bg4pri0, 3, 1); - - bg4pri1 = new QCheckBox("Priority 1"); - bg4pri1->setChecked(true); - gridLayout->addWidget(bg4pri1, 3, 2); - - oamLabel = new QLabel("OAM:"); - gridLayout->addWidget(oamLabel, 4, 0); - - oampri0 = new QCheckBox("Priority 0"); - oampri0->setChecked(true); - gridLayout->addWidget(oampri0, 4, 1); - - oampri1 = new QCheckBox("Priority 1"); - oampri1->setChecked(true); - gridLayout->addWidget(oampri1, 4, 2); - - oampri2 = new QCheckBox("Priority 2"); - oampri2->setChecked(true); - gridLayout->addWidget(oampri2, 4, 3); - - oampri3 = new QCheckBox("Priority 3"); - oampri3->setChecked(true); - gridLayout->addWidget(oampri3, 4, 4); - - spacer = new QWidget; - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - gridLayout->addWidget(spacer, 4, 5); - - connect(bg1pri0, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg1pri1, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg2pri0, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg2pri1, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg3pri0, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg3pri1, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg4pri0, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(bg4pri1, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(oampri0, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(oampri1, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(oampri2, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); - connect(oampri3, SIGNAL(stateChanged(int)), this, SLOT(stateChanged())); -} - -void LayerToggleWindow::stateChanged() { - if(sender() == bg1pri0) SNES::config.ppu.bg1_enabled[0] = bg1pri0->isChecked(); - if(sender() == bg1pri1) SNES::config.ppu.bg1_enabled[1] = bg1pri1->isChecked(); - if(sender() == bg2pri0) SNES::config.ppu.bg2_enabled[0] = bg2pri0->isChecked(); - if(sender() == bg2pri1) SNES::config.ppu.bg2_enabled[1] = bg2pri1->isChecked(); - if(sender() == bg3pri0) SNES::config.ppu.bg3_enabled[0] = bg3pri0->isChecked(); - if(sender() == bg3pri1) SNES::config.ppu.bg3_enabled[1] = bg3pri1->isChecked(); - if(sender() == bg4pri0) SNES::config.ppu.bg4_enabled[0] = bg4pri0->isChecked(); - if(sender() == bg4pri1) SNES::config.ppu.bg4_enabled[1] = bg4pri1->isChecked(); - if(sender() == oampri0) SNES::config.ppu.oam_enabled[0] = oampri0->isChecked(); - if(sender() == oampri1) SNES::config.ppu.oam_enabled[1] = oampri1->isChecked(); - if(sender() == oampri2) SNES::config.ppu.oam_enabled[2] = oampri2->isChecked(); - if(sender() == oampri3) SNES::config.ppu.oam_enabled[3] = oampri3->isChecked(); -} diff --git a/src/ui_qt/tools/statemanager.cpp b/src/ui_qt/tools/statemanager.cpp index 200f6c80..66d99fd7 100644 --- a/src/ui_qt/tools/statemanager.cpp +++ b/src/ui_qt/tools/statemanager.cpp @@ -3,20 +3,17 @@ StateManagerWindow *stateManagerWindow; StateManagerWindow::StateManagerWindow() { layout = new QVBoxLayout; - layout->setMargin(0); + layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - title = new QLabel("Save State Manager"); - title->setProperty("class", "title"); - layout->addWidget(title); - list = new QTreeWidget; list->setColumnCount(2); list->setHeaderLabels(QStringList() << "Slot" << "Description"); list->setAllColumnsShowFocus(true); list->sortByColumn(0, Qt::AscendingOrder); list->setRootIsDecorated(false); + list->resizeColumnToContents(0); layout->addWidget(list); infoLayout = new QHBoxLayout; @@ -31,6 +28,10 @@ StateManagerWindow::StateManagerWindow() { controlLayout = new QHBoxLayout; layout->addLayout(controlLayout); + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + controlLayout->addWidget(spacer); + loadButton = new QPushButton("Load"); controlLayout->addWidget(loadButton); @@ -54,7 +55,7 @@ void StateManagerWindow::reload() { list->clear(); list->setSortingEnabled(false); - if(SNES::cartridge.loaded()) { + if(SNES::cartridge.loaded() && cartridge.saveStatesSupported()) { for(unsigned n = 0; n < StateCount; n++) { QTreeWidgetItem *item = new QTreeWidgetItem(list); item->setData(0, Qt::UserRole, QVariant(n)); @@ -161,9 +162,7 @@ void StateManagerWindow::eraseAction() { } string StateManagerWindow::filename() const { - string name = config().path.state; - if(name == "") name = dir(utility.cartridge.fileName); - name << nall::basename(notdir(utility.cartridge.fileName)); + string name = filepath(nall::basename(cartridge.fileName), config().path.state); name << ".bsa"; return name; } diff --git a/src/ui_qt/tools/statemanager.moc.hpp b/src/ui_qt/tools/statemanager.moc.hpp index 0c95895a..be02653e 100644 --- a/src/ui_qt/tools/statemanager.moc.hpp +++ b/src/ui_qt/tools/statemanager.moc.hpp @@ -5,12 +5,12 @@ public: enum { StateCount = 32 }; QVBoxLayout *layout; - QLabel *title; QTreeWidget *list; QHBoxLayout *infoLayout; QLabel *descriptionLabel; QLineEdit *descriptionText; QHBoxLayout *controlLayout; + QWidget *spacer; QPushButton *loadButton; QPushButton *saveButton; QPushButton *eraseButton; diff --git a/src/ui_qt/tools/tools.cpp b/src/ui_qt/tools/tools.cpp index 53dcf943..f99bbabb 100644 --- a/src/ui_qt/tools/tools.cpp +++ b/src/ui_qt/tools/tools.cpp @@ -6,67 +6,24 @@ ToolsWindow *toolsWindow; #include "cheateditor.cpp" #include "cheatfinder.cpp" #include "statemanager.cpp" -#include "layertoggle.cpp" ToolsWindow::ToolsWindow() : QbWindow(config().geometry.toolsWindow) { setObjectName("tools-window"); setWindowTitle("Tools"); - resize(625, 360); + resize(600, 360); - layout = new QHBoxLayout; + layout = new QVBoxLayout; layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); - splitter = new QSplitter; - layout->addWidget(splitter); - - list = new QTreeWidget; - list->addTopLevelItem(cheatEditor = new QTreeWidgetItem(QStringList() << "Cheat Editor")); - list->addTopLevelItem(cheatFinder = new QTreeWidgetItem(QStringList() << "Cheat Finder")); - list->addTopLevelItem(stateManager = new QTreeWidgetItem(QStringList() << "State Manager")); - list->addTopLevelItem(layerToggle = new QTreeWidgetItem(QStringList() << "Layer Toggle")); - list->setCurrentItem(cheatEditor); - list->setHeaderHidden(true); - list->setRootIsDecorated(false); - list->setAllColumnsShowFocus(true); - list->setIconSize(QSize(22, 22)); - - cheatEditor->setIcon(0, QIcon(":/22x22/accessories-text-editor.png")); - cheatFinder->setIcon(0, QIcon(":/22x22/system-search.png")); - stateManager->setIcon(0, QIcon(":/22x22/system-file-manager.png")); - layerToggle->setIcon(0, QIcon(":/22x22/image-x-generic.png")); - - panel = new QWidget; - panel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - splitter->addWidget(list); - splitter->addWidget(panel); - splitter->setStretchFactor(0, 2); - splitter->setStretchFactor(1, 5); - cheatEditorWindow = new CheatEditorWindow; cheatFinderWindow = new CheatFinderWindow; stateManagerWindow = new StateManagerWindow; - layerToggleWindow = new LayerToggleWindow; - panelLayout = new QStackedLayout(panel); - panelLayout->addWidget(cheatEditorWindow); - panelLayout->addWidget(cheatFinderWindow); - panelLayout->addWidget(stateManagerWindow); - panelLayout->addWidget(layerToggleWindow); - panel->setLayout(panelLayout); - - connect(list, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(itemChanged())); - - itemChanged(); -} - -void ToolsWindow::itemChanged() { - QTreeWidgetItem *item = list->currentItem(); - - if(item == cheatEditor) panelLayout->setCurrentWidget(cheatEditorWindow); - if(item == cheatFinder) panelLayout->setCurrentWidget(cheatFinderWindow); - if(item == stateManager) panelLayout->setCurrentWidget(stateManagerWindow); - if(item == layerToggle) panelLayout->setCurrentWidget(layerToggleWindow); + tab = new QTabWidget; + tab->addTab(cheatEditorWindow, QIcon(":/16x16/accessories-text-editor.png"), "Cheat Editor"); + tab->addTab(cheatFinderWindow, QIcon(":/16x16/system-search.png"), "Cheat Finder"); + tab->addTab(stateManagerWindow, QIcon(":/16x16/system-file-manager.png"), "State Manager"); + layout->addWidget(tab); } diff --git a/src/ui_qt/tools/tools.moc.hpp b/src/ui_qt/tools/tools.moc.hpp index dbaca879..80a316ba 100644 --- a/src/ui_qt/tools/tools.moc.hpp +++ b/src/ui_qt/tools/tools.moc.hpp @@ -2,20 +2,12 @@ class ToolsWindow : public QbWindow { Q_OBJECT public: - QHBoxLayout *layout; - QSplitter *splitter; - QTreeWidget *list; - QTreeWidgetItem *cheatEditor; - QTreeWidgetItem *cheatFinder; - QTreeWidgetItem *stateManager; - QTreeWidgetItem *layerToggle; - QWidget *panel; - QStackedLayout *panelLayout; + QVBoxLayout *layout; + QTabWidget *tab; ToolsWindow(); public slots: - void itemChanged(); }; extern ToolsWindow *toolsWindow; diff --git a/src/ui_qt/ui-base.hpp b/src/ui_qt/ui-base.hpp index b5487d2f..fb404606 100644 --- a/src/ui_qt/ui-base.hpp +++ b/src/ui_qt/ui-base.hpp @@ -31,14 +31,24 @@ using namespace ruby; #include "base/loader.moc.hpp" #include "base/main.moc.hpp" +#include "cartridge/cartridge.hpp" + #if defined(DEBUGGER) #include "debugger/debugger.moc.hpp" - #include "debugger/disassembler.moc.hpp" - #include "debugger/breakpoint.moc.hpp" #include "debugger/hexeditor.moc.hpp" - #include "debugger/memory.moc.hpp" - #include "debugger/vramviewer.moc.hpp" #include "debugger/tracer.moc.hpp" + + #include "debugger/tools/disassembler.moc.hpp" + #include "debugger/tools/breakpoint.moc.hpp" + #include "debugger/tools/memory.moc.hpp" + #include "debugger/tools/properties.moc.hpp" + + #include "debugger/ppu/layer-toggle.moc.hpp" + #include "debugger/ppu/vram-viewer.moc.hpp" + #include "debugger/ppu/oam-viewer.moc.hpp" + #include "debugger/ppu/cgram-viewer.moc.hpp" + + #include "debugger/misc/debugger-options.moc.hpp" #endif #include "input/input.hpp" @@ -62,7 +72,6 @@ using namespace ruby; #include "tools/cheateditor.moc.hpp" #include "tools/cheatfinder.moc.hpp" #include "tools/statemanager.moc.hpp" -#include "tools/layertoggle.moc.hpp" #include "utility/utility.hpp" @@ -76,6 +85,8 @@ struct Style { }; }; +extern string filepath(const char *filename, const char *filepath); + #if !defined(PLATFORM_WIN) #define mkdir(path) (mkdir)(path, 0755) #endif diff --git a/src/ui_qt/utility/cartridge.cpp b/src/ui_qt/utility/cartridge.cpp deleted file mode 100644 index 3fe8709e..00000000 --- a/src/ui_qt/utility/cartridge.cpp +++ /dev/null @@ -1,365 +0,0 @@ -bool Utility::loadCartridgeNormal(const char *base) { - unloadCartridge(); - if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; - SNES::s21fx.base(dir(cartridge.baseName)); - SNES::cartridge.load(SNES::Cartridge::ModeNormal); - - loadMemory(cartridge.baseName, ".srm", SNES::memory::cartram); - loadMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); - - cartridge.fileName = cartridge.baseName; - cartridge.name = notdir(nall::basename(cartridge.baseName)); - - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeBsxSlotted(const char *base, const char *slot) { - unloadCartridge(); - if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; - loadCartridge(cartridge.slotAName = slot, SNES::memory::bsxflash); - SNES::cartridge.load(SNES::Cartridge::ModeBsxSlotted); - - loadMemory(cartridge.baseName, ".srm", SNES::memory::cartram); - loadMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); - - cartridge.fileName = cartridge.baseName; - cartridge.name = notdir(nall::basename(cartridge.baseName)); - if(*slot) cartridge.name << " + " << notdir(nall::basename(cartridge.slotAName)); - - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeBsx(const char *base, const char *slot) { - unloadCartridge(); - if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; - loadCartridge(cartridge.slotAName = slot, SNES::memory::bsxflash); - SNES::cartridge.load(SNES::Cartridge::ModeBsx); - - loadMemory(cartridge.baseName, ".srm", SNES::memory::bsxram ); - loadMemory(cartridge.baseName, ".psr", SNES::memory::bsxpram); - - cartridge.fileName = cartridge.slotAName; - cartridge.name = *slot - ? notdir(nall::basename(cartridge.slotAName)) - : notdir(nall::basename(cartridge.baseName)); - - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeSufamiTurbo(const char *base, const char *slotA, const char *slotB) { - unloadCartridge(); - if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; - loadCartridge(cartridge.slotAName = slotA, SNES::memory::stArom); - loadCartridge(cartridge.slotBName = slotB, SNES::memory::stBrom); - SNES::cartridge.load(SNES::Cartridge::ModeSufamiTurbo); - - loadMemory(cartridge.slotAName, ".srm", SNES::memory::stAram); - loadMemory(cartridge.slotBName, ".srm", SNES::memory::stBram); - - cartridge.fileName = cartridge.slotAName; - if(!*slotA && !*slotB) cartridge.name = notdir(nall::basename(cartridge.baseName)); - else if(!*slotB) cartridge.name = notdir(nall::basename(cartridge.slotAName)); - else if(!*slotA) cartridge.name = notdir(nall::basename(cartridge.slotBName)); - else cartridge.name = notdir(nall::basename(cartridge.slotAName)) << " + " << notdir(nall::basename(cartridge.slotBName)); - - modifySystemState(LoadCartridge); - return true; -} - -bool Utility::loadCartridgeSuperGameBoy(const char *base, const char *slot) { - unloadCartridge(); - if(loadCartridge(cartridge.baseName = base, SNES::memory::cartrom) == false) return false; - loadCartridge(cartridge.slotAName = slot, SNES::memory::gbrom); - SNES::cartridge.load(SNES::Cartridge::ModeSuperGameBoy); - - loadMemory(cartridge.slotAName, ".sav", SNES::memory::gbram); - loadMemory(cartridge.slotBName, ".rtc", SNES::memory::gbrtc); - - cartridge.fileName = cartridge.slotAName; - cartridge.name = *slot - ? notdir(nall::basename(cartridge.slotAName)) - : notdir(nall::basename(cartridge.baseName)); - - modifySystemState(LoadCartridge); - return true; -} - -void Utility::saveMemory() { - if(SNES::cartridge.loaded() == false) return; - - switch(SNES::cartridge.mode()) { - case SNES::Cartridge::ModeNormal: - case SNES::Cartridge::ModeBsxSlotted: { - saveMemory(cartridge.baseName, ".srm", SNES::memory::cartram); - saveMemory(cartridge.baseName, ".rtc", SNES::memory::cartrtc); - } break; - - case SNES::Cartridge::ModeBsx: { - saveMemory(cartridge.baseName, ".srm", SNES::memory::bsxram ); - saveMemory(cartridge.baseName, ".psr", SNES::memory::bsxpram); - } break; - - case SNES::Cartridge::ModeSufamiTurbo: { - saveMemory(cartridge.slotAName, ".srm", SNES::memory::stAram); - saveMemory(cartridge.slotBName, ".srm", SNES::memory::stBram); - } break; - - case SNES::Cartridge::ModeSuperGameBoy: { - saveMemory(cartridge.slotAName, ".sav", SNES::memory::gbram); - saveMemory(cartridge.slotAName, ".rtc", SNES::memory::gbrtc); - } break; - } -} - -void Utility::unloadCartridge() { - if(SNES::cartridge.loaded() == false) return; - modifySystemState(UnloadCartridge); -} - -void Utility::modifySystemState(system_state_t systemState) { - diskBrowser->hide(); //avoid edge case oddities (eg movie playback window still open from previous game) - state.resetHistory(); //do not allow rewinding past a destructive system action - movie.stop(); //movies cannot continue to record after destructive system actions - - video.clear(); - audio.clear(); - - switch(systemState) { - case LoadCartridge: { - //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) - if(SNES::cartridge.loaded() == false) break; - loadCheats(); - - application.power = true; - application.pause = false; - SNES::system.power(); - - //warn if unsupported hardware detected - string chip; - if(0); - else if(SNES::cartridge.has_dsp3()) chip = "DSP3"; - else if(SNES::cartridge.has_st011()) chip = "ST011"; - else if(SNES::cartridge.has_st018()) chip = "ST018"; - if(chip != "") { - QMessageBox::warning(mainWindow, "Warning", string() - << "

Warning:
The " << chip << " chip was detected, which is not fully emulated yet.
" - << "It is unlikely that this title will work properly.

"); - } - - showMessage(string() - << "Loaded " << cartridge.name - << (cartridge.patchApplied ? ", and applied UPS patch." : ".")); - mainWindow->setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion << " - " << cartridge.name); - #if defined(DEBUGGER) - debugger->echo(string() << "Loaded " << cartridge.name << ".
"); - #endif - } break; - - case UnloadCartridge: { - if(SNES::cartridge.loaded() == false) break; //no cart to unload? - saveCheats(); - - SNES::system.unload(); //flush all memory to memory::* devices - saveMemory(); //save memory to disk - SNES::cartridge.unload(); //deallocate memory - - application.power = false; - application.pause = true; - - showMessage(string() << "Unloaded " << cartridge.name << "."); - mainWindow->setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion); - } break; - - case PowerOn: { - if(SNES::cartridge.loaded() == false || application.power == true) break; - - application.power = true; - application.pause = false; - SNES::system.power(); - - showMessage("Power on."); - } break; - - case PowerOff: { - if(SNES::cartridge.loaded() == false || application.power == false) break; - - application.power = false; - application.pause = true; - - showMessage("Power off."); - } break; - - case PowerCycle: { - if(SNES::cartridge.loaded() == false) break; - - application.power = true; - application.pause = false; - SNES::system.power(); - - showMessage("System power was cycled."); - } break; - - case Reset: { - if(SNES::cartridge.loaded() == false || application.power == false) break; - - application.pause = false; - SNES::system.reset(); - - showMessage("System was reset."); - } break; - } - - mainWindow->syncUi(); - #if defined(DEBUGGER) - debugger->modifySystemState(systemState); - debugger->synchronize(); - #endif - cheatEditorWindow->reloadList(); - cheatFinderWindow->synchronize(); - stateManagerWindow->reload(); -} - -bool Utility::loadCartridge(string &filename, SNES::MappedRAM &memory) { - if(file::exists(filename) == false) return false; - - uint8_t *data; - unsigned size; - audio.clear(); - if(reader.load(filename, data, size) == false) return false; - - cartridge.patchApplied = false; - string name; - name << filepath(nall::basename(filename), config().path.patch); - name << ".ups"; - - file fp; - if(fp.open(name, file::mode_read) == true) { - unsigned patchsize = fp.size(); - uint8_t *patchdata = new uint8_t[patchsize]; - fp.read(patchdata, patchsize); - fp.close(); - - uint8_t *outdata = 0; - unsigned outsize = 0; - ups patcher; - ups::result result = patcher.apply(patchdata, patchsize, data, size, outdata, outsize); - delete[] patchdata; - - bool apply = false; - if(result == ups::ok) apply = true; - if(config().file.bypass_patch_crc32) { - if(result == ups::input_crc32_invalid ) apply = true; - if(result == ups::output_crc32_invalid) apply = true; - } - - if(apply == true) { - delete[] data; - data = outdata; - size = outsize; - cartridge.patchApplied = true; - } else { - delete[] outdata; - } - } - - memory.copy(data, size); - delete[] data; - return true; -} - -bool Utility::loadMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { - if(memory.size() == 0 || memory.size() == -1U) return false; - - string name; - name << filepath(nall::basename(filename), config().path.save); - name << extension; - - file fp; - if(fp.open(name, file::mode_read) == false) return false; - - unsigned size = fp.size(); - uint8_t *data = new uint8_t[size]; - fp.read(data, size); - fp.close(); - - memory.copy(data, size); - delete[] data; - return true; -} - -bool Utility::saveMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { - if(memory.size() == 0 || memory.size() == -1U) return false; - - string name; - name << filepath(nall::basename(filename), config().path.save); - name << extension; - - file fp; - if(fp.open(name, file::mode_write) == false) return false; - - fp.write(memory.data(), memory.size()); - fp.close(); - return true; -} - -void Utility::loadCheats() { - string name, data; - name << filepath(nall::basename(cartridge.baseName), config().path.cheat); - name << ".cht"; - - SNES::cheat.clear(); - if(data.readfile(name)) SNES::cheat.load(data); -} - -void Utility::saveCheats() { - string name; - name << filepath(nall::basename(cartridge.baseName), config().path.cheat); - name << ".cht"; - - file fp; - if(SNES::cheat.count() > 0 || file::exists(name)) { - if(fp.open(name, file::mode_write)) { - fp.print(SNES::cheat.save()); - fp.close(); - } - } -} - -bool Utility::saveStatesSupported() { - if(SNES::cartridge.mode() == SNES::Cartridge::ModeBsx) return false; - - if(SNES::cartridge.has_superfx()) return false; - if(SNES::cartridge.has_sa1()) return false; - if(SNES::cartridge.has_dsp3()) return false; - if(SNES::cartridge.has_dsp4()) return false; - if(SNES::cartridge.has_st011()) return false; - if(SNES::cartridge.has_st018()) return false; - - return true; -} - -//ensure file path is absolute (eg resolve relative paths) -string Utility::filepath(const char *filename, const char *pathname) { - //if no pathname, return filename as-is - string file(filename); - file.replace("\\", "/"); - - string path = (!pathname || !*pathname) ? (const char*)dir(filename) : pathname; - //ensure path ends with trailing '/' - path.replace("\\", "/"); - if(!strend(path, "/")) path.append("/"); - - //replace relative path with absolute path - if(strbegin(path, "./")) { - ltrim(path, "./"); - path = string() << config().path.base << path; - } - - //remove folder part of filename - lstring part; - part.split("/", file); - return path << part[part.size() - 1]; -} diff --git a/src/ui_qt/utility/system-state.cpp b/src/ui_qt/utility/system-state.cpp new file mode 100644 index 00000000..642c05de --- /dev/null +++ b/src/ui_qt/utility/system-state.cpp @@ -0,0 +1,102 @@ +void Utility::modifySystemState(system_state_t systemState) { + diskBrowser->hide(); //avoid edge case oddities (eg movie playback window still open from previous game) + state.resetHistory(); //do not allow rewinding past a destructive system action + movie.stop(); //movies cannot continue to record after destructive system actions + + video.clear(); + audio.clear(); + + switch(systemState) { + case LoadCartridge: { + //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) + if(SNES::cartridge.loaded() == false) break; + cartridge.loadCheats(); + + application.power = true; + application.pause = false; + SNES::system.power(); + + //warn if unsupported hardware detected + string chip; + if(0); + else if(SNES::cartridge.has_dsp3()) chip = "DSP3"; + else if(SNES::cartridge.has_st011()) chip = "ST011"; + else if(SNES::cartridge.has_st018()) chip = "ST018"; + if(chip != "") { + QMessageBox::warning(mainWindow, "Warning", string() + << "

Warning:
The " << chip << " chip was detected, which is not fully emulated yet.
" + << "It is unlikely that this title will work properly.

"); + } + + showMessage(string() + << "Loaded " << cartridge.name + << (cartridge.patchApplied ? ", and applied UPS patch." : ".")); + mainWindow->setWindowTitle(string() << cartridge.name << " - " << bsnesTitle << " v" << bsnesVersion); + #if defined(DEBUGGER) + debugger->echo(string() << "Loaded " << cartridge.name << ".
"); + #endif + } break; + + case UnloadCartridge: { + if(SNES::cartridge.loaded() == false) break; //no cart to unload? + cartridge.saveCheats(); + + SNES::system.unload(); //flush all memory to memory::* devices + cartridge.saveMemory(); //save memory to disk + SNES::cartridge.unload(); //deallocate memory + + application.power = false; + application.pause = true; + + showMessage(string() << "Unloaded " << cartridge.name << "."); + mainWindow->setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion); + } break; + + case PowerOn: { + if(SNES::cartridge.loaded() == false || application.power == true) break; + + application.power = true; + application.pause = false; + SNES::system.power(); + + showMessage("Power on."); + } break; + + case PowerOff: { + if(SNES::cartridge.loaded() == false || application.power == false) break; + + application.power = false; + application.pause = true; + + showMessage("Power off."); + } break; + + case PowerCycle: { + if(SNES::cartridge.loaded() == false) break; + + application.power = true; + application.pause = false; + SNES::system.power(); + + showMessage("System power was cycled."); + } break; + + case Reset: { + if(SNES::cartridge.loaded() == false || application.power == false) break; + + application.pause = false; + SNES::system.reset(); + + showMessage("System was reset."); + } break; + } + + mainWindow->syncUi(); + #if defined(DEBUGGER) + debugger->modifySystemState(systemState); + debugger->synchronize(); + #endif + cheatEditorWindow->synchronize(); + cheatFinderWindow->synchronize(); + stateManagerWindow->reload(); +} diff --git a/src/ui_qt/utility/utility.cpp b/src/ui_qt/utility/utility.cpp index 88f91796..4228d4ca 100644 --- a/src/ui_qt/utility/utility.cpp +++ b/src/ui_qt/utility/utility.cpp @@ -1,15 +1,15 @@ Utility utility; -#include "cartridge.cpp" +#include "system-state.cpp" #include "window.cpp" void Utility::inputEvent(uint16_t scancode) { + //release mouse capture if escape key is pressed on any keyboard for(unsigned i = 0; i < Keyboard::Count; i++) { if(scancode == keyboard(i)[Keyboard::Escape] && mapper().state(scancode)) { if(mainWindow->isActive() && input.acquired()) { - //release mouse capture input.unacquire(); - return; //do not trigger other UI actions that may be bound to escape key + return; } } } @@ -32,7 +32,7 @@ void Utility::updateSystemState() { text = "Paused"; } else if(SNES::ppu.status.frames_updated == true) { SNES::ppu.status.frames_updated = false; - text << (int)SNES::ppu.status.frames_executed; + text << SNES::ppu.status.frames_executed; text << " fps"; } else { //nothing to update diff --git a/src/ui_qt/utility/utility.hpp b/src/ui_qt/utility/utility.hpp index 221481be..3564f968 100644 --- a/src/ui_qt/utility/utility.hpp +++ b/src/ui_qt/utility/utility.hpp @@ -16,34 +16,10 @@ public: void updateEmulationSpeed(); void updateControllers(); - //cartridge.cpp - struct Cartridge { - string name; //printable cartridge name - string fileName; //ideal file name for saving eg states to disk - string baseName, slotAName, slotBName; - bool patchApplied; - } cartridge; - - bool loadCartridgeNormal(const char*); - bool loadCartridgeBsxSlotted(const char*, const char*); - bool loadCartridgeBsx(const char*, const char*); - bool loadCartridgeSufamiTurbo(const char*, const char *, const char*); - bool loadCartridgeSuperGameBoy(const char*, const char*); - void saveMemory(); - void unloadCartridge(); - + //system-state.cpp enum system_state_t { LoadCartridge, UnloadCartridge, PowerOn, PowerOff, PowerCycle, Reset }; void modifySystemState(system_state_t state); - bool loadCartridge(string&, SNES::MappedRAM&); - bool loadMemory(const char*, const char*, SNES::MappedRAM&); - bool saveMemory(const char*, const char*, SNES::MappedRAM&); - void loadCheats(); - void saveCheats(); - - bool saveStatesSupported(); - string filepath(const char *filename, const char *pathname); - //window.cpp void updateFullscreenState(); void constrainSize(unsigned &x, unsigned &y, unsigned max);