mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
37b610da53
commit
53843934c0
|
@ -8,6 +8,7 @@
|
|||
#include <nall/dl.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/shared-pointer.hpp>
|
||||
|
@ -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/";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,5 @@
|
|||
auto APU::serialize(serializer& s) -> void {
|
||||
Z80::serialize(s);
|
||||
Z80::Bus::serialize(s);
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
auto CPU::serialize(serializer& s) -> void {
|
||||
TLCS900H::serialize(s);
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -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<string> {
|
||||
return {};
|
||||
return {cartridge.hash()};
|
||||
}
|
||||
|
||||
auto Interface::manifests() -> vector<string> {
|
||||
return {};
|
||||
return {cartridge.manifest()};
|
||||
}
|
||||
|
||||
auto Interface::titles() -> vector<string> {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::load() -> bool {
|
||||
return false;
|
||||
return {cartridge.title()};
|
||||
}
|
||||
|
||||
auto Interface::save() -> void {
|
||||
|
@ -66,27 +70,33 @@ auto Interface::inputs(uint device) -> vector<Input> {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ struct Interface : Emulator::Interface {
|
|||
auto hashes() -> vector<string> override;
|
||||
auto manifests() -> vector<string> override;
|
||||
auto titles() -> vector<string> 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,3 +5,7 @@ auto NeoGeoPocketColorInterface::information() -> Information {
|
|||
information.extension = "ngpc";
|
||||
return information;
|
||||
}
|
||||
|
||||
auto NeoGeoPocketColorInterface::load() -> bool {
|
||||
return system.load(this, System::Model::NeoGeoPocketColor);
|
||||
}
|
||||
|
|
|
@ -5,3 +5,7 @@ auto NeoGeoPocketInterface::information() -> Information {
|
|||
information.extension = "ngp";
|
||||
return information;
|
||||
}
|
||||
|
||||
auto NeoGeoPocketInterface::load() -> bool {
|
||||
return system.load(this, System::Model::NeoGeoPocket);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@ namespace NeoGeoPocket {
|
|||
};
|
||||
|
||||
#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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,3 @@
|
|||
auto PSG::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +1,35 @@
|
|||
struct System {
|
||||
enum class Model : uint { NeoGeoPocket, NeoGeoPocketColor };
|
||||
Emulator::Memory::Readable<uint8> 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;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
auto VPU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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), " ");
|
||||
|
|
|
@ -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<int16>());
|
||||
if constexpr(bits == 16) return instructionModuloIncrement<1>(register, fetchImmediate<uint16>());
|
||||
return (void)Undefined;
|
||||
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;
|
||||
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;
|
||||
case 0x3b: return (void)Undefined;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
case 0x3f: return (void)Undefined;
|
||||
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
|
||||
|
|
|
@ -227,6 +227,7 @@ template<uint Modulo, typename Target, typename Source>
|
|||
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<uint Modulo, typename Target, typename Source>
|
|||
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<uint24>{0xffff00 + interrupt * 4}));
|
||||
store(INTNEST, load(INTNEST) + 1); //note: not confirmed behavior
|
||||
}
|
||||
|
||||
template<typename Target, typename Source>
|
||||
|
|
|
@ -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 {
|
||||
uint16 data = fetch<uint8>();
|
||||
return data | fetch<uint8>() << 8;
|
||||
uint16 data = read(PC++) << 0;
|
||||
return data | read(PC++) << 8;
|
||||
}
|
||||
|
||||
template<> auto TLCS900H::fetch<uint24>() -> uint24 {
|
||||
uint24 data = fetch<uint8>();
|
||||
data |= fetch<uint8>() << 8;
|
||||
return data |= fetch<uint8>() << 16;
|
||||
uint24 data = read(PC++) << 0;
|
||||
data |= read(PC++) << 8;
|
||||
return data |= read(PC++) << 16;
|
||||
}
|
||||
|
||||
template<> auto TLCS900H::fetch<uint32>() -> uint32 {
|
||||
uint32 data = fetch<uint8>();
|
||||
data |= fetch<uint8>() << 8;
|
||||
data |= fetch<uint8>() << 16;
|
||||
return data |= fetch<uint8>() << 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>() -> int16 { return (int16)fetch<uint16>(); }
|
||||
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 {
|
||||
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<typename T> 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<uint16> memory) -> uint16 {
|
|||
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 {
|
||||
uint32 data = read(memory.address + 0) << 0;
|
||||
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<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::store(Memory< uint8> memory, uint32 data) -> void {
|
||||
|
|
|
@ -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<uint24>{address}));
|
||||
store(INTNEST, load(INTNEST) + 1);
|
||||
}
|
||||
|
||||
auto TLCS900H::power() -> void {
|
||||
r = {};
|
||||
}
|
||||
|
||||
TLCS900H::TLCS900H() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<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 Memory { using type = T; enum : uint { bits = 8 * sizeof(T) }; T address; };
|
||||
template<typename T> struct Immediate { using type = T; enum : uint { bits = 8 * sizeof(T) }; T constant; };
|
||||
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 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) }; uint32 constant; };
|
||||
|
||||
template<typename T> auto load(Immediate<T> 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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
system name:Neo Geo Pocket Color
|
||||
bios name=bios.rom size=0x10000
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
system name:Neo Geo Pocket
|
||||
bios name=bios.rom size=0x10000
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,6 +4,7 @@ struct NeoGeoPocketColor {
|
|||
NeoGeoPocketColor(vector<uint8_t>& data, string location);
|
||||
explicit operator bool() const;
|
||||
auto manifest() const -> string;
|
||||
auto title() const -> string;
|
||||
|
||||
private:
|
||||
vector<uint8_t>& data;
|
||||
|
@ -14,18 +15,41 @@ NeoGeoPocketColor::NeoGeoPocketColor(vector<uint8_t>& 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ struct NeoGeoPocket {
|
|||
NeoGeoPocket(vector<uint8_t>& data, string location);
|
||||
explicit operator bool() const;
|
||||
auto manifest() const -> string;
|
||||
auto title() const -> string;
|
||||
|
||||
private:
|
||||
vector<uint8_t>& data;
|
||||
|
@ -14,18 +15,41 @@ NeoGeoPocket::NeoGeoPocket(vector<uint8_t>& 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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; }
|
||||
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
#include <nall/interpolation.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/iterator.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/locale.hpp>
|
||||
#include <nall/location.hpp>
|
||||
#include <nall/map.hpp>
|
||||
|
|
|
@ -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!() 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<uint Precision = 64> 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<typename T> inline auto orElse(const T& value) { return Integer<>{data ? data : value}; }
|
||||
|
||||
|
|
|
@ -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!() 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
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue