Update to v106r84 release.

byuu says:

Changelog:

  - fixed a few TLCS900H CPU and disassembler bugs
  - hooked up a basic Neo Geo Pocket emulator skeleton and memory map;
    can run a few instructions from the BIOS
  - emulated the flash memory used by Neo Geo Pocket games
  - added sourcery to the higan source archives
  - fixed ternary expressions in sfc/ppu-fast [hex_usr]
This commit is contained in:
Tim Allen 2019-01-21 16:27:24 +11:00
parent 37b610da53
commit 53843934c0
51 changed files with 1040 additions and 124 deletions

View File

@ -8,6 +8,7 @@
#include <nall/dl.hpp> #include <nall/dl.hpp>
#include <nall/endian.hpp> #include <nall/endian.hpp>
#include <nall/image.hpp> #include <nall/image.hpp>
#include <nall/literals.hpp>
#include <nall/random.hpp> #include <nall/random.hpp>
#include <nall/serializer.hpp> #include <nall/serializer.hpp>
#include <nall/shared-pointer.hpp> #include <nall/shared-pointer.hpp>
@ -30,7 +31,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";

View File

@ -44,6 +44,14 @@ struct Readable {
inline auto read(uint address) const -> T { return self.data[address & self.mask]; } inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) const -> void {} 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: private:
struct { struct {
T* data = nullptr; T* data = nullptr;

View File

@ -46,6 +46,14 @@ struct Writable {
inline auto read(uint address) const -> T { return self.data[address & self.mask]; } 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; } 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: private:
struct { struct {
T* data = nullptr; T* data = nullptr;

View File

@ -24,7 +24,7 @@ Cartridge::~Cartridge() {
} }
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = Information(); information = {};
if(auto loaded = platform->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) { if(auto loaded = platform->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) {
information.pathID = loaded.pathID; information.pathID = loaded.pathID;

View File

@ -1,6 +1,12 @@
processors += tlcs900h z80 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-interface.o: ngp/interface/interface.cpp
obj/ngp-system.o: ngp/system/system.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

33
higan/ngp/apu/apu.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <ngp/ngp.hpp>
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);
}
}

22
higan/ngp/apu/apu.hpp Normal file
View File

@ -0,0 +1,22 @@
struct APU : Processor::Z80, Processor::Z80::Bus, Thread {
Emulator::Memory::Writable<uint8> 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;

View File

@ -0,0 +1,5 @@
auto APU::serialize(serializer& s) -> void {
Z80::serialize(s);
Z80::Bus::serialize(s);
Thread::serialize(s);
}

View File

@ -0,0 +1,73 @@
#include <ngp/ngp.hpp>
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);
}
}

View File

@ -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<uint8> rom;
boolean modified;
uint8 vendorID;
uint8 deviceID;
struct Block {
boolean writable;
natural offset;
natural length;
};
vector<Block> 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;

View File

@ -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);
}

View File

@ -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);
}

35
higan/ngp/cpu/cpu.cpp Normal file
View File

@ -0,0 +1,35 @@
#include <ngp/ngp.hpp>
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;
}
}

18
higan/ngp/cpu/cpu.hpp Normal file
View File

@ -0,0 +1,18 @@
struct CPU : Processor::TLCS900H, Thread {
Emulator::Memory::Writable<uint8> 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;

27
higan/ngp/cpu/memory.cpp Normal file
View File

@ -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);
}

View File

@ -0,0 +1,4 @@
auto CPU::serialize(serializer& s) -> void {
TLCS900H::serialize(s);
Thread::serialize(s);
}

View File

