diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index dd3357aa..4f0b27f4 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.83"; + static const string Version = "106.84"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/emulator/memory/readable.hpp b/higan/emulator/memory/readable.hpp index 6b5b0215..211a8321 100644 --- a/higan/emulator/memory/readable.hpp +++ b/higan/emulator/memory/readable.hpp @@ -44,6 +44,14 @@ struct Readable { inline auto read(uint address) const -> T { return self.data[address & self.mask]; } inline auto write(uint address, T data) const -> void {} + auto serialize(serializer& s) -> void { + const uint size = self.size; + s.integer(self.size); + s.integer(self.mask); + if(self.size != size) allocate(self.size); + s.array(self.data, self.size); + } + private: struct { T* data = nullptr; diff --git a/higan/emulator/memory/writable.hpp b/higan/emulator/memory/writable.hpp index a2820a52..7810569a 100644 --- a/higan/emulator/memory/writable.hpp +++ b/higan/emulator/memory/writable.hpp @@ -46,6 +46,14 @@ struct Writable { inline auto read(uint address) const -> T { return self.data[address & self.mask]; } inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; } + auto serialize(serializer& s) -> void { + const uint size = self.size; + s.integer(self.size); + s.integer(self.mask); + if(self.size != size) allocate(self.size); + s.array(self.data, self.size); + } + private: struct { T* data = nullptr; diff --git a/higan/gba/cartridge/cartridge.cpp b/higan/gba/cartridge/cartridge.cpp index 022fe3e2..1cd1c3ca 100644 --- a/higan/gba/cartridge/cartridge.cpp +++ b/higan/gba/cartridge/cartridge.cpp @@ -24,7 +24,7 @@ Cartridge::~Cartridge() { } auto Cartridge::load() -> bool { - information = Information(); + information = {}; if(auto loaded = platform->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) { information.pathID = loaded.pathID; diff --git a/higan/ngp/GNUmakefile b/higan/ngp/GNUmakefile index 0e5ab8e1..17a56543 100644 --- a/higan/ngp/GNUmakefile +++ b/higan/ngp/GNUmakefile @@ -1,6 +1,12 @@ processors += tlcs900h z80 -objects += ngp-interface ngp-system +objects += ngp-interface ngp-system ngp-cartridge +objects += ngp-cpu ngp-apu ngp-vpu ngp-psg obj/ngp-interface.o: ngp/interface/interface.cpp obj/ngp-system.o: ngp/system/system.cpp +obj/ngp-cartridge.o: ngp/cartridge/cartridge.cpp +obj/ngp-cpu.o: ngp/cpu/cpu.cpp +obj/ngp-apu.o: ngp/apu/apu.cpp +obj/ngp-vpu.o: ngp/vpu/vpu.cpp +obj/ngp-psg.o: ngp/psg/psg.cpp diff --git a/higan/ngp/apu/apu.cpp b/higan/ngp/apu/apu.cpp new file mode 100644 index 00000000..11009c19 --- /dev/null +++ b/higan/ngp/apu/apu.cpp @@ -0,0 +1,33 @@ +#include + +namespace NeoGeoPocket { + +APU apu; +#include "serialization.cpp" + +auto APU::Enter() -> void { + while(true) scheduler.synchronize(), apu.main(); +} + +auto APU::main() -> void { + step(1); +} + +auto APU::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + +auto APU::synchronizing() const -> bool { + return scheduler.synchronizing(); +} + +auto APU::power() -> void { + Z80::bus = this; + Z80::power(); + bus->grant(false); + create(APU::Enter, system.frequency() / 2.0); + ram.allocate(0x1000); +} + +} diff --git a/higan/ngp/apu/apu.hpp b/higan/ngp/apu/apu.hpp new file mode 100644 index 00000000..8c45f449 --- /dev/null +++ b/higan/ngp/apu/apu.hpp @@ -0,0 +1,22 @@ +struct APU : Processor::Z80, Processor::Z80::Bus, Thread { + Emulator::Memory::Writable ram; + + //apu.cpp + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void override; + auto synchronizing() const -> bool override; + auto power() -> void; + + //memory.cpp + auto read(uint16 address) -> uint8 override { return 0; } + auto write(uint16 address, uint8 data) -> void override {} + + auto in(uint8 address) -> uint8 override { return 0; } + auto out(uint8 address, uint8 data) -> void override {} + + //serialization.cpp + auto serialize(serializer&) -> void; +}; + +extern APU apu; diff --git a/higan/ngp/apu/serialization.cpp b/higan/ngp/apu/serialization.cpp new file mode 100644 index 00000000..e7aaa581 --- /dev/null +++ b/higan/ngp/apu/serialization.cpp @@ -0,0 +1,5 @@ +auto APU::serialize(serializer& s) -> void { + Z80::serialize(s); + Z80::Bus::serialize(s); + Thread::serialize(s); +} diff --git a/higan/ngp/cartridge/cartridge.cpp b/higan/ngp/cartridge/cartridge.cpp new file mode 100644 index 00000000..d9183a7a --- /dev/null +++ b/higan/ngp/cartridge/cartridge.cpp @@ -0,0 +1,73 @@ +#include + +namespace NeoGeoPocket { + +Cartridge cartridge; +#include "flash.cpp" +#include "serialization.cpp" + +auto Cartridge::load() -> bool { + information = {}; + + if(Model::NeoGeoPocket()) { + if(auto loaded = platform->load(ID::NeoGeoPocket, "Neo Geo Pocket", "ngp")) { + information.pathID = loaded.pathID; + } else return true; //boot into BIOS + } + + if(Model::NeoGeoPocketColor()) { + if(auto loaded = platform->load(ID::NeoGeoPocketColor, "Neo Geo Pocket Color", "ngpc")) { + information.pathID = loaded.pathID; + } else return true; //boot into BIOS + } + + if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + information.title = document["game/label"].text(); + + flash[0].reset(0); + flash[1].reset(1); + + if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) { + auto size = memory["size"].natural(); + flash[0].allocate(min(16_Mibit, size)); + flash[1].allocate(size >= 16_Mibit ? 16_Mibit - size : 0); + if(auto fp = platform->open(pathID(), "program.rom", File::Read, File::Required)) { + flash[0].load(fp); + flash[1].load(fp); + } else return false; + } + + return true; +} + +auto Cartridge::save() -> void { + auto document = BML::unserialize(information.manifest); +} + +auto Cartridge::unload() -> void { + flash[0].reset(0); + flash[1].reset(1); +} + +auto Cartridge::power() -> void { + flash[0].power(); + flash[1].power(); +} + +auto Cartridge::read(uint1 chip, uint21 address) -> uint8 { + if(!flash[0]) return 0xff; + if(!flash[1]) chip = 0; + return flash[chip].read(address); +} + +auto Cartridge::write(uint1 chip, uint21 address, uint8 data) -> void { + if(!flash[0]) return; + if(!flash[1]) chip = 0; + return flash[chip].write(address, data); +} + +} diff --git a/higan/ngp/cartridge/cartridge.hpp b/higan/ngp/cartridge/cartridge.hpp new file mode 100644 index 00000000..bbcafe27 --- /dev/null +++ b/higan/ngp/cartridge/cartridge.hpp @@ -0,0 +1,76 @@ +//Toshiba 0x98 +//Sharp 0xb0 +//Samsung 0xec + +// 4mbit 0xab +// 8mbit 0x2c +//16mbit 0x2f + +struct Flash { + natural ID; //todo: can this be made const, even though it's declared as Cartridge::Flash[2] ? + + Emulator::Memory::Writable rom; + boolean modified; + uint8 vendorID; + uint8 deviceID; + + struct Block { + boolean writable; + natural offset; + natural length; + }; + vector blocks; + + explicit operator bool() const { return (bool)rom; } + + //flash.cpp + auto reset(natural ID) -> void; + auto allocate(natural size) -> bool; + auto load(vfs::shared::file fp) -> void; + + auto power() -> void; + auto read(uint21 address) -> uint8; + auto write(uint21 address, uint8 data) -> void; + + auto status(uint) -> void; + auto program(uint21 address, uint8 data) -> void; + auto erase(uint6 blockID) -> void; + auto eraseAll() -> void; + auto protect(uint6 blockID) -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + + enum : uint { Read, Prefix, Suffix, ExtendedPrefix, ExtendedSuffix, ReadID, Write }; + natural mode; +}; + +struct Cartridge { + Flash flash[2]; + + auto pathID() const -> natural { return information.pathID; } + auto hash() const -> string { return information.hash; } + auto manifest() const -> string { return information.manifest; } + auto title() const -> string { return information.title; } + + //cartridge.cpp + auto load() -> bool; + auto save() -> void; + auto unload() -> void; + auto power() -> void; + + auto read(uint1 bank, uint21 address) -> uint8; + auto write(uint1 bank, uint21 address, uint8 data) -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + + struct Information { + natural pathID; + string hash; + string manifest; + string title; + } information; +}; + +extern Cartridge cartridge; diff --git a/higan/ngp/cartridge/flash.cpp b/higan/ngp/cartridge/flash.cpp new file mode 100644 index 00000000..727e4d46 --- /dev/null +++ b/higan/ngp/cartridge/flash.cpp @@ -0,0 +1,96 @@ +auto Flash::reset(natural ID) -> void { + this->ID = ID; + rom.reset(); + modified = false; + vendorID = 0; + deviceID = 0; + blocks.reset(); +} + +auto Flash::allocate(natural size) -> bool { + if(size == 4_Mibit) { rom.allocate(size); vendorID = 0x98; deviceID = 0xab; } //vendorID 0x98 => Toshiba + if(size == 8_Mibit) { rom.allocate(size); vendorID = 0x98; deviceID = 0x2c; } //vendorID 0xb0 => Sharp + if(size == 16_Mibit) { rom.allocate(size); vendorID = 0x98; deviceID = 0x2f; } //vendorID 0xec => Samsung + if(!size) return false; + + for(uint index : range(size / 64_KiB - 1)) blocks.append({true, index * 64_KiB, 64_KiB}); + blocks.append({true, size - 64_KiB, 32_KiB}); + blocks.append({true, size - 32_KiB, 8_KiB}); + blocks.append({true, size - 24_KiB, 8_KiB}); + blocks.append({true, size - 16_KiB, 16_KiB}); + return true; +} + +auto Flash::load(vfs::shared::file fp) -> void { + fp->read(rom.data(), rom.size()); +} + +auto Flash::power() -> void { + status(Read); +} + +auto Flash::read(uint21 address) -> uint8 { + if(mode == ReadID) { + switch((uint15)address) { //todo: actual mask value unknown + case 0: return vendorID; + case 1: return deviceID; + case 2: return 0x02; //unknown purpose + case 3: return 0x80; //unknown purpose + } + return 0xff; //invalid ReadID address; todo: actual return value unknown + } + return rom.read(address); //todo: what happens when mode != Read here? +} + +auto Flash::write(uint21 address, uint8 data) -> void { + if(mode == Write) return program(address, data); + if(data == 0xf0) return status(Read); + address = (uint15)address; + if(address == 0x5555 && data == 0xaa) return status(Prefix); + if(mode == Prefix && address == 0x2aaa && data == 0x55) return status(Suffix); + if(mode == Suffix && address == 0x5555 && data == 0x90) return status(ReadID); + if(mode == Suffix && address == 0x5555 && data == 0xa0) return status(Write); + if(mode == Suffix && address == 0x5555 && data == 0xf0) return status(Read); + if(mode == Suffix && address == 0x5555 && data == 0x80) return status(ExtendedPrefix); + if(mode == ExtendedPrefix && address == 0x2aaa && data == 0x55) return status(ExtendedSuffix); + if(mode == ExtendedSuffix && address == 0x5555 && data == 0x10) return eraseAll(); + if(mode == ExtendedSuffix && data == 0x30) return erase((uint6)address); + if(mode == ExtendedSuffix && data == 0x90) return protect((uint6)address); + return status(Read); //invalid or unsupported command +} + +auto Flash::status(uint mode_) -> void { + mode = mode_; +} + +auto Flash::program(uint21 address, uint8 data) -> void { + for(auto& block : blocks) { + if(address >= block.offset && address < block.offset + block.length && block.writable) { + if(auto input = rom.read(address); input != (input & data)) { + modified = true; + return rom.write(address, input & data); + } + } + } +} + +auto Flash::erase(uint6 blockID) -> void { + //todo: unknown what happens when erasing invalid block IDs + if(blockID >= blocks.size() || !blocks[blockID].writable) return; + auto address = blocks[blockID].offset; + for(auto offset : range(blocks[blockID].length)) rom.write(address + offset, 0xff); + modified = true; + return status(Read); +} + +auto Flash::eraseAll() -> void { + for(uint blockID : range(blocks.size())) erase(blockID); +} + +auto Flash::protect(uint6 blockID) -> void { + //todo: unknown what happens when protected invalid block IDs + if(blockID >= blocks.size() || !blocks[blockID].writable) return; + blocks[blockID].writable = false; + modified = true; + return status(Read); +} diff --git a/higan/ngp/cartridge/serialization.cpp b/higan/ngp/cartridge/serialization.cpp new file mode 100644 index 00000000..139f92e3 --- /dev/null +++ b/higan/ngp/cartridge/serialization.cpp @@ -0,0 +1,10 @@ +auto Flash::serialize(serializer& s) -> void { + rom.serialize(s); + s.integer(vendorID); + s.integer(deviceID); +} + +auto Cartridge::serialize(serializer& s) -> void { + flash[0].serialize(s); + flash[1].serialize(s); +} diff --git a/higan/ngp/cpu/cpu.cpp b/higan/ngp/cpu/cpu.cpp new file mode 100644 index 00000000..de3a165c --- /dev/null +++ b/higan/ngp/cpu/cpu.cpp @@ -0,0 +1,35 @@ +#include + +namespace NeoGeoPocket { + +CPU cpu; +#include "memory.cpp" +#include "serialization.cpp" + +auto CPU::Enter() -> void { + while(true) scheduler.synchronize(), cpu.main(); +} + +auto CPU::main() -> void { +static uint ctr=0; +if(++ctr < 200) print(disassemble(), "\n"); +else return step(1); + instruction(); + step(1); +} + +auto CPU::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(vpu); + synchronize(apu); + synchronize(psg); +} + +auto CPU::power() -> void { + TLCS900H::power(); + create(CPU::Enter, system.frequency()); + ram.allocate(0x3000); + r.pc.l.l0 = 0xff1800; +} + +} diff --git a/higan/ngp/cpu/cpu.hpp b/higan/ngp/cpu/cpu.hpp new file mode 100644 index 00000000..4d9e9cc3 --- /dev/null +++ b/higan/ngp/cpu/cpu.hpp @@ -0,0 +1,18 @@ +struct CPU : Processor::TLCS900H, Thread { + Emulator::Memory::Writable ram; + + //cpu.cpp + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void override; + auto power() -> void; + + //memory.cpp + auto read(uint24 address) -> uint8 override; + auto write(uint24 address, uint8 data) -> void override; + + //serialization.cpp + auto serialize(serializer&) -> void; +}; + +extern CPU cpu; diff --git a/higan/ngp/cpu/memory.cpp b/higan/ngp/cpu/memory.cpp new file mode 100644 index 00000000..969f507e --- /dev/null +++ b/higan/ngp/cpu/memory.cpp @@ -0,0 +1,27 @@ +auto CPU::read(uint24 address) -> uint8 { + if(address < 0x000100) return 0xff; + if(address < 0x004000) return 0xff; + if(address < 0x007000) return cpu.ram.read((uint14)address); + if(address < 0x008000) return apu.ram.read((uint12)address); + if(address < 0x00c000) return vpu.ram.read((uint14)address); + if(address < 0x200000) return 0xff; + if(address < 0x400000) return cartridge.read(0, (uint21)address); + if(address < 0x800000) return 0xff; + if(address < 0xa00000) return cartridge.read(1, (uint21)address); + if(address < 0xff0000) return 0xff; + return system.bios.read((uint16)address); +} + +auto CPU::write(uint24 address, uint8 data) -> void { + if(address < 0x000100) return; + if(address < 0x004000) return; + if(address < 0x007000) return cpu.ram.write((uint14)address, data); + if(address < 0x008000) return apu.ram.write((uint12)address, data); + if(address < 0x00c000) return vpu.ram.write((uint14)address, data); + if(address < 0x200000) return; + if(address < 0x400000) return cartridge.write(0, (uint21)address, data); + if(address < 0x800000) return; + if(address < 0xa00000) return cartridge.write(1, (uint21)address, data); + if(address < 0xff0000) return; + return system.bios.write((uint16)address, data); +} diff --git a/higan/ngp/cpu/serialization.cpp b/higan/ngp/cpu/serialization.cpp new file mode 100644 index 00000000..a46fa754 --- /dev/null +++ b/higan/ngp/cpu/serialization.cpp @@ -0,0 +1,4 @@ +auto CPU::serialize(serializer& s) -> void { + TLCS900H::serialize(s); + Thread::serialize(s); +} diff --git a/higan/ngp/interface/interface.cpp b/higan/ngp/interface/interface.cpp index b1150c56..c3d5217b 100644 --- a/higan/ngp/interface/interface.cpp +++ b/higan/ngp/interface/interface.cpp @@ -9,38 +9,42 @@ namespace NeoGeoPocket { auto Interface::display() -> Display { Display display; display.type = Display::Type::LCD; - display.colors = 1; - display.width = 320; - display.height = 240; - display.internalWidth = 320; - display.internalHeight = 240; + display.colors = 1 << 12; + display.width = 160; + display.height = 152; + display.internalWidth = 160; + display.internalHeight = 152; display.aspectCorrection = 1.0; display.refreshRate = 60.0; return display; } auto Interface::color(uint32 color) -> uint64 { - return 0; + uint b = color.bits(0, 3); + uint g = color.bits(4, 7); + uint r = color.bits(8,11); + + natural R = image::normalize(r, 4, 16); + natural G = image::normalize(g, 4, 16); + natural B = image::normalize(b, 4, 16); + + return R << 32 | G << 16 | B << 0; } auto Interface::loaded() -> bool { - return false; + return system.loaded(); } auto Interface::hashes() -> vector { - return {}; + return {cartridge.hash()}; } auto Interface::manifests() -> vector { - return {}; + return {cartridge.manifest()}; } auto Interface::titles() -> vector { - return {}; -} - -auto Interface::load() -> bool { - return false; + return {cartridge.title()}; } auto Interface::save() -> void { @@ -66,27 +70,33 @@ auto Interface::inputs(uint device) -> vector { using Type = Input::Type; if(device == ID::Device::Controls) return { - {Type::Hat, "Up" }, - {Type::Hat, "Down" }, - {Type::Hat, "Left" }, - {Type::Hat, "Right"} + {Type::Hat, "Up" }, + {Type::Hat, "Down" }, + {Type::Hat, "Left" }, + {Type::Hat, "Right" }, + {Type::Button, "A" }, + {Type::Button, "B" }, + {Type::Control, "Option"} }; return {}; } auto Interface::power() -> void { + system.power(); } auto Interface::run() -> void { + system.run(); } auto Interface::serialize() -> serializer { - return {}; + system.runToSave(); + return system.serialize(); } auto Interface::unserialize(serializer& s) -> bool { - return false; + return system.unserialize(s); } } diff --git a/higan/ngp/interface/interface.hpp b/higan/ngp/interface/interface.hpp index 72acadbe..0db16a28 100644 --- a/higan/ngp/interface/interface.hpp +++ b/higan/ngp/interface/interface.hpp @@ -26,7 +26,6 @@ struct Interface : Emulator::Interface { auto hashes() -> vector override; auto manifests() -> vector override; auto titles() -> vector override; - auto load() -> bool override; auto save() -> void override; auto unload() -> void override; @@ -43,10 +42,14 @@ struct Interface : Emulator::Interface { struct NeoGeoPocketInterface : Interface { auto information() -> Information override; + + auto load() -> bool override; }; struct NeoGeoPocketColorInterface : Interface { auto information() -> Information override; + + auto load() -> bool override; }; } diff --git a/higan/ngp/interface/neo-geo-pocket-color.cpp b/higan/ngp/interface/neo-geo-pocket-color.cpp index f5427c64..0942861e 100644 --- a/higan/ngp/interface/neo-geo-pocket-color.cpp +++ b/higan/ngp/interface/neo-geo-pocket-color.cpp @@ -5,3 +5,7 @@ auto NeoGeoPocketColorInterface::information() -> Information { information.extension = "ngpc"; return information; } + +auto NeoGeoPocketColorInterface::load() -> bool { + return system.load(this, System::Model::NeoGeoPocketColor); +} diff --git a/higan/ngp/interface/neo-geo-pocket.cpp b/higan/ngp/interface/neo-geo-pocket.cpp index 46619ba0..1e8d37ec 100644 --- a/higan/ngp/interface/neo-geo-pocket.cpp +++ b/higan/ngp/interface/neo-geo-pocket.cpp @@ -5,3 +5,7 @@ auto NeoGeoPocketInterface::information() -> Information { information.extension = "ngp"; return information; } + +auto NeoGeoPocketInterface::load() -> bool { + return system.load(this, System::Model::NeoGeoPocket); +} diff --git a/higan/ngp/ngp.hpp b/higan/ngp/ngp.hpp index ed87a3ae..eb9fcd3d 100644 --- a/higan/ngp/ngp.hpp +++ b/higan/ngp/ngp.hpp @@ -36,6 +36,11 @@ namespace NeoGeoPocket { }; #include + #include + #include + #include + #include + #include } #include diff --git a/higan/ngp/psg/psg.cpp b/higan/ngp/psg/psg.cpp new file mode 100644 index 00000000..c43f00d8 --- /dev/null +++ b/higan/ngp/psg/psg.cpp @@ -0,0 +1,29 @@ +#include + +namespace NeoGeoPocket { + +PSG psg; +#include "serialization.cpp" + +auto PSG::Enter() -> void { + while(true) scheduler.synchronize(), psg.main(); +} + +auto PSG::main() -> void { + stream->sample(0.0); + step(1); +} + +auto PSG::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + +auto PSG::power() -> void { + create(PSG::Enter, system.frequency() / 2.0); + stream = Emulator::audio.createStream(1, frequency()); + stream->addHighPassFilter(20.0, Emulator::Filter::Order::First); + stream->addDCRemovalFilter(); +} + +} diff --git a/higan/ngp/psg/psg.hpp b/higan/ngp/psg/psg.hpp new file mode 100644 index 00000000..d30c6e37 --- /dev/null +++ b/higan/ngp/psg/psg.hpp @@ -0,0 +1,16 @@ +//Texas Instruments SN76489A + +struct PSG : Thread { + shared_pointer stream; + + //psg.cpp + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + auto power() -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; +}; + +extern PSG psg; diff --git a/higan/ngp/psg/serialization.cpp b/higan/ngp/psg/serialization.cpp new file mode 100644 index 00000000..80bf3879 --- /dev/null +++ b/higan/ngp/psg/serialization.cpp @@ -0,0 +1,3 @@ +auto PSG::serialize(serializer& s) -> void { + Thread::serialize(s); +} diff --git a/higan/ngp/system/serialization.cpp b/higan/ngp/system/serialization.cpp new file mode 100644 index 00000000..46829eee --- /dev/null +++ b/higan/ngp/system/serialization.cpp @@ -0,0 +1,59 @@ +auto System::serializeInit() -> void { + serializer s; + + uint signature = 0; + char version[16] = {}; + char description[512] = {}; + + s.integer(signature); + s.array(version); + s.array(description); + + serializeAll(s); + information.serializeSize = s.size(); +} + +auto System::serialize() -> serializer { + serializer s(information.serializeSize); + + uint signature = 0x31545342; + char version[16] = {}; + char description[512] = {}; + memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size()); + + s.integer(signature); + s.array(version); + s.array(description); + + serializeAll(s); + return s; +} + +auto System::unserialize(serializer& s) -> bool { + uint signature = 0; + char version[16] = {}; + char description[512] = {}; + + s.integer(signature); + s.array(version); + s.array(description); + + if(signature != 0x31545342) return false; + if(string{version} != Emulator::SerializerVersion) return false; + + power(); + serializeAll(s); + return true; +} + +auto System::serializeAll(serializer& s) -> void { + system.serialize(s); + cartridge.serialize(s); + cpu.serialize(s); + apu.serialize(s); + vpu.serialize(s); + psg.serialize(s); +} + +auto System::serialize(serializer& s) -> void { +} diff --git a/higan/ngp/system/system.cpp b/higan/ngp/system/system.cpp index 6a58a809..0a488131 100644 --- a/higan/ngp/system/system.cpp +++ b/higan/ngp/system/system.cpp @@ -5,5 +5,72 @@ namespace NeoGeoPocket { System system; Scheduler scheduler; Cheat cheat; +#include "serialization.cpp" + +auto System::run() -> void { + if(scheduler.enter() == Scheduler::Event::Frame) { + vpu.refresh(); + } +} + +auto System::runToSave() -> void { + scheduler.synchronize(cpu); + scheduler.synchronize(apu); + scheduler.synchronize(vpu); + scheduler.synchronize(psg); +} + +auto System::load(Emulator::Interface* interface_, Model model_) -> bool { + information.model = model_; + + if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + if(auto memory = document["system/bios"]) { + bios.allocate(memory["size"].natural()); + if(auto fp = platform->open(ID::System, memory["name"].text(), File::Read, File::Required)) { + bios.load(fp); + } else return false; + } else return false; + + if(model() == Model::NeoGeoPocket) { + } + + if(model() == Model::NeoGeoPocketColor) { + } + + if(!cartridge.load()) return false; + serializeInit(); + interface = interface_; + return information.loaded = true; +} + +auto System::save() -> void { + if(!loaded()) return; + cartridge.save(); +} + +auto System::unload() -> void { + if(!loaded()) return; + bios.reset(); + cartridge.unload(); + information.loaded = false; +} + +auto System::power() -> void { + Emulator::video.reset(interface); + Emulator::video.setPalette(); + Emulator::audio.reset(interface); + + scheduler.reset(); + cartridge.power(); + cpu.power(); + apu.power(); + vpu.power(); + psg.power(); + scheduler.primary(cpu); +} } diff --git a/higan/ngp/system/system.hpp b/higan/ngp/system/system.hpp index 347fe32c..ecd99a36 100644 --- a/higan/ngp/system/system.hpp +++ b/higan/ngp/system/system.hpp @@ -1,14 +1,35 @@ struct System { enum class Model : uint { NeoGeoPocket, NeoGeoPocketColor }; + Emulator::Memory::Readable bios; - auto loaded() const -> bool { return information.loaded; } - auto model() const -> Model { return information.model; } + inline auto loaded() const -> bool { return information.loaded; } + inline auto model() const -> Model { return information.model; } + inline auto frequency() const -> double { return 6'144'000; } + + //system.cpp + auto run() -> void; + auto runToSave() -> void; + auto load(Emulator::Interface*, Model) -> bool; + auto save() -> void; + auto unload() -> void; + auto power() -> void; + + //serialization.cpp + auto serializeInit() -> void; + auto serialize() -> serializer; + auto unserialize(serializer&) -> bool; + auto serializeAll(serializer&) -> void; + auto serialize(serializer&) -> void; struct Information { string manifest; bool loaded = false; Model model = Model::NeoGeoPocket; + natural serializeSize; } information; + +private: + Emulator::Interface* interface = nullptr; }; extern System system; diff --git a/higan/ngp/vpu/serialization.cpp b/higan/ngp/vpu/serialization.cpp new file mode 100644 index 00000000..ed0a7d5e --- /dev/null +++ b/higan/ngp/vpu/serialization.cpp @@ -0,0 +1,3 @@ +auto VPU::serialize(serializer& s) -> void { + Thread::serialize(s); +} diff --git a/higan/ngp/vpu/vpu.cpp b/higan/ngp/vpu/vpu.cpp new file mode 100644 index 00000000..74a7872c --- /dev/null +++ b/higan/ngp/vpu/vpu.cpp @@ -0,0 +1,31 @@ +#include + +namespace NeoGeoPocket { + +VPU vpu; +#include "serialization.cpp" + +auto VPU::Enter() -> void { + while(true) scheduler.synchronize(), vpu.main(); +} + +auto VPU::main() -> void { + step(system.frequency() / 60.0); + scheduler.exit(Scheduler::Event::Frame); +} + +auto VPU::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + +auto VPU::refresh() -> void { + Emulator::video.refresh(buffer, 160 * sizeof(uint32), 160, 152); +} + +auto VPU::power() -> void { + create(VPU::Enter, system.frequency()); + ram.allocate(0x4000); +} + +} diff --git a/higan/ngp/vpu/vpu.hpp b/higan/ngp/vpu/vpu.hpp new file mode 100644 index 00000000..db64a926 --- /dev/null +++ b/higan/ngp/vpu/vpu.hpp @@ -0,0 +1,21 @@ +//K1GE (Neo Geo Pocket) +//K2GE (Neo Geo Pocket Color) + +struct VPU : Thread { + Emulator::Memory::Writable ram; + + //vpu.cpp + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + auto refresh() -> void; + auto power() -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + +private: + uint32 buffer[160 * 152]; +}; + +extern VPU vpu; diff --git a/higan/processor/tlcs900h/disassembler.cpp b/higan/processor/tlcs900h/disassembler.cpp index 4744cfbd..f685eaa2 100644 --- a/higan/processor/tlcs900h/disassembler.cpp +++ b/higan/processor/tlcs900h/disassembler.cpp @@ -94,8 +94,8 @@ auto TLCS900H::disassemble() -> string { boolean opSourceMemory; boolean opTargetMemory; - static const natural opcodeSizes[] = {8, 16, 32, 0}; //0xc0 - 0xf5 - + static const natural opSizes[] = {8, 16, 32, 0}; //0xc0 - 0xf5 use combined logic switch cases: + #define opSize opSizes[fetch.bits(4,5)] //extract the size from the opcode fetch switch(auto fetch = read8()) { case 0x00: name = "nop"; break; case 0x01: break; @@ -153,7 +153,7 @@ auto TLCS900H::disassemble() -> string { case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: opSourceMemory = true; lhs.indirectRegister3(8, fetch); break; case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: - opSourceMemory = true; lhs.indirectRegister3Displacement8(8, fetch, read8()); + opSourceMemory = true; lhs.indirectRegister3Displacement8(8, fetch, read8()); break; case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: opSourceMemory = true; lhs.indirectRegister3(16, fetch); break; case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: @@ -168,31 +168,31 @@ auto TLCS900H::disassemble() -> string { opTargetMemory = true; lhs.indirectRegister3Displacement8(0, fetch, read8()); break; case 0xc0: case 0xd0: case 0xe0: case 0xf0: opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; - lhs.indirectImmediate8(opcodeSizes[fetch >> 4], read8()); break; + lhs.indirectImmediate8(opSize, read8()); break; case 0xc1: case 0xd1: case 0xe1: case 0xf1: opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; - lhs.indirectImmediate16(opcodeSizes[fetch >> 4], read16()); break; + lhs.indirectImmediate16(opSize, read16()); break; case 0xc2: case 0xd2: case 0xe2: case 0xf2: opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; - lhs.indirectImmediate24(opcodeSizes[fetch >> 4], read24()); break; + lhs.indirectImmediate24(opSize, read24()); break; case 0xc3: case 0xd3: case 0xe3: case 0xf3: { opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; auto data = read8(); - if((uint2)data == 0) lhs.indirectRegister(opcodeSizes[fetch >> 4], data); - if((uint2)data == 1) lhs.indirectRegisterDisplacement16(opcodeSizes[fetch >> 4], data, read16()); - if(data == 0x03) { auto r32 = read8(); lhs.indirectRegisterRegister8(opcodeSizes[fetch >> 4], r32, read8()); } - if(data == 0x07) { auto r32 = read8(); lhs.indirectRegisterRegister16(opcodeSizes[fetch >> 4], r32, read16()); } + if((uint2)data == 0) lhs.indirectRegister(opSize, data); + if((uint2)data == 1) lhs.indirectRegisterDisplacement16(opSize, data, read16()); + if(data == 0x03) { auto r32 = read8(); lhs.indirectRegisterRegister8(opSize, r32, read8()); } + if(data == 0x07) { auto r32 = read8(); lhs.indirectRegisterRegister16(opSize, r32, read16()); } } break; case 0xc4: case 0xd4: case 0xe4: case 0xf4: opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; - lhs.indirectRegisterDecrement(opcodeSizes[fetch >> 4], read8()); break; + lhs.indirectRegisterDecrement(opSize, read8()); break; case 0xc5: case 0xd5: case 0xe5: case 0xf5: opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; - lhs.indirectRegisterIncrement(opcodeSizes[fetch >> 4], read8()); break; + lhs.indirectRegisterIncrement(opSize, read8()); break; case 0xc6: case 0xd6: case 0xe6: case 0xf6: break; case 0xc7: case 0xd7: case 0xe7: opRegister = true; - lhs.indirectRegister(opcodeSizes[fetch >> 4], read8()); break; + lhs.indirectRegister(opSize, read8()); break; case 0xf7: name = "ldx"; read8(); lhs.indirectImmediate8(8, read8()); @@ -207,6 +207,7 @@ auto TLCS900H::disassemble() -> string { case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff: name = "swi"; lhs.immediate(3, (uint3)fetch); break; } + #undef opSize auto reads = [&](uint size) -> uint32 { if(size == 8) return read8(); @@ -344,7 +345,7 @@ auto TLCS900H::disassemble() -> string { if(opSourceMemory) switch(auto fetch = read8()) { case 0x00: case 0x01: case 0x02: case 0x03: break; - case 0x04: name = "push"; break; + case 0x04: name = lhs.size() == 8 ? "push" : "pushw"; break; case 0x05: break; case 0x06: name = "rld"; rhs = lhs; lhs.register(8, A.id); break; case 0x07: name = "rrd"; rhs = lhs; lhs.register(8, A.id); break; @@ -397,14 +398,14 @@ auto TLCS900H::disassemble() -> string { case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: break; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: name = "ex"; rhs.register3(lhs.size(), fetch); break; - case 0x38: name = "add"; rhs.immediate(3, (uint3)fetch); break; - case 0x39: name = "adc"; rhs.immediate(3, (uint3)fetch); break; - case 0x3a: name = "sub"; rhs.immediate(3, (uint3)fetch); break; - case 0x3b: name = "sbb"; rhs.immediate(3, (uint3)fetch); break; - case 0x3c: name = "and"; rhs.immediate(3, (uint3)fetch); break; - case 0x3d: name = "xor"; rhs.immediate(3, (uint3)fetch); break; - case 0x3e: name = "or"; rhs.immediate(3, (uint3)fetch); break; - case 0x3f: name = "cp"; rhs.immediate(3, (uint3)fetch); break; + case 0x38: name = "add"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x39: name = "adc"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3a: name = "sub"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3b: name = "sbb"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3c: name = "and"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3d: name = "xor"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3e: name = "or"; rhs.immediate(lhs.size(), reads(lhs.size())); break; + case 0x3f: name = "cp"; rhs.immediate(lhs.size(), reads(lhs.size())); break; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: name = "mul"; rhs = lhs; lhs.register3(rhs.size(), fetch); break; case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: @@ -541,7 +542,7 @@ auto TLCS900H::disassemble() -> string { }; auto register8 = [](uint8 register) -> string { if(register < 0x40) return registers8[register]; - if(register >= 0xd0) return registers8[register - 0x80]; + if(register >= 0xd0) return registers8[(register - 0xd0 >> 0) + 0x40]; return "rb?"; }; @@ -556,7 +557,7 @@ auto TLCS900H::disassemble() -> string { }; auto register16 = [](uint8 register) -> string { if(register < 0x40) return registers16[register >> 1]; - if(register >= 0xd0) return registers16[register - 0x80 >> 1]; + if(register >= 0xd0) return registers16[(register - 0xd0 >> 1) + 0x20]; return "rw?"; }; @@ -571,7 +572,7 @@ auto TLCS900H::disassemble() -> string { }; auto register32 = [&](uint8 register) -> string { if(register < 0x40) return registers32[register >> 2]; - if(register >= 0xd0) return registers32[register - 0x80 >> 2]; + if(register >= 0xd0) return registers32[(register - 0xd0 >> 2) + 0x10]; return "rl?"; }; @@ -630,13 +631,13 @@ auto TLCS900H::disassemble() -> string { if(operand.mode() == Displacement) { if(operand.size() == 8) { integer displacement = (int8)operand.displacement(); - if(displacement < 0) return {"-0x", hex(-displacement, 2L)}; - return {"+0x", hex(displacement, 2L)}; + if(displacement < 0) return {"-0x", hex(-displacement, 2L)}; + if(displacement >= 0) return {"+0x", hex(+displacement, 2L)}; } if(operand.size() == 16) { integer displacement = (int8)operand.displacement(); - if(displacement < 0) return {"-0x", hex(-displacement, 4L)}; - return {"+0x", hex(displacement, 4L)}; + if(displacement < 0) return {"-0x", hex(-displacement, 4L)}; + if(displacement >= 0) return {"+0x", hex(+displacement, 4L)}; } } if(operand.mode() == DisplacementPC) { @@ -656,13 +657,15 @@ auto TLCS900H::disassemble() -> string { if(operand.mode() == IndirectRegisterRegister16) return {"(", register32(operand.register()), "+", register16(operand.registerAdd()), ")"}; if(operand.mode() == IndirectRegisterDisplacement8) { integer displacement = (int8)operand.displacement(); - if(displacement < 0) return {"(", register32(operand.register()), "-0x", hex(-displacement, 2L), ")"}; - return {"(", register32(operand.register()), "+0x", hex(displacement, 2L), ")"}; + if(displacement == 0) return {"(", register32(operand.register()), ")"}; + if(displacement < 0) return {"(", register32(operand.register()), "-0x", hex(-displacement, 2L), ")"}; + if(displacement > 0) return {"(", register32(operand.register()), "+0x", hex(+displacement, 2L), ")"}; } if(operand.mode() == IndirectRegisterDisplacement16) { integer displacement = (int16)operand.displacement(); - if(displacement < 0) return {"(", register32(operand.register()), "-0x", hex(-displacement, 4L), ")"}; - return {"(", register32(operand.register()), "+0x", hex(displacement, 4L), ")"}; + if(displacement == 0) return {"(", register32(operand.register()), ")"}; + if(displacement < 0) return {"(", register32(operand.register()), "-0x", hex(-displacement, 4L), ")"}; + if(displacement > 0) return {"(", register32(operand.register()), "+0x", hex(+displacement, 4L), ")"}; } if(operand.mode() == IndirectImmediate8) return {"(0x", hex(operand.immediate(), 2L), ")"}; if(operand.mode() == IndirectImmediate16) return {"(0x", hex(operand.immediate(), 4L), ")"}; @@ -670,19 +673,28 @@ auto TLCS900H::disassemble() -> string { return {}; }; + //omit true condition operand + if(lhs.mode() == Condition && lhs.condition() == 8) { + lhs = rhs; + rhs.null(); + } + if(name) { output.append(pad(name, -6)); if(lhs) { - output.append(operand(lhs), ","); + output.append(operand(lhs)); if(rhs) { - output.append(operand(rhs)); + output.append(",", operand(rhs)); } } } else { output.append(pad("???", -6)); - for(auto byte : range(ops)) output.append(hex(op[byte], 2L), " "); + for(auto byte : range(ops)) output.append("0x", hex(op[byte], 2L), ","); + output.trimRight(",", 1L); } +//for(auto byte : range(ops)) print("0x", hex(op[byte], 2L), " "); print("\n"); + output.size(-48); output.append("XWA:", hex(r.xwa[r.rfp].l.l0, 8L), " "); output.append("XBC:", hex(r.xbc[r.rfp].l.l0, 8L), " "); diff --git a/higan/processor/tlcs900h/instruction.cpp b/higan/processor/tlcs900h/instruction.cpp index a2234953..5db33d61 100644 --- a/higan/processor/tlcs900h/instruction.cpp +++ b/higan/processor/tlcs900h/instruction.cpp @@ -382,23 +382,23 @@ auto TLCS900H::instructionRegister(R register) -> void { return (void)Undefined; case 0x35: case 0x36: case 0x37: return (void)Undefined; case 0x38: - if constexpr(bits == 16) return instructionModuloIncrement<1>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloIncrement<1>(register, fetchImmediate()); return (void)Undefined; case 0x39: - if constexpr(bits == 16) return instructionModuloIncrement<2>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloIncrement<2>(register, fetchImmediate()); return (void)Undefined; case 0x3a: - if constexpr(bits == 16) return instructionModuloIncrement<4>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloIncrement<4>(register, fetchImmediate()); return (void)Undefined; case 0x3b: return (void)Undefined; case 0x3c: - if constexpr(bits == 16) return instructionModuloDecrement<1>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloDecrement<1>(register, fetchImmediate()); return (void)Undefined; case 0x3d: - if constexpr(bits == 16) return instructionModuloDecrement<2>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloDecrement<2>(register, fetchImmediate()); return (void)Undefined; case 0x3e: - if constexpr(bits == 16) return instructionModuloDecrement<4>(register, fetchImmediate()); + if constexpr(bits == 16) return instructionModuloDecrement<4>(register, fetchImmediate()); return (void)Undefined; case 0x3f: return (void)Undefined; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: diff --git a/higan/processor/tlcs900h/instructions.cpp b/higan/processor/tlcs900h/instructions.cpp index 7d82ade7..6c1eae1a 100644 --- a/higan/processor/tlcs900h/instructions.cpp +++ b/higan/processor/tlcs900h/instructions.cpp @@ -227,6 +227,7 @@ template auto TLCS900H::instructionModuloDecrement(Target target, Source source) -> void { auto result = load(target); auto number = load(source); + if(bit::count(number) != 1) return (void)Undefined; //must be power of two (what about number==1?) if(result % number == 0) { store(target, result + (number - Modulo)); } else { @@ -238,6 +239,7 @@ template auto TLCS900H::instructionModuloIncrement(Target target, Source source) -> void { auto result = load(target); auto number = load(source); + if(bit::count(number) != 1) return (void)Undefined; //must be power of two (what about number==1?) if(result % number == number - Modulo) { store(target, result - (number - Modulo)); } else { @@ -479,7 +481,10 @@ auto TLCS900H::instructionStoreCarry(Target target, Offset offset) -> void { } auto TLCS900H::instructionSoftwareInterrupt(uint3 interrupt) -> void { - //TODO + push(PC); + push(SR); + store(PC, load(Memory{0xffff00 + interrupt * 4})); + store(INTNEST, load(INTNEST) + 1); //note: not confirmed behavior } template diff --git a/higan/processor/tlcs900h/memory.cpp b/higan/processor/tlcs900h/memory.cpp index 4db6f5ec..b6f83b6c 100644 --- a/higan/processor/tlcs900h/memory.cpp +++ b/higan/processor/tlcs900h/memory.cpp @@ -1,25 +1,29 @@ +#define PC r.pc.l.l0 + template<> auto TLCS900H::fetch< uint8>() -> uint8 { - return rand(); + return read(PC++); } template<> auto TLCS900H::fetch() -> uint16 { - uint16 data = fetch(); - return data | fetch() << 8; + uint16 data = read(PC++) << 0; + return data | read(PC++) << 8; } template<> auto TLCS900H::fetch() -> uint24 { - uint24 data = fetch(); - data |= fetch() << 8; - return data |= fetch() << 16; + uint24 data = read(PC++) << 0; + data |= read(PC++) << 8; + return data |= read(PC++) << 16; } template<> auto TLCS900H::fetch() -> uint32 { - uint32 data = fetch(); - data |= fetch() << 8; - data |= fetch() << 16; - return data |= fetch() << 24; + uint32 data = read(PC++) << 0; + data |= read(PC++) << 8; + data |= read(PC++) << 16; + return data |= read(PC++) << 24; } +#undef PC + template<> auto TLCS900H::fetch< int8>() -> int8 { return ( int8)fetch< uint8>(); } template<> auto TLCS900H::fetch() -> int16 { return (int16)fetch(); } template<> auto TLCS900H::fetch() -> int24 { return (int24)fetch(); } @@ -35,19 +39,21 @@ template auto TLCS900H::fetchImmediate() -> Immediate { return Im template auto TLCS900H::pop(T data) -> void { auto value = typename T::type(); - if constexpr(T::bits >= 8) value.byte(0) = read(XSP++); - if constexpr(T::bits >= 16) value.byte(1) = read(XSP++); - if constexpr(T::bits >= 24) value.byte(2) = read(XSP++); - if constexpr(T::bits >= 32) value.byte(3) = read(XSP++); + if constexpr(T::bits >= 8) value.byte(0) = read(XSP + 0); + if constexpr(T::bits >= 16) value.byte(1) = read(XSP + 1); + if constexpr(T::bits >= 24) value.byte(2) = read(XSP + 2); + if constexpr(T::bits >= 32) value.byte(3) = read(XSP + 3); store(data, value); + XSP += T::bits >> 3; } template auto TLCS900H::push(T data) -> void { + XSP -= T::bits >> 3; auto value = load(data); - if constexpr(T::bits >= 8) write(--XSP, value >> 0); - if constexpr(T::bits >= 16) write(--XSP, value >> 8); - if constexpr(T::bits >= 24) write(--XSP, value >> 16); - if constexpr(T::bits >= 32) write(--XSP, value >> 24); + if constexpr(T::bits >= 8) write(XSP + 0, value >> 0); + if constexpr(T::bits >= 16) write(XSP + 1, value >> 8); + if constexpr(T::bits >= 24) write(XSP + 2, value >> 16); + if constexpr(T::bits >= 32) write(XSP + 3, value >> 24); } #undef XSP @@ -63,6 +69,12 @@ template<> auto TLCS900H::load(Memory memory) -> uint16 { return data | read(memory.address + 1) << 8; } +template<> auto TLCS900H::load(Memory memory) -> uint24 { + uint24 data = read(memory.address + 0) << 0; + data |= read(memory.address + 1) << 8; + return data |= read(memory.address + 2) << 16; +} + template<> auto TLCS900H::load(Memory memory) -> uint32 { uint32 data = read(memory.address + 0) << 0; data |= read(memory.address + 1) << 8; @@ -72,6 +84,7 @@ template<> auto TLCS900H::load(Memory memory) -> uint32 { template<> auto TLCS900H::load(Memory< int8> memory) -> int8 { return (int8)load< uint8>(Memory< uint8>{memory.address}); } template<> auto TLCS900H::load(Memory memory) -> int16 { return (int16)load(Memory{memory.address}); } +template<> auto TLCS900H::load(Memory memory) -> int24 { return (int24)load(Memory{memory.address}); } template<> auto TLCS900H::load(Memory memory) -> int32 { return (int32)load(Memory{memory.address}); } template<> auto TLCS900H::store(Memory< uint8> memory, uint32 data) -> void { diff --git a/higan/processor/tlcs900h/tlcs900h.cpp b/higan/processor/tlcs900h/tlcs900h.cpp index e50461b8..e00b2488 100644 --- a/higan/processor/tlcs900h/tlcs900h.cpp +++ b/higan/processor/tlcs900h/tlcs900h.cpp @@ -24,13 +24,15 @@ namespace Processor { #include "serialization.cpp" #include "disassembler.cpp" -TLCS900H tlcs900h; +auto TLCS900H::interrupt(uint24 address) -> void { + push(PC); + push(SR); + store(PC, load(Memory{address})); + store(INTNEST, load(INTNEST) + 1); +} auto TLCS900H::power() -> void { r = {}; } -TLCS900H::TLCS900H() { -} - } diff --git a/higan/processor/tlcs900h/tlcs900h.hpp b/higan/processor/tlcs900h/tlcs900h.hpp index 27398710..12b9b614 100644 --- a/higan/processor/tlcs900h/tlcs900h.hpp +++ b/higan/processor/tlcs900h/tlcs900h.hpp @@ -9,6 +9,7 @@ * what value is read back from a non-existent 8-bit register ID? (eg 0x40-0xcf) * many instructions are undefined, some are marked as dummy instructions ... what do each do? * what happens when using (AND,OR,XOR,LD)CF (byte)source,A with A.bit(3) set? + * what happens when using MINC#,MDEC# with a non-power of two? what about with a value of 1? */ #pragma once @@ -16,22 +17,22 @@ namespace Processor { struct TLCS900H { - virtual auto read(uint24 address) -> uint8 { return 0; } - virtual auto write(uint24 address, uint8 data) -> void {}; - - TLCS900H(); + virtual auto step(uint clocks) -> void = 0; + virtual auto read(uint24 address) -> uint8 = 0; + virtual auto write(uint24 address, uint8 data) -> void = 0; struct FlagRegister { using type = uint8; enum : uint { bits = 8 }; uint1 id; }; struct StatusRegister { using type = uint16; enum : uint { bits = 16 }; }; struct ProgramCounter { using type = uint32; enum : uint { bits = 32 }; }; - template struct ControlRegister { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; - template struct Register { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; - template struct Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; T address; }; - template struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; T constant; }; + template struct ControlRegister { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; + template struct Register { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; + template struct Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint32 address; }; + template struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint32 constant; }; template auto load(Immediate immediate) const -> T { return immediate.constant; } //tlcs900h.cpp + auto interrupt(uint24 vector) -> void; auto power() -> void; //registers.cpp @@ -261,7 +262,7 @@ struct TLCS900H { static inline const uint1 Undefined = 0; //disassembler.cpp - virtual auto disassembleRead(uint24 address) -> uint8 { return rand(); } + virtual auto disassembleRead(uint24 address) -> uint8 { return read(address); } auto disassemble() -> string; }; diff --git a/higan/sfc/ppu-fast/background.cpp b/higan/sfc/ppu-fast/background.cpp index 6fa0abb2..14cfef17 100644 --- a/higan/sfc/ppu-fast/background.cpp +++ b/higan/sfc/ppu-fast/background.cpp @@ -26,7 +26,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void uint hmask = (width << self.tileSize << self.screenSize.bit(0)) - 1; uint vmask = (width << self.tileSize << self.screenSize.bit(1)) - 1; - uint y = this->y - self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0; + uint y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0); if(hires) { hscroll <<= 1; if(io.interlace) y = y << 1 | ppu.field(); diff --git a/higan/sfc/ppu-fast/mode7.cpp b/higan/sfc/ppu-fast/mode7.cpp index eeb3dc16..5f9668fe 100644 --- a/higan/sfc/ppu-fast/mode7.cpp +++ b/higan/sfc/ppu-fast/mode7.cpp @@ -1,5 +1,5 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { - int Y = this->y - self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0; + int Y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0); int y = !io.mode7.vflip ? Y : 255 - Y; int a = (int16)io.mode7.a; diff --git a/higan/systems/Neo Geo Pocket Color.sys/manifest.bml b/higan/systems/Neo Geo Pocket Color.sys/manifest.bml index a2d8bea4..ff78dcc2 100644 --- a/higan/systems/Neo Geo Pocket Color.sys/manifest.bml +++ b/higan/systems/Neo Geo Pocket Color.sys/manifest.bml @@ -1 +1,2 @@ system name:Neo Geo Pocket Color + bios name=bios.rom size=0x10000 diff --git a/higan/systems/Neo Geo Pocket.sys/manifest.bml b/higan/systems/Neo Geo Pocket.sys/manifest.bml index 4f72276f..f8b83e07 100644 --- a/higan/systems/Neo Geo Pocket.sys/manifest.bml +++ b/higan/systems/Neo Geo Pocket.sys/manifest.bml @@ -1 +1,2 @@ system name:Neo Geo Pocket + bios name=bios.rom size=0x10000 diff --git a/higan/ws/system/serialization.cpp b/higan/ws/system/serialization.cpp index a7c744b9..452b2aa5 100644 --- a/higan/ws/system/serialization.cpp +++ b/higan/ws/system/serialization.cpp @@ -2,8 +2,8 @@ auto System::serializeInit() -> void { serializer s; uint signature = 0; - char version[16] = {0}; - char description[512] = {0}; + char version[16] = {}; + char description[512] = {}; s.integer(signature); s.array(version); @@ -17,8 +17,8 @@ auto System::serialize() -> serializer { serializer s(_serializeSize); uint signature = 0x31545342; - char version[16] = {0}; - char description[512] = {0}; + char version[16] = {}; + char description[512] = {}; memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size()); s.integer(signature); @@ -31,8 +31,8 @@ auto System::serialize() -> serializer { auto System::unserialize(serializer& s) -> bool { uint signature = 0; - char version[16] = {0}; - char description[512] = {0}; + char version[16] = {}; + char description[512] = {}; s.integer(signature); s.array(version); diff --git a/icarus/heuristics/neo-geo-pocket-color.cpp b/icarus/heuristics/neo-geo-pocket-color.cpp index 3fd2d19e..6390f166 100644 --- a/icarus/heuristics/neo-geo-pocket-color.cpp +++ b/icarus/heuristics/neo-geo-pocket-color.cpp @@ -4,6 +4,7 @@ struct NeoGeoPocketColor { NeoGeoPocketColor(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; + auto title() const -> string; private: vector& data; @@ -14,18 +15,41 @@ NeoGeoPocketColor::NeoGeoPocketColor(vector& data, string location) : d } NeoGeoPocketColor::operator bool() const { - return (bool)data; + switch(data.size()) { + case 0x080000: return true; // 4mbit + case 0x100000: return true; // 8mbit + case 0x200000: return true; //16mbit + case 0x280000: return true; //20mbit + case 0x300000: return true; //24mbit + case 0x400000: return true; //32mbit + } + return false; } auto NeoGeoPocketColor::manifest() const -> string { + if(!operator bool()) return {}; + string output; output.append("game\n"); output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" label: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n"); + output.append(" title: ", title(), "\n"); output.append(" board\n"); output.append(Memory{}.type("ROM").size(data.size()).content("Program").text()); return output; } +auto NeoGeoPocketColor::title() const -> string { + if(!operator bool()) return {}; + + string title; + title.size(12); + for(uint index : range(12)) { + char letter = data[0x24 + index]; + if(letter >= 0x20 && letter <= 0x7e) title.get()[index] = letter; + } + return title.strip(); +} + } diff --git a/icarus/heuristics/neo-geo-pocket.cpp b/icarus/heuristics/neo-geo-pocket.cpp index c73f155a..46b014e9 100644 --- a/icarus/heuristics/neo-geo-pocket.cpp +++ b/icarus/heuristics/neo-geo-pocket.cpp @@ -4,6 +4,7 @@ struct NeoGeoPocket { NeoGeoPocket(vector& data, string location); explicit operator bool() const; auto manifest() const -> string; + auto title() const -> string; private: vector& data; @@ -14,18 +15,41 @@ NeoGeoPocket::NeoGeoPocket(vector& data, string location) : data(data), } NeoGeoPocket::operator bool() const { - return (bool)data; + switch(data.size()) { + case 0x080000: return true; // 4mbit + case 0x100000: return true; // 8mbit + case 0x200000: return true; //16mbit + case 0x280000: return true; //20mbit + case 0x300000: return true; //24mbit + case 0x400000: return true; //32mbit + } + return false; } auto NeoGeoPocket::manifest() const -> string { + if(!operator bool()) return {}; + string output; output.append("game\n"); output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" label: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n"); + output.append(" title: ", title(), "\n"); output.append(" board\n"); output.append(Memory{}.type("ROM").size(data.size()).content("Program").text()); return output; } +auto NeoGeoPocket::title() const -> string { + if(!operator bool()) return {}; + + string title; + title.size(12); + for(uint index : range(12)) { + char letter = data[0x24 + index]; + if(letter >= 0x20 && letter <= 0x7e) title.get()[index] = letter; + } + return title.strip(); +} + } diff --git a/nall/bit.hpp b/nall/bit.hpp index f2a54b6e..f48f0886 100644 --- a/nall/bit.hpp +++ b/nall/bit.hpp @@ -61,7 +61,7 @@ namespace bit { //count number of bits set in a byte inline auto count(uintmax x) -> uint { uint count = 0; - do count += x & 1; while(x >>= 1); + while(x) x &= x - 1, count++; //clear the least significant bit return count; } diff --git a/nall/inline-if.hpp b/nall/inline-if.hpp index 34473b97..b7b7a865 100644 --- a/nall/inline-if.hpp +++ b/nall/inline-if.hpp @@ -2,7 +2,7 @@ #warning "these defines break if statements with multiple parameters to templates" #define if1(statement) if(statement) -#define if2(condition, false) ([](auto&& value) -> decltype(condition) { \ +#define if2(condition, false) ([&](auto&& value) -> decltype(condition) { \ return (bool)value ? value : (decltype(condition))false; \ })(condition) #define if3(condition, true, false) ((condition) ? (true) : (decltype(true))(false)) diff --git a/nall/literals.hpp b/nall/literals.hpp new file mode 100644 index 00000000..304823be --- /dev/null +++ b/nall/literals.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace nall { + +inline constexpr auto operator"" _Kibit(unsigned long long value) { return value * 1024 / 8; } +inline constexpr auto operator"" _Mibit(unsigned long long value) { return value * 1024 * 1024 / 8; } +inline constexpr auto operator"" _Gibit(unsigned long long value) { return value * 1024 * 1024 * 1024 / 8; } +inline constexpr auto operator"" _Tibit(unsigned long long value) { return value * 1024 * 1024 * 1024 * 1024 / 8; } + +inline constexpr auto operator"" _KiB(unsigned long long value) { return value * 1024; } +inline constexpr auto operator"" _MiB(unsigned long long value) { return value * 1024 * 1024; } +inline constexpr auto operator"" _GiB(unsigned long long value) { return value * 1024 * 1024 * 1024; } +inline constexpr auto operator"" _TiB(unsigned long long value) { return value * 1024 * 1024 * 1024 * 1024; } + +inline constexpr auto operator"" _KHz(unsigned long long value) { return value * 1000; } +inline constexpr auto operator"" _MHz(unsigned long long value) { return value * 1000 * 1000; } +inline constexpr auto operator"" _GHz(unsigned long long value) { return value * 1000 * 1000 * 1000; } +inline constexpr auto operator"" _THz(unsigned long long value) { return value * 1000 * 1000 * 1000 * 1000; } + +} diff --git a/nall/nall.hpp b/nall/nall.hpp index 888866c9..e3720883 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/primitives/integer.hpp b/nall/primitives/integer.hpp index 39c0c57d..6b44c68a 100644 --- a/nall/primitives/integer.hpp +++ b/nall/primitives/integer.hpp @@ -28,10 +28,10 @@ template struct Integer { inline auto& operator++() { data = mask(data + 1); return *this; } inline auto& operator--() { data = mask(data - 1); return *this; } - inline auto operator!() const { return Integer{!data}; } - inline auto operator~() const { return Integer{~data}; } - inline auto operator+() const { return Integer<>{+(int64_t)data}; } - inline auto operator-() const { return Integer<>{-(int64_t)data}; } + inline auto operator!() const { return !data; } + inline auto operator~() const { return Integer<>{mask(~data)}; } + inline auto operator+() const { return Integer<>{+data}; } + inline auto operator-() const { return Integer<>{data == sign() ? data : -data}; } #define lhs data #define rhs value @@ -49,11 +49,6 @@ template struct Integer { #undef lhs #undef rhs - #define lhs (int64_t)data - #define rhs value - #undef lhs - #undef rhs - //warning: this does not and cannot short-circuit; value is always evaluated template inline auto orElse(const T& value) { return Integer<>{data ? data : value}; } diff --git a/nall/primitives/natural.hpp b/nall/primitives/natural.hpp index 55c823fb..1a663abb 100644 --- a/nall/primitives/natural.hpp +++ b/nall/primitives/natural.hpp @@ -26,9 +26,9 @@ template struct Natural { inline auto& operator++() { data = mask(data + 1); return *this; } inline auto& operator--() { data = mask(data - 1); return *this; } - inline auto operator!() const { return Natural{!data}; } - inline auto operator~() const { return Natural{~data}; } - inline auto operator+() const { return Natural<>{+(uint64_t)data}; } + inline auto operator!() const { return !data; } + inline auto operator~() const { return Natural<>{mask(~data)}; } + inline auto operator+() const { return Natural<>{+data}; } inline auto operator-() const { return Natural<>{-(uint64_t)data}; } #define lhs data diff --git a/sourcery/GNUmakefile b/sourcery/GNUmakefile new file mode 100644 index 00000000..14074580 --- /dev/null +++ b/sourcery/GNUmakefile @@ -0,0 +1,26 @@ +name := sourcery +build := stable +flags += -I.. + +nall.path := ../nall +include $(nall.path)/GNUmakefile + +objects := obj/sourcery.o + +obj/sourcery.o: sourcery.cpp + +all: $(objects) + $(info Linking out/$(name) ...) + +@$(compiler) -o out/$(name) $(objects) $(options) + +verbose: nall.verbose all; + +clean: + $(call delete,obj/*) + $(call delete,out/*) + +install: all + cp out/$(name) $(prefix)/bin/$(name) + +uninstall: + rm -f $(prefix)/bin/$(name) diff --git a/sourcery/sourcery.cpp b/sourcery/sourcery.cpp new file mode 100644 index 00000000..e010d30c --- /dev/null +++ b/sourcery/sourcery.cpp @@ -0,0 +1,93 @@ +#include +using namespace nall; + +struct Sourcery { + auto main(Arguments arguments) -> void; + auto parse(Markup::Node&) -> void; + +private: + string pathname; + file_buffer source; + file_buffer header; +}; + +auto Sourcery::main(Arguments arguments) -> void { + if(arguments.size() != 3) return print("usage: sourcery resource.bml resource.cpp resource.hpp\n"); + + string markupName = arguments.take(); + string sourceName = arguments.take(); + string headerName = arguments.take(); + if(!markupName.endsWith(".bml")) return print("error: arguments in incorrect order\n"); + if(!sourceName.endsWith(".cpp")) return print("error: arguments in incorrect order\n"); + if(!headerName.endsWith(".hpp")) return print("error: arguments in incorrect order\n"); + + string markup = string::read(markupName); + if(!markup) return print("error: unable to read resource manifest\n"); + + pathname = Location::path(markupName); + if(!source.open(sourceName, file::mode::write)) return print("error: unable to write source file\n"); + if(!header.open(headerName, file::mode::write)) return print("error: unable to write header file\n"); + + source.print("#include \"", headerName, "\"\n"); + source.print("\n"); + + auto document = BML::unserialize(markup); + parse(document); +} + +auto Sourcery::parse(Markup::Node& root) -> void { + for(auto node : root) { + if(node.name() == "namespace") { + header.print("namespace ", node["name"].text(), " {\n"); + source.print("namespace ", node["name"].text(), " {\n"); + parse(node); + header.print("}\n"); + source.print("}\n"); + } else if(node.name() == "binary") { + string filename{pathname, node["file"].text()}; + if(!file::exists(filename)) { + print("warning: binary file ", node["file"].text(), " not found\n"); + continue; + } + auto buffer = file::read(filename); + header.print("extern const unsigned char ", node["name"].text(), "[", buffer.size(), "];\n"); + source.print("const unsigned char ", node["name"].text(), "[", buffer.size(), "] = {\n"); + buffer.foreach([&](uint offset, int data) { + if((offset & 31) == 0) source.print(" "); + source.print(data, ","); + if((offset & 31) == 31) source.print("\n"); + }); + if(buffer.size() & 31) source.print("\n"); + source.print("};\n"); + } else if(node.name() == "string") { + string filename{pathname, node["file"].text()}; + if(!file::exists(filename)) { + print("warning: string file ", node["file"].text(), " not found\n"); + continue; + } + auto buffer = file::read(filename); + header.print("extern const char ", node["name"].text(), "[", buffer.size() + 1, "];\n"); + source.print("const char ", node["name"].text(), "[", buffer.size() + 1, "] =\n"); + buffer.foreach([&](uint offset, char data) { + if((offset & 127) == 0) source.print(" \""); + if(!data) source.print("\\0"); + else if(data == '\\') source.print("\\\\"); + else if(data == '\"') source.print("\\\""); + else if(data == '\?') source.print("\\\?"); + else if(data == '\r') source.print("\\r"); + else if(data == '\n') source.print("\\n"); + else if(data == '\t') source.print("\\t"); + else if(data <= 0x1f || data >= 0x7f) source.print("\\x", hex(data, 2L)); + else source.print(data); + if((offset & 127) == 127) source.print("\"\n"); + }); + if(buffer.size() & 127) source.print("\"\n"); + source.print(";\n"); + } + } +} + +#include +auto nall::main(Arguments arguments) -> void { + Sourcery().main(arguments); +}