Commit 53843934 authored by Tim Allen's avatar Tim Allen

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]
parent 37b610da
Pipeline #44099699 passed with stage
in 14 minutes and 4 seconds
......@@ -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;
......
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
#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);
}
}
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;
auto APU::serialize(serializer& s) -> void {
Z80::serialize(s);
Z80::Bus::serialize(s);
Thread::serialize(s);
}
#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);
}
}
//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;
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);
}
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);
}
#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;
}
}
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;
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);
}
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