Commit 3159285e authored by Tim Allen's avatar Tim Allen

Update to v106r68 release.

byuu says:

Changelog:

  - nall: converted range, iterator, vector to 64-bit
  - added (very poor) ColecoVision emulation (including Coleco Adam
    expansion)
  - added MSX skeleton
  - added Neo Geo Pocket skeleton
  - moved audio,video,resource folders into emulator folder
  - SFC heuristics: BS-X Town cart is "ZBSJ" [hex_usr]

The nall change is for future work on things like BPA: I need to be able
to handle files larger than 4GB. It is extremely possible that there are
still some truncations to 32-bit lurking around, and even more
disastrously, possibly some -1s lurking that won't sign-extend to
`(uint64_t)0-1`. There's a lot more classes left to do: `string`,
`array_view`, `array_span`, etc.
parent 90da6917
Pipeline #41082045 failed with stage
in 6 minutes and 37 seconds
......@@ -34,7 +34,7 @@ endif
$(call delete,obj/*)
$(call delete,out/*)
install:
install: all
ifeq ($(platform),macos)
cp -R out/$(name).app /Applications/$(name).app
else ifneq ($(filter $(platform),linux bsd),)
......
......@@ -34,16 +34,13 @@ else
$(error "unsupported platform")
endif
objects := libco emulator audio video resource
objects := libco emulator
obj/libco.o: ../libco/libco.c
obj/emulator.o: emulator/emulator.cpp
obj/audio.o: audio/audio.cpp
obj/video.o: video/video.cpp
obj/resource.o: resource/resource.cpp
ifeq ($(target),higan)
cores := fc sfc ms md pce gb gba ws
cores := fc sfc ms md pce msx gb gba ws ngp
endif
ifeq ($(target),bsnes)
......@@ -70,6 +67,10 @@ ifneq ($(filter $(cores),pce),)
include pce/GNUmakefile
endif
ifneq ($(filter $(cores),msx),)
include msx/GNUmakefile
endif
ifneq ($(filter $(cores),gb),)
include gb/GNUmakefile
endif
......@@ -82,6 +83,10 @@ ifneq ($(filter $(cores),ws),)
include ws/GNUmakefile
endif
ifneq ($(filter $(cores),ngp),)
include ngp/GNUmakefile
endif
include processor/GNUmakefile
flags += $(foreach c,$(call strupper,$(cores)),-DCORE_$c)
......
#include <emulator/emulator.hpp>
namespace Emulator {
#include "stream.cpp"
......
#include <emulator/emulator.hpp>
#include <emulator/audio/audio.cpp>
#include <emulator/video/video.cpp>
#include <emulator/resource/resource.cpp>
namespace Emulator {
Platform* platform = nullptr;
......
......@@ -20,15 +20,15 @@
#include <nall/hash/sha256.hpp>
using namespace nall;
#include "types.hpp"
#include <libco/libco.h>
#include <audio/audio.hpp>
#include <video/video.hpp>
#include <resource/resource.hpp>
#include <emulator/types.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.67";
static const string Version = "106.68";
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;
};
}
#include <emulator/emulator.hpp>
namespace Emulator {
#include "sprite.cpp"
......
......@@ -41,7 +41,7 @@ auto System::load(Emulator::Interface* interface) -> bool {
if(auto name = document["system/cpu/rom/name"].text()) {
if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) {
fp->read(bios.data, bios.size);
}
} else return false;
}
if(!cartridge.load()) return false;
......
......@@ -9,6 +9,13 @@ Cartridge cartridge;
auto Cartridge::load() -> bool {
information = {};
if(Model::ColecoVision()) {
if(auto loaded = platform->load(ID::ColecoVision, "ColecoVision", "cv", {"NTSC", "PAL"})) {
information.pathID = loaded.pathID;
information.region = loaded.option;
} else return false;
}
if(Model::SG1000()) {
if(auto loaded = platform->load(ID::SG1000, "SG-1000", "sg1000", {"NTSC", "PAL"})) {
information.pathID = loaded.pathID;
......
......@@ -18,7 +18,7 @@ struct Cartridge {
//serialization.cpp
auto serialize(serializer&) -> void;
private:
//private:
struct Information {
uint pathID = 0;
string region;
......
......@@ -4,6 +4,7 @@ namespace MasterSystem {
ControllerPort controllerPort1;
ControllerPort controllerPort2;
#include "numberpad/numberpad.cpp"
#include "gamepad/gamepad.cpp"
Controller::Controller(uint port) : port(port) {
......@@ -36,6 +37,7 @@ auto ControllerPort::connect(uint deviceID) -> void {
switch(deviceID) { default:
case ID::Device::None: device = new Controller(port); break;
case ID::Device::NumberPad: device = new NumberPad(port); break;
case ID::Device::Gamepad: device = new Gamepad(port); break;
}
......
......@@ -5,7 +5,8 @@ struct Controller : Thread {
static auto Enter() -> void;
auto main() -> void;
virtual auto readData() -> uint7 { return 0x7f; }
virtual auto readData() -> uint8 { return 0xff; }
virtual auto writeData(uint8 data) -> void {}
const uint port;
};
......@@ -24,4 +25,5 @@ struct ControllerPort {
extern ControllerPort controllerPort1;
extern ControllerPort controllerPort2;
#include "numberpad/numberpad.hpp"
#include "gamepad/gamepad.hpp"
Gamepad::Gamepad(uint port) : Controller(port) {
}
auto Gamepad::readData() -> uint7 {
uint7 data = 0x7f;
auto Gamepad::readData() -> uint8 {
uint8 data = 0xff;
data.bit(0) = !platform->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = !platform->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = !platform->inputPoll(port, ID::Device::Gamepad, Left);
......
......@@ -5,5 +5,5 @@ struct Gamepad : Controller {
Gamepad(uint port);
auto readData() -> uint7 override;
auto readData() -> uint8 override;
};
NumberPad::NumberPad(uint port) : Controller(port) {
}
auto NumberPad::readData() -> uint8 {
const uint device = ID::Device::NumberPad;
uint8 data = 0xff;
if(select == 0) {
if(platform->inputPoll(port, device, One )) data.bits(0,3) = 0b1101;
else if(platform->inputPoll(port, device, Two )) data.bits(0,3) = 0b0111;
else if(platform->inputPoll(port, device, Three)) data.bits(0,3) = 0b1100;
else if(platform->inputPoll(port, device, Four )) data.bits(0,3) = 0b0010;
else if(platform->inputPoll(port, device, Five )) data.bits(0,3) = 0b0011;
else if(platform->inputPoll(port, device, Six )) data.bits(0,3) = 0b1110;
else if(platform->inputPoll(port, device, Seven)) data.bits(0,3) = 0b0101;
else if(platform->inputPoll(port, device, Eight)) data.bits(0,3) = 0b0001;
else if(platform->inputPoll(port, device, Nine )) data.bits(0,3) = 0b1011;
else if(platform->inputPoll(port, device, Star )) data.bits(0,3) = 0b1001;
else if(platform->inputPoll(port, device, Zero )) data.bits(0,3) = 0b1010;
else if(platform->inputPoll(port, device, Pound)) data.bits(0,3) = 0b0110;
data.bit(6) = !platform->inputPoll(port, device, R);
} else {
data.bit(0) = !platform->inputPoll(port, device, Up);
data.bit(1) = !platform->inputPoll(port, device, Right);
data.bit(2) = !platform->inputPoll(port, device, Down);
data.bit(3) = !platform->inputPoll(port, device, Left);
data.bit(6) = !platform->inputPoll(port, device, L);
}
return data;
}
auto NumberPad::writeData(uint8 data) -> void {
select = data.bit(0);
}
struct NumberPad : Controller {
enum : uint {
Up, Down, Left, Right,
L, R,
One, Two, Three,
Four, Five, Six,
Seven, Eight, Nine,
Star, Zero, Pound,
};
NumberPad(uint port);
auto readData() -> uint8;
auto writeData(uint8 data) -> void;
uint1 select;
};
auto CPU::readColeco(uint16 address) -> uint8 {
uint8 data = 0xff;
if(address >= 0x0000 && address <= 0x1fff && coleco.replaceBIOS) return expansion.read(address);
if(address >= 0x2000 && address <= 0x7fff && coleco.replaceRAM ) return expansion.read(address);
if(address >= 0x0000 && address <= 0x1fff) return system.bios[address & 0x1fff];
if(address >= 0x6000 && address <= 0x7fff) return ram.read(address - 0x6000);
if(address >= 0x8000 && address <= 0xffff) return cartridge.rom.read(address - 0x8000);
return data;
}
auto CPU::writeColeco(uint16 address, uint8 data) -> void {
if(address >= 0x0000 && address <= 0x1fff && coleco.replaceBIOS) return expansion.write(address, data);
if(address >= 0x2000 && address <= 0x7fff && coleco.replaceRAM) return expansion.write(address, data);
if(address >= 0x6000 && address <= 0x7fff) return ram.write(address - 0x6000, data);
if(address >= 0x8000 && address <= 0xffff) return;
}
auto CPU::inColeco(uint8 address) -> uint8 {
uint8 data = 0xff;
if(address >= 0xa0 && address <= 0xbf) return !address.bit(0) ? vdp.data() : vdp.status();
if(address >= 0xe0 && address <= 0xff && address.bit(1) == 0) return controllerPort1.device->readData();
if(address >= 0xe0 && address <= 0xff && address.bit(1) == 1) return controllerPort2.device->readData();
return data;
}
auto CPU::outColeco(uint8 address, uint8 data) -> void {
if(address == 0x53) coleco.replaceRAM = data.bit(0);
if(address == 0x7f) coleco.replaceBIOS = data.bit(1);
if(address >= 0x80 && address <= 0x9f) controllerPort1.device->writeData(0), controllerPort2.device->writeData(0);
if(address >= 0xa0 && address <= 0xbf) return !address.bit(0) ? vdp.data(data) : vdp.control(data);
if(address >= 0xc0 && address <= 0xdf) controllerPort1.device->writeData(1), controllerPort2.device->writeData(1);
if(address >= 0xe0 && address <= 0xff) return psg.write(data);
}
......@@ -3,7 +3,8 @@
namespace MasterSystem {
CPU cpu;
#include "bus.cpp"
#include "coleco.cpp"
#include "sega.cpp"
#include "serialization.cpp"
auto CPU::Enter() -> void {
......@@ -60,14 +61,36 @@ auto CPU::setINT(bool value) -> void {
state.intLine = value;
}
auto CPU::read(uint16 address) -> uint8 {
return Model::ColecoVision() ? readColeco(address) : readSega(address);
}
auto CPU::write(uint16 address, uint8 data) -> void {
return Model::ColecoVision() ? writeColeco(address, data) : writeSega(address, data);
}
auto CPU::in(uint8 address) -> uint8 {
return Model::ColecoVision() ? inColeco(address) : inSega(address);
}
auto CPU::out(uint8 address, uint8 data) -> void {
return Model::ColecoVision() ? outColeco(address, data) : outSega(address, data);
}
auto CPU::power() -> void {
Z80::bus = this;
Z80::power();
create(CPU::Enter, system.colorburst());
r.pc = 0x0000; //reset vector address
if(Model::ColecoVision()) ram.allocate(0x0400), expansion.allocate(0x1000);
if(Model::SG1000()) ram.allocate(0x0400);
if(Model::SC3000()) ram.allocate(0x0800);
if(Model::MasterSystem()) ram.allocate(0x2000);
if(Model::GameGear()) ram.allocate(0x2000);
r.pc = 0x0000; //reset vector address
state = {};
coleco = {};
}
}
......@@ -11,14 +11,27 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
auto setNMI(bool value) -> void;
auto setINT(bool value) -> void;
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;
auto power() -> void;
//bus.cpp
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
//coleco.cpp
auto readColeco(uint16 address) -> uint8;
auto writeColeco(uint16 address, uint8 data) -> void;
auto inColeco(uint8 address) -> uint8;
auto outColeco(uint8 address, uint8 data) -> void;
auto in(uint8 addr) -> uint8 override;
auto out(uint8 addr, uint8 data) -> void override;
//sega.cpp
auto readSega(uint16 address) -> uint8;
auto writeSega(uint16 address, uint8 data) -> void;
auto inSega(uint8 address) -> uint8;
auto outSega(uint8 address, uint8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
......@@ -26,12 +39,18 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
vector<Thread*> peripherals;
private:
uint8 ram[8 * 1024];
Emulator::WritableMemory<uint8> ram;
Emulator::WritableMemory<uint8> expansion;
struct State {
bool nmiLine = 0;
bool intLine = 0;
} state;
struct Coleco {
uint1 replaceBIOS;
uint1 replaceRAM;
} coleco;
};
extern CPU cpu;
auto CPU::read(uint16 addr) -> uint8 {
uint8 data;
if(auto result = cartridge.read(addr)) {
auto CPU::readSega(uint16 address) -> uint8 {
uint8 data = 0xff;
if(auto result = cartridge.read(address)) {
data = result();
} else if(addr >= 0xc000) {
data = ram[addr & 0x1fff];
} else if(address >= 0xc000) {
data = ram.read(address);
}
if(auto result = cheat.find(addr, data)) {
data = result();
}
if(auto result = cheat.find(address, data)) data = result();
return data;
}
auto CPU::write(uint16 addr, uint8 data) -> void {
if(cartridge.write(addr, data)) {
} else if(addr >= 0xc000) {
ram[addr & 0x1fff] = data;
auto CPU::writeSega(uint16 address, uint8 data) -> void {
if(cartridge.write(address, data)) {
} else if(address >= 0xc000) {
ram.write(address, data);
}
}
auto CPU::in(uint8 addr) -> uint8 {
auto CPU::inSega(uint8 addr) -> uint8 {
switch(addr >> 6) {
case 0: {
......@@ -87,7 +82,7 @@ auto CPU::in(uint8 addr) -> uint8 {
return 0xff;
}
auto CPU::out(uint8 addr, uint8 data) -> void {
auto CPU::outSega(uint8 addr, uint8 data) -> void {
if(addr == 0x06) {
if(Model::GameGear()) return psg.balance(data);
}
......
......@@ -3,7 +3,7 @@ auto CPU::serialize(serializer& s) -> void {
Z80::Bus::serialize(s);
Thread::serialize(s);
s.array(ram);
s.array(ram.data(), ram.size());
s.integer(state.nmiLine);
s.integer(state.intLine);
......
auto ColecoVisionInterface::information() -> Information {
Information information;
information.manufacturer = "Coleco Industries";
information.name = "ColecoVision";
information.extension = "cv";
return information;
}
auto ColecoVisionInterface::displays() -> vector<Display> {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 4;
display.width = 256;
display.height = 192;
display.internalWidth = 256;
display.internalHeight = 192;
display.aspectCorrection = 1.0;
if(Region::NTSC()) display.refreshRate = (system.colorburst() * 15.0 / 5.0) / (262.0 * 684.0);
if(Region::PAL()) display.refreshRate = (system.colorburst() * 15.0 / 5.0) / (312.0 * 684.0);
return {display};
}
auto ColecoVisionInterface::color(uint32 color) -> uint64 {
switch(color.bits(0,3)) {
case 0: return 0x0000'0000'0000ull; //transparent
case 1: return 0x0000'0000'0000ull; //black
case 2: return 0x2121'c8c8'4242ull; //medium green
case 3: return 0x5e5e'dcdc'7878ull; //light green
case 4: return 0x5454'5555'ededull; //dark blue
case 5: return 0x7d7d'7676'fcfcull; //light blue
case 6: return 0xd4d4'5252'4d4dull; //dark red
case 7: return 0x4242'ebeb'f5f5ull; //cyan
case 8: return 0xfcfc'5555'5454ull; //medium red
case 9: return 0xffff'7979'7878ull; //light red
case 10: return 0xd4d4'c1c1'5454ull; //dark yellow
case 11: return 0xe6e6'cece'8080ull; //light yellow
case 12: return 0x2121'b0b0'3b3bull; //dark green
case 13: return 0xc9c9'5b5b'babaull; //magenta
case 14: return 0xcccc'cccc'ccccull; //gray
case 15: return 0xffff'ffff'ffffull; //white
}
unreachable;
}
auto ColecoVisionInterface::ports() -> vector<Port> { return {
{ID::Port::Controller1, "Controller Port 1"},
{ID::Port::Controller2, "Controller Port 2"}};
}
auto ColecoVisionInterface::devices(uint port) -> vector<Device> {
if(port == ID::Port::Controller1) return {
{ID::Device::None, "None"},
{ID::Device::NumberPad, "Gamepad"}
};
if(port == ID::Port::Controller2) return {
{ID::Device::None, "None"},
{ID::Device::NumberPad, "Gamepad"}
};
return {};
}
auto ColecoVisionInterface::inputs(uint device) -> vector<Input> {
using Type = Input::Type;
if(device == ID::Device::NumberPad) return {
{Type::Hat, "Up"},
{Type::Hat, "Down"},
{Type::Hat, "Left"},
{Type::Hat, "Right"},
{Type::Button, "L"},
{Type::Button, "R"},
{Type::Button, "1"},
{Type::Button, "2"},