@ -9,38 +9,42 @@ namespace NeoGeoPocket {
auto Interface::display() -> Display { auto Interface::display() -> Display {
Display display; Display display;
display.type = Display::Type::LCD; display.type = Display::Type::LCD;
display.colors = 1; display.colors = 1 << 12;
display.width = 320; display.width = 160;
display.height = 240; display.height = 152;
display.internalWidth = 320; display.internalWidth = 160;
display.internalHeight = 240; display.internalHeight = 152;
display.aspectCorrection = 1.0; display.aspectCorrection = 1.0;
display.refreshRate = 60.0; display.refreshRate = 60.0;
return display; return display;
} }
auto Interface::color(uint32 color) -> uint64 { 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 { auto Interface::loaded() -> bool {
return false; return system.loaded();
} }
auto Interface::hashes() -> vector<string> { auto Interface::hashes() -> vector<string> {
return {}; return {cartridge.hash()};
} }
auto Interface::manifests() -> vector<string> { auto Interface::manifests() -> vector<string> {
return {}; return {cartridge.manifest()};
} }
auto Interface::titles() -> vector<string> { auto Interface::titles() -> vector<string> {
return {}; return {cartridge.title()};
}
auto Interface::load() -> bool {
return false;
} }
auto Interface::save() -> void { auto Interface::save() -> void {
@ -69,24 +73,30 @@ auto Interface::inputs(uint device) -> vector<Input> {
{Type::Hat, "Up" }, {Type::Hat, "Up" },
{Type::Hat, "Down" }, {Type::Hat, "Down" },
{Type::Hat, "Left" }, {Type::Hat, "Left" },
{Type::Hat, "Right"} {Type::Hat, "Right" },
{Type::Button, "A" },
{Type::Button, "B" },
{Type::Control, "Option"}
}; };
return {}; return {};
} }
auto Interface::power() -> void { auto Interface::power() -> void {
system.power();
} }
auto Interface::run() -> void { auto Interface::run() -> void {
system.run();
} }
auto Interface::serialize() -> serializer { auto Interface::serialize() -> serializer {
return {}; system.runToSave();
return system.serialize();
} }
auto Interface::unserialize(serializer& s) -> bool { auto Interface::unserialize(serializer& s) -> bool {
return false; return system.unserialize(s);
} }
} }

View File

@ -26,7 +26,6 @@ struct Interface : Emulator::Interface {
auto hashes() -> vector<string> override; auto hashes() -> vector<string> override;
auto manifests() -> vector<string> override; auto manifests() -> vector<string> override;
auto titles() -> vector<string> override; auto titles() -> vector<string> override;
auto load() -> bool override;
auto save() -> void override; auto save() -> void override;
auto unload() -> void override; auto unload() -> void override;
@ -43,10 +42,14 @@ struct Interface : Emulator::Interface {
struct NeoGeoPocketInterface : Interface { struct NeoGeoPocketInterface : Interface {
auto information() -> Information override; auto information() -> Information override;
auto load() -> bool override;
}; };
struct NeoGeoPocketColorInterface : Interface { struct NeoGeoPocketColorInterface : Interface {
auto information() -> Information override; auto information() -> Information override;
auto load() -> bool override;
}; };
} }

View File

@ -5,3 +5,7 @@ auto NeoGeoPocketColorInterface::information() -> Information {
information.extension = "ngpc"; information.extension = "ngpc";
return information; return information;
} }
auto NeoGeoPocketColorInterface::load() -> bool {
return system.load(this, System::Model::NeoGeoPocketColor);
}

View File

@ -5,3 +5,7 @@ auto NeoGeoPocketInterface::information() -> Information {
information.extension = "ngp"; information.extension = "ngp";
return information; return information;
} }
auto NeoGeoPocketInterface::load() -> bool {
return system.load(this, System::Model::NeoGeoPocket);
}

View File

@ -36,6 +36,11 @@ namespace NeoGeoPocket {
}; };
#include <ngp/system/system.hpp> #include <ngp/system/system.hpp>
#include <ngp/cartridge/cartridge.hpp>
#include <ngp/cpu/cpu.hpp>
#include <ngp/apu/apu.hpp>
#include <ngp/vpu/vpu.hpp>
#include <ngp/psg/psg.hpp>
} }
#include <ngp/interface/interface.hpp> #include <ngp/interface/interface.hpp>

29
higan/ngp/psg/psg.cpp Normal file
View File

@ -0,0 +1,29 @@
#include <ngp/ngp.hpp>
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();
}
}

16
higan/ngp/psg/psg.hpp Normal file
View File

@ -0,0 +1,16 @@
//Texas Instruments SN76489A
struct PSG : Thread {
shared_pointer<Emulator::Stream> 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;

View File

@ -0,0 +1,3 @@
auto PSG::serialize(serializer& s) -> void {
Thread::serialize(s);
}

View File

@ -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 {
}

View File

@ -5,5 +5,72 @@ namespace NeoGeoPocket {
System system; System system;
Scheduler scheduler; Scheduler scheduler;
Cheat cheat; 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);
}
} }

View File

@ -1,14 +1,35 @@
struct System { struct System {
enum class Model : uint { NeoGeoPocket, NeoGeoPocketColor }; enum class Model : uint { NeoGeoPocket, NeoGeoPocketColor };
Emulator::Memory::Readable<uint8> bios;
auto loaded() const -> bool { return information.loaded; } inline auto loaded() const -> bool { return information.loaded; }
auto model() const -> Model { return information.model; } 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 { struct Information {
string manifest; string manifest;
bool loaded = false; bool loaded = false;
Model model = Model::NeoGeoPocket; Model model = Model::NeoGeoPocket;
natural serializeSize;
} information; } information;
private:
Emulator::Interface* interface = nullptr;
}; };
extern System system; extern System system;

