Commit aaf094e7 authored by Tim Allen's avatar Tim Allen

Update to v106r69 release.

byuu says:

The biggest change was improving WonderSwan emulation. With help from
trap15, I tracked down a bug where I was checking the wrong bit for
reverse DMA transfers. Then I also emulated VTOTAL to support variable
refresh rate. Then I improved HyperVoice emulation which should be
unsigned samples in three of four modes. That got Fire Lancer running
great. I also rewrote the disassembler. The old one disassembled many
instructions completely wrong, and deviated too much from any known x86
syntax. I also emulated some of the quirks of the V30 (two-byte POP into
registers fails, SALC is just XLAT mirrored, etc) which probably don't
matter unless someone tries to run code to verify it's a NEC CPU and not
an Intel CPU, but hey, why not?

I also put more work into the MSX skeleton, but it's still just a
skeleton with no real emulation yet.
parent 3159285e
Pipeline #41826932 failed with stage
in 6 minutes and 31 seconds
......@@ -22,13 +22,15 @@ using namespace nall;
#include <libco/libco.h>
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
#include <emulator/audio/audio.hpp>
#include <emulator/video/video.hpp>
#include <emulator/resource/resource.hpp>
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.68";
static const string Version = "106.69";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
......
#pragma once
namespace Emulator {
template<typename type>
struct WritableMemory {
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, type fill = ~0ull) -> void {
delete[] self.data;
self.size = size;
self.mask = bit::round(size) - 1;
self.data = new uint8[self.mask];
memory::fill<type>(self.data, size, fill);
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() -> type* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) -> type& {
return self.data[address & self.mask];
}
inline auto read(uint address) -> type {
return self.data[address & self.mask];
}
inline auto write(uint address, type data) -> void {
self.data[address & self.mask] = data;
}
struct {
type* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}
#pragma once
namespace Emulator::Memory {
inline auto mirror(uint address, uint size) -> uint {
if(size == 0) return 0;
uint base = 0;
uint mask = 1 << 31;
while(address >= size) {
while(!(address & mask)) mask >>= 1;
address -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + address;
}
inline auto reduce(uint address, uint mask) -> uint {
while(mask) {
uint bits = (mask & -mask) - 1;
address = address >> 1 & ~bits | address & bits;
mask = (mask & mask - 1) >> 1;
}
return address;
}
}
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Readable {
~Readable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](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 {}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Writable {
~Writable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(vfs::shared::file fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(vfs::shared::file fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() -> T* { return self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
inline auto operator[](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; }
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}
......@@ -51,21 +51,17 @@ auto Cartridge::load() -> bool {
information.title = document["game/label"].text();
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
rom.size = memory.size;
rom.mask = bit::round(rom.size) - 1;
rom.data = new uint8[rom.mask + 1];
rom.allocate(memory.size);
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Required)) {
fp->read(rom.data, rom.size);
}
rom.load(fp);
} else return false;
}
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
ram.size = memory.size;
ram.mask = bit::round(ram.size) - 1;
ram.data = new uint8[ram.mask + 1];
ram.allocate(memory.size);
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Read)) {
fp->read(ram.data, ram.size);
ram.load(fp);
}
}
}
......@@ -79,17 +75,15 @@ auto Cartridge::save() -> void {
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
if(memory.nonVolatile) {
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
fp->write(ram.data, ram.size);
ram.save(fp);
}
}
}
}
auto Cartridge::unload() -> void {
delete[] rom.data;
delete[] ram.data;
rom = {};
ram = {};
rom.reset();
ram.reset();
}
auto Cartridge::power() -> void {
......@@ -99,29 +93,4 @@ auto Cartridge::power() -> void {
mapper.romPage2 = 2;
}
auto Cartridge::Memory::mirror(uint addr, uint size) -> uint {
uint base = 0;
uint mask = 1 << 21;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + addr;
}
auto Cartridge::Memory::read(uint addr) -> uint8 {
if(!size) return 0x00;
return this->data[mirror(addr, size)];
}
auto Cartridge::Memory::write(uint addr, uint8 data) -> void {
if(!size) return;
this->data[mirror(addr, size)] = data;
}
}
......@@ -27,18 +27,8 @@ struct Cartridge {
string title;
} information;
struct Memory {
uint8* data = nullptr;
uint size = 0;
uint mask = 0;
static auto mirror(uint addr, uint size) -> uint;
auto read(uint addr) -> uint8;
auto write(uint addr, uint8 data) -> void;
};
Memory rom;
Memory ram;
Emulator::Memory::Readable<uint8> rom;
Emulator::Memory::Writable<uint8> ram;
struct Mapper {
//$fffc
......
......@@ -14,7 +14,7 @@ auto Cartridge::read(uint16 addr) -> maybe<uint8> {
}
case 2: {
if(mapper.ramEnablePage2) {
if(ram && mapper.ramEnablePage2) {
return ram.read(mapper.ramPage2 << 14 | addr);
}
......@@ -22,7 +22,7 @@ auto Cartridge::read(uint16 addr) -> maybe<uint8> {
}
case 3: {
if(mapper.ramEnablePage3) {
if(ram && mapper.ramEnablePage3) {
return ram.read(addr);
}
......@@ -69,7 +69,7 @@ auto Cartridge::write(uint16 addr, uint8 data) -> bool {
}
case 2: {
if(mapper.ramEnablePage2) {
if(ram && mapper.ramEnablePage2) {
ram.write(mapper.ramPage2 << 14 | addr, data);
return true;
}
......@@ -78,7 +78,7 @@ auto Cartridge::write(uint16 addr, uint8 data) -> bool {
}
case 3: {
if(mapper.ramEnablePage3) {
if(ram && mapper.ramEnablePage3) {
ram.write(addr, data);
return true;
}
......
auto Cartridge::serialize(serializer& s) -> void {
if(ram.size) s.array(ram.data, ram.size);
if(ram) s.array(ram.data(), ram.size());
}
......@@ -39,8 +39,8 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
vector<Thread*> peripherals;
private:
Emulator::WritableMemory<uint8> ram;
Emulator::WritableMemory<uint8> expansion;
Emulator::Memory::Writable<uint8> ram;
Emulator::Memory::Writable<uint8> expansion;
struct State {
bool nmiLine = 0;
......
......@@ -6,7 +6,6 @@
#include <emulator/emulator.hpp>
#include <emulator/thread.hpp>
#include <emulator/scheduler.hpp>
#include <emulator/memory.hpp>
#include <emulator/cheat.hpp>
#include <processor/z80/z80.hpp>
......
......@@ -7,6 +7,7 @@ struct System {
auto region() const -> Region { return information.region; }
auto colorburst() const -> double { return information.colorburst; }
//system.cpp
auto run() -> void;
auto runToSave() -> void;
......
processors += z80
objects += msx-interface msx-system
objects += msx-interface msx-system msx-cartridge
objects += msx-cpu msx-vdp msx-psg
obj/msx-interface.o: msx/interface/interface.cpp
obj/msx-system.o: msx/system/system.cpp
obj/msx-cartridge.o: msx/cartridge/cartridge.cpp
obj/msx-cpu.o: msx/cpu/cpu.cpp
obj/msx-vdp.o: msx/vdp/vdp.cpp
obj/msx-psg.o: msx/psg/psg.cpp
#include <msx/msx.hpp>
namespace MSX {
Cartridge cartridge;
Cartridge expansion;
#include "serialization.cpp"
auto Cartridge::load() -> bool {
information = {};
if(Model::MSX()) {
if(auto loaded = platform->load(ID::MSX, "MSX", "msx", {"NTSC", "PAL"})) {
information.pathID = loaded.pathID;
information.region = loaded.option;
} else return false;
}
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();
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
rom.allocate(memory["size"].natural());
if(auto fp = platform->open(pathID(), "program.rom", File::Read, File::Required)) {
rom.load(fp);
} else return false;
}
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
ram.allocate(memory["size"].natural());
if(auto fp = platform->open(pathID(), "save.ram", File::Read)) {
ram.load(fp);
}
}
return true;
}
auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
if(auto fp = platform->open(pathID(), "save.ram", File::Write)) {
ram.save(fp);
}
}
}
auto Cartridge::unload() -> void {
rom.reset();
ram.reset();
}
auto Cartridge::power() -> void {
}
}
struct Cartridge {
auto pathID() const -> uint { return information.pathID; }
auto region() const -> string { return information.region; }
auto hash() const -> string { return information.sha256; }
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;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
struct Information {
uint pathID = 0;
string region;
string sha256;
string manifest;
string title;
} information;
Emulator::Memory::Readable<uint8> rom;
Emulator::Memory::Writable<uint8> ram;
};
extern Cartridge cartridge;
extern Cartridge expansion;
auto Cartridge::serialize(serializer& s) -> void {
if(ram) s.array(ram.data(), ram.size());
}
#include <msx/msx.hpp>
namespace MSX {
CPU cpu;
#include "memory.cpp"
#include "serialization.cpp"
auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {
instruction();
}
auto CPU::step(uint clocks) -> void {
Thread::step(clocks);
}
auto CPU::synchronizing() const -> bool {
return scheduler.synchronizing();
}
auto CPU::power() -> void {
}
}
struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
//cpu.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;
auto write(uint16 address, uint8 data) -> void override;
auto in(uint8 address) -> uint8 override;
auto out(uint8 address, uint8 data) -> void override;
//serialization.cpp
auto serialize(serializer&) -> void;
};
extern CPU cpu;
auto CPU::read(uint16 address) -> uint8 {
return 0xff;
}
auto CPU::write(uint16 address, uint8 data) -> void {
}
auto CPU::in(uint8 address) -> uint8 {
return 0xff;
}