View File

@ -0,0 +1,3 @@
auto VPU::serialize(serializer& s) -> void {
Thread::serialize(s);
}

31
higan/ngp/vpu/vpu.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <ngp/ngp.hpp>
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);
}
}

21
higan/ngp/vpu/vpu.hpp Normal file
View File

@ -0,0 +1,21 @@
//K1GE (Neo Geo Pocket)
//K2GE (Neo Geo Pocket Color)
struct VPU : Thread {
Emulator::Memory::Writable<uint8> 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;

View File

@ -94,8 +94,8 @@ auto TLCS900H::disassemble() -> string {
boolean opSourceMemory; boolean opSourceMemory;
boolean opTargetMemory; 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()) { switch(auto fetch = read8()) {
case 0x00: name = "nop"; break; case 0x00: name = "nop"; break;
case 0x01: 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: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
opSourceMemory = true; lhs.indirectRegister3(8, fetch); break; opSourceMemory = true; lhs.indirectRegister3(8, fetch); break;
case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: 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: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
opSourceMemory = true; lhs.indirectRegister3(16, fetch); break; opSourceMemory = true; lhs.indirectRegister3(16, fetch); break;
case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: 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; opTargetMemory = true; lhs.indirectRegister3Displacement8(0, fetch, read8()); break;
case 0xc0: case 0xd0: case 0xe0: case 0xf0: case 0xc0: case 0xd0: case 0xe0: case 0xf0:
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; 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: case 0xc1: case 0xd1: case 0xe1: case 0xf1:
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; 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: case 0xc2: case 0xd2: case 0xe2: case 0xf2:
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; 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: { case 0xc3: case 0xd3: case 0xe3: case 0xf3: {
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory;
auto data = read8(); auto data = read8();
if((uint2)data == 0) lhs.indirectRegister(opcodeSizes[fetch >> 4], data); if((uint2)data == 0) lhs.indirectRegister(opSize, data);
if((uint2)data == 1) lhs.indirectRegisterDisplacement16(opcodeSizes[fetch >> 4], data, read16()); if((uint2)data == 1) lhs.indirectRegisterDisplacement16(opSize, data, read16());
if(data == 0x03) { auto r32 = read8(); lhs.indirectRegisterRegister8(opcodeSizes[fetch >> 4], r32, read8()); } if(data == 0x03) { auto r32 = read8(); lhs.indirectRegisterRegister8(opSize, r32, read8()); }
if(data == 0x07) { auto r32 = read8(); lhs.indirectRegisterRegister16(opcodeSizes[fetch >> 4], r32, read16()); } if(data == 0x07) { auto r32 = read8(); lhs.indirectRegisterRegister16(opSize, r32, read16()); }
} break; } break;
case 0xc4: case 0xd4: case 0xe4: case 0xf4: case 0xc4: case 0xd4: case 0xe4: case 0xf4:
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; 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: case 0xc5: case 0xd5: case 0xe5: case 0xf5:
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory; 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 0xc6: case 0xd6: case 0xe6: case 0xf6: break;
case 0xc7: case 0xd7: case 0xe7: case 0xc7: case 0xd7: case 0xe7:
opRegister = true; opRegister = true;
lhs.indirectRegister(opcodeSizes[fetch >> 4], read8()); break; lhs.indirectRegister(opSize, read8()); break;
case 0xf7: case 0xf7:
name = "ldx"; name = "ldx";
read8(); lhs.indirectImmediate8(8, read8()); 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: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
name = "swi"; lhs.immediate(3, (uint3)fetch); break; name = "swi"; lhs.immediate(3, (uint3)fetch); break;
} }
#undef opSize
auto reads = [&](uint size) -> uint32 { auto reads = [&](uint size) -> uint32 {
if(size == 8) return read8(); if(size == 8) return read8();
@ -344,7 +345,7 @@ auto TLCS900H::disassemble() -> string {
if(opSourceMemory) if(opSourceMemory)
switch(auto fetch = read8()) { switch(auto fetch = read8()) {
case 0x00: case 0x01: case 0x02: case 0x03: break; 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 0x05: break;
case 0x06: name = "rld"; rhs = lhs; lhs.register(8, A.id); 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; 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 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: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
name = "ex"; rhs.register3(lhs.size(), fetch); break; name = "ex"; rhs.register3(lhs.size(), fetch); break;
case 0x38: name = "add"; rhs.immediate(3, (uint3)fetch); break; case 0x38: name = "add"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x39: name = "adc"; rhs.immediate(3, (uint3)fetch); break; case 0x39: name = "adc"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3a: name = "sub"; rhs.immediate(3, (uint3)fetch); break; case 0x3a: name = "sub"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3b: name = "sbb"; rhs.immediate(3, (uint3)fetch); break; case 0x3b: name = "sbb"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3c: name = "and"; rhs.immediate(3, (uint3)fetch); break; case 0x3c: name = "and"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3d: name = "xor"; rhs.immediate(3, (uint3)fetch); break; case 0x3d: name = "xor"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3e: name = "or"; rhs.immediate(3, (uint3)fetch); break; case 0x3e: name = "or"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
case 0x3f: name = "cp"; rhs.immediate(3, (uint3)fetch); 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: 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; 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: 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 { auto register8 = [](uint8 register) -> string {
if(register < 0x40) return registers8[register]; if(register < 0x40) return registers8[register];
if(register >= 0xd0) return registers8[register - 0x80]; if(register >= 0xd0) return registers8[(register - 0xd0 >> 0) + 0x40];
return "rb?"; return "rb?";
}; };
@ -556,7 +557,7 @@ auto TLCS900H::disassemble() -> string {
}; };
auto register16 = [](uint8 register) -> string { auto register16 = [](uint8 register) -> string {
if(register < 0x40) return registers16[register >> 1]; 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?"; return "rw?";
}; };
@ -571,7 +572,7 @@ auto TLCS900H::disassemble() -> string {
}; };
auto register32 = [&](uint8 register) -> string { auto register32 = [&](uint8 register) -> string {
if(register < 0x40) return registers32[register >> 2]; 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?"; return "rl?";
}; };
@ -631,12 +632,12 @@ auto TLCS900H::disassemble() -> string {
if(operand.size() == 8) { if(operand.size() == 8) {
integer displacement = (int8)operand.displacement(); integer displacement = (int8)operand.displacement();
if(displacement < 0) return {"-0x", hex(-displacement, 2L)}; if(displacement < 0) return {"-0x", hex(-displacement, 2L)};
return {"+0x", hex(displacement, 2L)}; if(displacement >= 0) return {"+0x", hex(+displacement, 2L)};
} }
if(operand.size() == 16) { if(operand.size() == 16) {
integer displacement = (int8)operand.displacement(); integer displacement = (int8)operand.displacement();
if(displacement < 0) return {"-0x", hex(-displacement, 4L)}; if(displacement < 0) return {"-0x", hex(-displacement, 4L)};
return {"+0x", hex(displacement, 4L)}; if(displacement >= 0) return {"+0x", hex(+displacement, 4L)};
} }
} }
if(operand.mode() == DisplacementPC) { 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() == IndirectRegisterRegister16) return {"(", register32(operand.register()), "+", register16(operand.registerAdd()), ")"};
if(operand.mode() == IndirectRegisterDisplacement8) { if(operand.mode() == IndirectRegisterDisplacement8) {
integer displacement = (int8)operand.displacement(); integer displacement = (int8)operand.displacement();
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), ")"};
return {"(", register32(operand.register()), "+0x", hex(displacement, 2L), ")"}; if(displacement > 0) return {"(", register32(operand.register()), "+0x", hex(+displacement, 2L), ")"};
} }
if(operand.mode() == IndirectRegisterDisplacement16) { if(operand.mode() == IndirectRegisterDisplacement16) {
integer displacement = (int16)operand.displacement(); integer displacement = (int16)operand.displacement();
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), ")"};
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() == IndirectImmediate8) return {"(0x", hex(operand.immediate(), 2L), ")"};
if(operand.mode() == IndirectImmediate16) return {"(0x", hex(operand.immediate(), 4L), ")"}; if(operand.mode() == IndirectImmediate16) return {"(0x", hex(operand.immediate(), 4L), ")"};
@ -670,19 +673,28 @@ auto TLCS900H::disassemble() -> string {
return {}; return {};
}; };
//omit true condition operand
if(lhs.mode() == Condition && lhs.condition() == 8) {
lhs = rhs;
rhs.null();
}
if(name) { if(name) {
output.append(pad(name, -6)); output.append(pad(name, -6));
if(lhs) { if(lhs) {
output.append(operand(lhs), ","); output.append(operand(lhs));
if(rhs) { if(rhs) {
output.append(operand(rhs)); output.append(",", operand(rhs));
} }
} }
} else { } else {
output.append(pad("???", -6)); 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.size(-48);
output.append("XWA:", hex(r.xwa[r.rfp].l.l0, 8L), " "); output.append("XWA:", hex(r.xwa[r.rfp].l.l0, 8L), " ");
output.append("XBC:", hex(r.xbc[r.rfp].l.l0, 8L), " "); output.append("XBC:", hex(r.xbc[r.rfp].l.l0, 8L), " ");

View File

@ -382,23 +382,23 @@ auto TLCS900H::instructionRegister(R register) -> void {
return (void)Undefined; return (void)Undefined;
case 0x35: case 0x36: case 0x37: return (void)Undefined; case 0x35: case 0x36: case 0x37: return (void)Undefined;
case 0x38: case 0x38:
if constexpr(bits == 16) return instructionModuloIncrement<1>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloIncrement<1>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x39: case 0x39:
if constexpr(bits == 16) return instructionModuloIncrement<2>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloIncrement<2>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x3a: case 0x3a:
if constexpr(bits == 16) return instructionModuloIncrement<4>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloIncrement<4>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x3b: return (void)Undefined; case 0x3b: return (void)Undefined;
case 0x3c: case 0x3c:
if constexpr(bits == 16) return instructionModuloDecrement<1>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloDecrement<1>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x3d: case 0x3d:
if constexpr(bits == 16) return instructionModuloDecrement<2>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloDecrement<2>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x3e: case 0x3e:
if constexpr(bits == 16) return instructionModuloDecrement<4>(register, fetchImmediate<int16>()); if constexpr(bits == 16) return instructionModuloDecrement<4>(register, fetchImmediate<uint16>());
return (void)Undefined; return (void)Undefined;
case 0x3f: return (void)Undefined; case 0x3f: return (void)Undefined;
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:

View File

@ -227,6 +227,7 @@ template<uint Modulo, typename Target, typename Source>
auto TLCS900H::instructionModuloDecrement(Target target, Source source) -> void { auto TLCS900H::instructionModuloDecrement(Target target, Source source) -> void {
auto result = load(target); auto result = load(target);
auto number = load(source); 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) { if(result % number == 0) {
store(target, result + (number - Modulo)); store(target, result + (number - Modulo));
} else { } else {
@ -238,6 +239,7 @@ template<uint Modulo, typename Target, typename Source>
auto TLCS900H::instructionModuloIncrement(Target target, Source source) -> void { auto TLCS900H::instructionModuloIncrement(Target target, Source source) -> void {
auto result = load(target); auto result = load(target);
auto number = load(source); 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) { if(result % number == number - Modulo) {
store(target, result - (number - Modulo)); store(target, result - (number - Modulo));
} else { } else {
@ -479,7 +481,10 @@ auto TLCS900H::instructionStoreCarry(Target target, Offset offset) -> void {
} }
auto TLCS900H::instructionSoftwareInterrupt(uint3 interrupt) -> void { auto TLCS900H::instructionSoftwareInterrupt(uint3 interrupt) -> void {
//TODO push(PC);
push(SR);
store(PC, load(Memory<uint24>{0xffff00 + interrupt * 4}));
store(INTNEST, load(INTNEST) + 1); //note: not confirmed behavior
} }
template<typename Target, typename Source> template<typename Target, typename Source>

View File

@ -1,25 +1,29 @@
#define PC r.pc.l.l0
template<> auto TLCS900H::fetch< uint8>() -> uint8 { template<> auto TLCS900H::fetch< uint8>() -> uint8 {
return rand(); return read(PC++);
} }
template<> auto TLCS900H::fetch<uint16>() -> uint16 { template<> auto TLCS900H::fetch<uint16>() -> uint16 {
uint16 data = fetch<uint8>(); uint16 data = read(PC++) << 0;
return data | fetch<uint8>() << 8; return data | read(PC++) << 8;
} }
template<> auto TLCS900H::fetch<uint24>() -> uint24 { template<> auto TLCS900H::fetch<uint24>() -> uint24 {
uint24 data = fetch<uint8>(); uint24 data = read(PC++) << 0;
data |= fetch<uint8>() << 8; data |= read(PC++) << 8;
return data |= fetch<uint8>() << 16; return data |= read(PC++) << 16;
} }
template<> auto TLCS900H::fetch<uint32>() -> uint32 { template<> auto TLCS900H::fetch<uint32>() -> uint32 {
uint32 data = fetch<uint8>(); uint32 data = read(PC++) << 0;
data |= fetch<uint8>() << 8; data |= read(PC++) << 8;
data |= fetch<uint8>() << 16; data |= read(PC++) << 16;
return data |= fetch<uint8>() << 24; return data |= read(PC++) << 24;
} }
#undef PC
template<> auto TLCS900H::fetch< int8>() -> int8 { return ( int8)fetch< uint8>(); } template<> auto TLCS900H::fetch< int8>() -> int8 { return ( int8)fetch< uint8>(); }
template<> auto TLCS900H::fetch<int16>() -> int16 { return (int16)fetch<uint16>(); } template<> auto TLCS900H::fetch<int16>() -> int16 { return (int16)fetch<uint16>(); }
template<> auto TLCS900H::fetch<int24>() -> int24 { return (int24)fetch<uint24>(); } template<> auto TLCS900H::fetch<int24>() -> int24 { return (int24)fetch<uint24>(); }
@ -35,19 +39,21 @@ template<typename T> auto TLCS900H::fetchImmediate() -> Immediate<T> { return Im
template<typename T> auto TLCS900H::pop(T data) -> void { template<typename T> auto TLCS900H::pop(T data) -> void {
auto value = typename T::type(); auto value = typename T::type();
if constexpr(T::bits >= 8) value.byte(0) = read(XSP++); if constexpr(T::bits >= 8) value.byte(0) = read(XSP + 0);
if constexpr(T::bits >= 16) value.byte(1) = read(XSP++); if constexpr(T::bits >= 16) value.byte(1) = read(XSP + 1);
if constexpr(T::bits >= 24) value.byte(2) = read(XSP++); if constexpr(T::bits >= 24) value.byte(2) = read(XSP + 2);
if constexpr(T::bits >= 32) value.byte(3) = read(XSP++); if constexpr(T::bits >= 32) value.byte(3) = read(XSP + 3);
store(data, value); store(data, value);
XSP += T::bits >> 3;
} }
template<typename T> auto TLCS900H::push(T data) -> void { template<typename T> auto TLCS900H::push(T data) -> void {
XSP -= T::bits >> 3;
auto value = load(data); auto value = load(data);
if constexpr(T::bits >= 8) write(--XSP, value >> 0); if constexpr(T::bits >= 8) write(XSP + 0, value >> 0);
if constexpr(T::bits >= 16) write(--XSP, value >> 8); if constexpr(T::bits >= 16) write(XSP + 1, value >> 8);
if constexpr(T::bits >= 24) write(--XSP, value >> 16); if constexpr(T::bits >= 24) write(XSP + 2, value >> 16);
if constexpr(T::bits >= 32) write(--XSP, value >> 24); if constexpr(T::bits >= 32) write(XSP + 3, value >> 24);
} }
#undef XSP #undef XSP
@ -63,6 +69,12 @@ template<> auto TLCS900H::load(Memory<uint16> memory) -> uint16 {
return data | read(memory.address + 1) << 8; return data | read(memory.address + 1) << 8;
} }
template<> auto TLCS900H::load(Memory<uint24> 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<uint32> memory) -> uint32 { template<> auto TLCS900H::load(Memory<uint32> memory) -> uint32 {
uint32 data = read(memory.address + 0) << 0; uint32 data = read(memory.address + 0) << 0;
data |= read(memory.address + 1) << 8; data |= read(memory.address + 1) << 8;
@ -72,6 +84,7 @@ template<> auto TLCS900H::load(Memory<uint32> memory) -> uint32 {
template<> auto TLCS900H::load(Memory< int8> memory) -> int8 { return (int8)load< uint8>(Memory< uint8>{memory.address}); } template<> auto TLCS900H::load(Memory< int8> memory) -> int8 { return (int8)load< uint8>(Memory< uint8>{memory.address}); }
template<> auto TLCS900H::load(Memory<int16> memory) -> int16 { return (int16)load<uint16>(Memory<uint16>{memory.address}); } template<> auto TLCS900H::load(Memory<int16> memory) -> int16 { return (int16)load<uint16>(Memory<uint16>{memory.address}); }
template<> auto TLCS900H::load(Memory<int24> memory) -> int24 { return (int24)load<uint24>(Memory<uint24>{memory.address}); }
template<> auto TLCS900H::load(Memory<int32> memory) -> int32 { return (int32)load<uint32>(Memory<uint32>{memory.address}); } template<> auto TLCS900H::load(Memory<int32> memory) -> int32 { return (int32)load<uint32>(Memory<uint32>{memory.address}); }
template<> auto TLCS900H::store(Memory< uint8> memory, uint32 data) -> void { template<> auto TLCS900H::store(Memory< uint8> memory, uint32 data) -> void {

View File

@ -24,13 +24,15 @@ namespace Processor {
#include "serialization.cpp" #include "serialization.cpp"
#include "disassembler.cpp" #include "disassembler.cpp"
TLCS900H tlcs900h; auto TLCS900H::interrupt(uint24 address) -> void {
push(PC);
push(SR);
store(PC, load(Memory<uint24>{address}));
store(INTNEST, load(INTNEST) + 1);
}
auto TLCS900H::power() -> void { auto TLCS900H::power() -> void {
r = {}; r = {};
} }
TLCS900H::TLCS900H() {
}
} }

View File

@ -9,6 +9,7 @@
* what value is read back from a non-existent 8-bit register ID? (eg 0x40-0xcf) * 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? * 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 (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 #pragma once
@ -16,22 +17,22 @@
namespace Processor { namespace Processor {
struct TLCS900H { struct TLCS900H {
virtual auto read(uint24 address) -> uint8 { return 0; } virtual auto step(uint clocks) -> void = 0;
virtual auto write(uint24 address, uint8 data) -> void {}; virtual auto read(uint24 address) -> uint8 = 0;
virtual auto write(uint24 address, uint8 data) -> void = 0;
TLCS900H();
struct FlagRegister { using type = uint8; enum : uint { bits = 8 }; uint1 id; }; struct FlagRegister { using type = uint8; enum : uint { bits = 8 }; uint1 id; };
struct StatusRegister { using type = uint16; enum : uint { bits = 16 }; }; struct StatusRegister { using type = uint16; enum : uint { bits = 16 }; };
struct ProgramCounter { using type = uint32; enum : uint { bits = 32 }; }; struct ProgramCounter { using type = uint32; enum : uint { bits = 32 }; };
template<typename T> struct ControlRegister { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; template<typename T> struct ControlRegister { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; };
template<typename T> struct Register { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; }; template<typename T> struct Register { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint8 id; };
template<typename T> struct Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; T address; }; template<typename T> struct Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint32 address; };
template<typename T> struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; T constant; }; template<typename T> struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; uint32 constant; };
template<typename T> auto load(Immediate<T> immediate) const -> T { return immediate.constant; } template<typename T> auto load(Immediate<T> immediate) const -> T { return immediate.constant; }
//tlcs900h.cpp //tlcs900h.cpp
auto interrupt(uint24 vector) -> void;
auto power() -> void; auto power() -> void;
//registers.cpp //registers.cpp
@ -261,7 +262,7 @@ struct TLCS900H {
static inline const uint1 Undefined = 0; static inline const uint1 Undefined = 0;
//disassembler.cpp //disassembler.cpp
virtual auto disassembleRead(uint24 address) -> uint8 { return rand(); } virtual auto disassembleRead(uint24 address) -> uint8 { return read(address); }
auto disassemble() -> string; auto disassemble() -> string;
}; };

View File

@ -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 hmask = (width << self.tileSize << self.screenSize.bit(0)) - 1;
uint vmask = (width << self.tileSize << self.screenSize.bit(1)) - 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) { if(hires) {
hscroll <<= 1; hscroll <<= 1;
if(io.interlace) y = y << 1 | ppu.field(); if(io.interlace) y = y << 1 | ppu.field();

View File

@ -1,5 +1,5 @@
auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { 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 y = !io.mode7.vflip ? Y : 255 - Y;
int a = (int16)io.mode7.a; int a = (int16)io.mode7.a;

View File

@ -1 +1,2 @@
system name:Neo Geo Pocket Color system name:Neo Geo Pocket Color
bios name=bios.rom size=0x10000

View File

@ -1 +1,2 @@
system name:Neo Geo Pocket system name:Neo Geo Pocket
bios name=bios.rom size=0x10000

View File

@ -2,8 +2,8 @@ auto System::serializeInit() -> void {
serializer s; serializer s;
uint signature = 0; uint signature = 0;
char version[16] = {0}; char version[16] = {};
char description[512] = {0}; char description[512] = {};
s.integer(signature); s.integer(signature);
s.array(version); s.array(version);
@ -17,8 +17,8 @@ auto System::serialize() -> serializer {
serializer s(_serializeSize); serializer s(_serializeSize);
uint signature = 0x31545342; uint signature = 0x31545342;
char version[16] = {0}; char version[16] = {};
char description[512] = {0}; char description[512] = {};
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size()); memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
s.integer(signature); s.integer(signature);
@ -31,8 +31,8 @@ auto System::serialize() -> serializer {
auto System::unserialize(serializer& s) -> bool { auto System::unserialize(serializer& s) -> bool {
uint signature = 0; uint signature = 0;
char version[16] = {0}; char version[16] = {};
char description[512] = {0}; char description[512] = {};
s.integer(signature); s.integer(signature);
s.array(version); s.array(version);

View File

@ -4,6 +4,7 @@ struct NeoGeoPocketColor {
NeoGeoPocketColor(vector<uint8_t>& data, string location); NeoGeoPocketColor(vector<uint8_t>& data, string location);
explicit operator bool() const; explicit operator bool() const;
auto manifest() const -> string; auto manifest() const -> string;
auto title() const -> string;
private: private:
vector<uint8_t>& data; vector<uint8_t>& data;
@ -14,18 +15,41 @@ NeoGeoPocketColor::NeoGeoPocketColor(vector<uint8_t>& data, string location) : d
} }
NeoGeoPocketColor::operator bool() const { 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 { auto NeoGeoPocketColor::manifest() const -> string {
if(!operator bool()) return {};
string output; string output;
output.append("game\n"); output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n"); output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title(), "\n");
output.append(" board\n"); output.append(" board\n");
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text()); output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
return output; 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();
}
} }

View File

@ -4,6 +4,7 @@ struct NeoGeoPocket {
NeoGeoPocket(vector<uint8_t>& data, string location); NeoGeoPocket(vector<uint8_t>& data, string location);
explicit operator bool() const; explicit operator bool() const;
auto manifest() const -> string; auto manifest() const -> string;
auto title() const -> string;
private: private:
vector<uint8_t>& data; vector<uint8_t>& data;
@ -14,18 +15,41 @@ NeoGeoPocket::NeoGeoPocket(vector<uint8_t>& data, string location) : data(data),
} }
NeoGeoPocket::operator bool() const { 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 { auto NeoGeoPocket::manifest() const -> string {
if(!operator bool()) return {};
string output; string output;
output.append("game\n"); output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n"); output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title(), "\n");
output.append(" board\n"); output.append(" board\n");
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text()); output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
return output; 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();
}
} }

View File

@ -61,7 +61,7 @@ namespace bit {
//count number of bits set in a byte //count number of bits set in a byte
inline auto count(uintmax x) -> uint { inline auto count(uintmax x) -> uint {
uint count = 0; uint count = 0;
do count += x & 1; while(x >>= 1); while(x) x &= x - 1, count++; //clear the least significant bit
return count; return count;
} }

View File

@ -2,7 +2,7 @@
#warning "these defines break if statements with multiple parameters to templates" #warning "these defines break if statements with multiple parameters to templates"
#define if1(statement) if(statement) #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; \ return (bool)value ? value : (decltype(condition))false; \
})(condition) })(condition)
#define if3(condition, true, false) ((condition) ? (true) : (decltype(true))(false)) #define if3(condition, true, false) ((condition) ? (true) : (decltype(true))(false))

20
nall/literals.hpp Normal file
View File

@ -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; }
}

View File

@ -39,6 +39,7 @@
#include <nall/interpolation.hpp> #include <nall/interpolation.hpp>
#include <nall/intrinsics.hpp> #include <nall/intrinsics.hpp>
#include <nall/iterator.hpp> #include <nall/iterator.hpp>
#include <nall/literals.hpp>
#include <nall/locale.hpp> #include <nall/locale.hpp>
#include <nall/location.hpp> #include <nall/location.hpp>
#include <nall/map.hpp> #include <nall/map.hpp>

View File

@ -28,10 +28,10 @@ template<uint Precision = 64> struct Integer {
inline auto& operator++() { data = mask(data + 1); return *this; } inline auto& operator++() { data = mask(data + 1); return *this; }
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 !data; }
inline auto operator~() const { return Integer{~data}; } inline auto operator~() const { return Integer<>{mask(~data)}; }
inline auto operator+() const { return Integer<>{+(int64_t)data}; } inline auto operator+() const { return Integer<>{+data}; }
inline auto operator-() const { return Integer<>{-(int64_t)data}; } inline auto operator-() const { return Integer<>{data == sign() ? data : -data}; }
#define lhs data #define lhs data
#define rhs value #define rhs value
@ -49,11 +49,6 @@ template<uint Precision = 64> struct Integer {
#undef lhs #undef lhs
#undef rhs #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 //warning: this does not and cannot short-circuit; value is always evaluated
template<typename T> inline auto orElse(const T& value) { return Integer<>{data ? data : value}; } template<typename T> inline auto orElse(const T& value) { return Integer<>{data ? data : value}; }

View File

@ -26,9 +26,9 @@ template<uint Precision = 64> struct Natural {
inline auto& operator++() { data = mask(data + 1); return *this; } inline auto& operator++() { data = mask(data + 1); return *this; }
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 !data; }
inline auto operator~() const { return Natural{~data}; } inline auto operator~() const { return Natural<>{mask(~data)}; }
inline auto operator+() const { return Natural<>{+(uint64_t)data}; } inline auto operator+() const { return Natural<>{+data}; }
inline auto operator-() const { return Natural<>{-(uint64_t)data}; } inline auto operator-() const { return Natural<>{-(uint64_t)data}; }
#define lhs data #define lhs data

26
sourcery/GNUmakefile Normal file
View File

@ -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)

93
sourcery/sourcery.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <nall/nall.hpp>
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 <nall/main.hpp>
auto nall::main(Arguments arguments) -> void {
Sourcery().main(arguments);
}