Update to v106r70 release.

byuu says:

Changelog:

  - Interface::displays() -> vector<Display> → Interface::display() -> Display
  - <Platform::videoRefresh(display>, ...) → <Platform::videoFrame>(...)
  - <Platform::audioSample>(...) → <Platform::audioFrame>(...)
  - higan, icarus: use AboutDialog class instead of ad-hoc
    implementations
      - about dialog is now modal, but now has a clickable website URL
  - icarus: reverted if constexpr for now
  - MSX: implemented basic CPU, VDP support

I took out the multiple displays support thing because it was never
really implemented fully (Emulator::Video and the GUIs both ignored it)
or used anyway. If it ends up necessary in the future, I'll worry about
it then.

There's enough MSX emulation now to run Mr. Do! without sound or input.
I'm shipping higan with C-BIOS 0.29a, although it likely won't be good
enough in the future (eg it can't do BASIC, floppy disk, or cassette
loading.) I have keyboard and (not working) AY-3-8910 support in a
different branch, so that won't take too long to implement. Main problem
is naming all the darned keyboard keys. I think I need to change
settings.bml's input mapping lines so that the key names are values
instead of node names, so that any characters can appear inside of them.

It turns out my MSX set uses .rom for the file extensions ... gods. So,
icarus can't really import them like this. I may have to re-design
icarus' importer to stop caring about the file extension and instead ask
you what kind of games you are importing. There's no way icarus can
heuristically guess what systems the images belong to, because many
systems don't have any standardized magic bytes.

I'm struggling with where to put SG-1000, SC-3000, ColecoVision, Coleco
Adam stuff. I think they need to be split to two separate higan
subfolders (sg and cv, most likely ...) The MS/GG share a very
customized and extended VDP that the other systems don't have. The Sega
and Coleco older hardware share the same TMS9918 as the MSX, yet have
very different memory maps and peripherals that I don't want to mix
together. Especially if we start getting into the computer-variants
more.
This commit is contained in:
Tim Allen 2019-01-03 21:05:20 +11:00
parent cac3858f65
commit 79be6f2355
58 changed files with 522 additions and 187 deletions

View File

@ -64,7 +64,7 @@ auto Audio::process() -> void {
if(balance > 0.0) samples[0] *= 1.0 - balance;
}
platform->audioSample(samples, channels);
platform->audioFrame(samples, channels);
}
}

View File

@ -30,7 +30,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.69";
static const string Version = "106.70";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -54,7 +54,7 @@ struct Interface {
//information
virtual auto information() -> Information { return {}; }
virtual auto displays() -> vector<Display> { return {}; }
virtual auto display() -> Display { return {}; }
virtual auto color(uint32 color) -> uint64 { return 0; }
//game interface

View File

@ -16,8 +16,8 @@ struct Platform {
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
virtual auto videoRefresh(uint display, const uint32* data, uint pitch, uint width, uint height) -> void {}
virtual auto audioSample(const double* samples, uint channels) -> void {}
virtual auto videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void {}
virtual auto audioFrame(const double* samples, uint channels) -> void {}
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }

View File

@ -28,7 +28,7 @@ auto Video::setPalette() -> void {
if(!interface) return;
delete palette;
colors = interface->displays()[0].colors;
colors = interface->display().colors;
palette = new uint32[colors];
for(auto index : range(colors)) {
uint64 color = interface->color(index);
@ -185,7 +185,7 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
}
}
platform->videoRefresh(0, output, width * sizeof(uint32), width, height);
platform->videoFrame(output, width * sizeof(uint32), width, height);
}
}

View File

@ -13,7 +13,7 @@ auto Interface::information() -> Information {
return information;
}
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 9;
@ -23,7 +23,7 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 240;
display.aspectCorrection = 8.0 / 7.0;
display.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
return {display};
return display;
}
auto Interface::color(uint32 n) -> uint64 {

View File

@ -23,7 +23,7 @@ struct ID {
struct Interface : Emulator::Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -7,7 +7,7 @@ Settings settings;
#include "game-boy.cpp"
#include "game-boy-color.cpp"
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::LCD;
display.colors = Model::GameBoyColor() ? 1 << 15 : 1 << 2;
@ -17,7 +17,7 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 144;
display.aspectCorrection = 1.0;
display.refreshRate = (4.0 * 1024.0 * 1024.0) / (154.0 * 456.0);
return {display};
return display;
}
auto Interface::loaded() -> bool {

View File

@ -23,7 +23,7 @@ struct ID {
};
struct Interface : Emulator::Interface {
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto loaded() -> bool override;
auto hashes() -> vector<string> override;

View File

@ -12,7 +12,7 @@ auto Interface::information() -> Information {
return information;
}
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::LCD;
display.colors = 1 << 15;
@ -26,7 +26,7 @@ auto Interface::displays() -> vector<Display> {
swap(display.width, display.height);
swap(display.internalWidth, display.internalHeight);
}
return {display};
return display;
}
auto Interface::color(uint32 color) -> uint64 {

View File

@ -20,7 +20,7 @@ struct ID {
struct Interface : Emulator::Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -13,7 +13,7 @@ auto Interface::information() -> Information {
return information;
}
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 3 * (1 << 9);
@ -23,7 +23,7 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 480;
display.aspectCorrection = 1.0;
display.refreshRate = (system.frequency() / 2.0) / (vdp.frameHeight() * 1710.0);
return {display};
return display;
}
auto Interface::color(uint32 color) -> uint64 {

View File

@ -24,7 +24,7 @@ struct ID {
struct Interface : Emulator::Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -6,7 +6,7 @@ auto ColecoVisionInterface::information() -> Information {
return information;
}
auto ColecoVisionInterface::displays() -> vector<Display> {
auto ColecoVisionInterface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 4;
@ -17,7 +17,7 @@ auto ColecoVisionInterface::displays() -> vector<Display> {
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};
return display;
}
auto ColecoVisionInterface::color(uint32 color) -> uint64 {

View File

@ -6,7 +6,7 @@ auto GameGearInterface::information() -> Information {
return information;
}
auto GameGearInterface::displays() -> vector<Display> {
auto GameGearInterface::display() -> Display {
Display display;
display.type = Display::Type::LCD;
display.colors = 1 << 12;
@ -16,7 +16,7 @@ auto GameGearInterface::displays() -> vector<Display> {
display.internalHeight = 144;
display.aspectCorrection = 1.0;
display.refreshRate = (system.colorburst() * 15.0 / 5.0) / (262.0 * 684.0);
return {display};
return display;
}
auto GameGearInterface::color(uint32 color) -> uint64 {

View File

@ -53,7 +53,7 @@ struct Interface : Emulator::Interface {
struct ColecoVisionInterface : Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;
@ -69,7 +69,7 @@ struct ColecoVisionInterface : Interface {
struct SG1000Interface : Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;
@ -85,7 +85,7 @@ struct SG1000Interface : Interface {
struct SC3000Interface : Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;
@ -101,7 +101,7 @@ struct SC3000Interface : Interface {
struct MasterSystemInterface : Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;
@ -117,7 +117,7 @@ struct MasterSystemInterface : Interface {
struct GameGearInterface : Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;

View File

@ -6,7 +6,7 @@ auto MasterSystemInterface::information() -> Information {
return information;
}
auto MasterSystemInterface::displays() -> vector<Display> {
auto MasterSystemInterface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 6;
@ -17,7 +17,7 @@ auto MasterSystemInterface::displays() -> vector<Display> {
display.aspectCorrection = 8.0 / 7.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};
return display;
}
auto MasterSystemInterface::color(uint32 color) -> uint64 {

View File

@ -6,7 +6,7 @@ auto SC3000Interface::information() -> Information {
return information;
}
auto SC3000Interface::displays() -> vector<Display> {
auto SC3000Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 4;
@ -17,7 +17,7 @@ auto SC3000Interface::displays() -> vector<Display> {
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};
return display;
}
auto SC3000Interface::color(uint32 color) -> uint64 {

View File

@ -6,7 +6,7 @@ auto SG1000Interface::information() -> Information {
return information;
}
auto SG1000Interface::displays() -> vector<Display> {
auto SG1000Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 4;
@ -17,7 +17,7 @@ auto SG1000Interface::displays() -> vector<Display> {
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};
return display;
}
auto SG1000Interface::color(uint32 color) -> uint64 {

View File

@ -57,13 +57,8 @@ auto VDP::Background::graphics2(uint8 hoffset, uint9 voffset) -> void {
patternAddress.bits(3,10) = pattern;
if(voffset >= 64 && voffset <= 127) patternAddress.bit(11) = vdp.io.patternTableAddress.bit(0);
if(voffset >= 128 && voffset <= 191) patternAddress.bit(12) = vdp.io.patternTableAddress.bit(1);
uint14 colorAddress = patternAddress;
patternAddress.bit(13) = vdp.io.patternTableAddress.bit(2);
uint14 colorAddress;
colorAddress.bits(0, 2) = voffset.bits(0,2);
colorAddress.bits(3,10) = pattern;
if(voffset >= 64 && voffset <= 127) colorAddress.bit(11) = vdp.io.patternTableAddress.bit(0);
if(voffset >= 128 && voffset <= 191) colorAddress.bit(12) = vdp.io.patternTableAddress.bit(1);
colorAddress.bit(13) = vdp.io.colorTableAddress.bit(7);
uint8 colorMask = vdp.io.colorTableAddress.bits(0,6) << 1 | 1;

View File

@ -58,4 +58,11 @@ auto Cartridge::unload() -> void {
auto Cartridge::power() -> void {
}
auto Cartridge::read(uint16 address) -> uint8 {
return rom.read(address);
}
auto Cartridge::write(uint16 address, uint8 data) -> void {
}
}

View File

@ -12,6 +12,9 @@ struct Cartridge {
auto power() -> void;
auto read(uint16 address) -> uint8;
auto write(uint16 address, uint8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;

View File

@ -11,11 +11,14 @@ auto CPU::Enter() -> void {
}
auto CPU::main() -> void {
if(io.irqLine) irq(1, 0x0038, 0xff);
instruction();
}
auto CPU::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(vdp);
synchronize(psg);
}
auto CPU::synchronizing() const -> bool {
@ -23,6 +26,19 @@ auto CPU::synchronizing() const -> bool {
}
auto CPU::power() -> void {
Z80::bus = this;
Z80::power();
create(CPU::Enter, system.colorburst());
r.pc = 0x0000; //reset vector address
ram.allocate(0x10000);
io = {};
}
auto CPU::setIRQ(bool line) -> void {
io.irqLine = line;
}
}

View File

@ -6,6 +6,7 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
auto synchronizing() const -> bool override;
auto power() -> void;
auto setIRQ(bool) -> void;
//memory.cpp
auto read(uint16 address) -> uint8 override;
@ -16,6 +17,14 @@ struct CPU : Processor::Z80, Processor::Z80::Bus, Thread {
//serialization.cpp
auto serialize(serializer&) -> void;
private:
Emulator::Memory::Writable<uint8> ram;
struct IO {
uint1 irqLine;
uint2 slot[4];
} io;
};
extern CPU cpu;

View File

@ -1,13 +1,65 @@
auto CPU::read(uint16 address) -> uint8 {
uint2 slot = io.slot[address.bits(14,15)];
if(slot == 0) {
if(!address.bit(15)) return system.bios.read(address);
return 0xff;
}
if(slot == 1) {
return cartridge.read(address);
}
if(slot == 2) {
return ram.read(address);
}
if(slot == 3) {
}
return 0xff;
}
auto CPU::write(uint16 address, uint8 data) -> void {
uint2 slot = io.slot[address.bits(14,15)];
if(slot == 0) {
return;
}
if(slot == 1) {
return cartridge.write(address, data);
}
if(slot == 2) {
return ram.write(address, data);
}
if(slot == 3) {
return;
}
}
auto CPU::in(uint8 address) -> uint8 {
switch(address) {
case 0x98: return vdp.data();
case 0x99: return vdp.status();
case 0xa8: return io.slot[0] << 0
| io.slot[1] << 2
| io.slot[2] << 4
| io.slot[3] << 6;
}
return 0xff;
}
auto CPU::out(uint8 address, uint8 data) -> void {
switch(address) {
case 0x98: return vdp.data(data);
case 0x99: return vdp.control(data);
case 0xa8: io.slot[0] = data.bits(0,1);
io.slot[1] = data.bits(2,3);
io.slot[2] = data.bits(4,5);
io.slot[3] = data.bits(6,7);
break;
}
}

View File

@ -6,11 +6,11 @@ auto Interface::information() -> Information {
Information information;
information.manufacturer = "";
information.name = "MSX";
information.extension = ".msx";
information.extension = "msx";
return information;
}
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 4;
@ -20,40 +20,58 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 192;
display.aspectCorrection = 1.0;
display.refreshRate = 60.0; //todo: PAL
return {display};
return display;
}
auto Interface::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
}
return 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 {};
return {cartridge.title()};
}
auto Interface::load() -> bool {
return false;
return system.load(this, System::Model::MSX);
}
auto Interface::save() -> void {
system.save();
}
auto Interface::unload() -> void {
save();
system.unload();
}
auto Interface::ports() -> vector<Port> { return {
@ -91,17 +109,20 @@ auto Interface::inputs(uint device) -> vector<Input> {
}
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);
}
}

View File

@ -25,7 +25,7 @@ struct ID {
struct Interface : Emulator::Interface {
auto information() -> Information override;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -29,6 +29,13 @@ auto System::load(Emulator::Interface* interface, Model model) -> bool {
auto document = BML::unserialize(information.manifest);
if(auto memory = document["system/memory(type=ROM,content=BIOS)"]) {
bios.allocate(memory["size"].natural());
if(auto fp = platform->open(ID::System, "bios.rom", File::Read, File::Required)) {
bios.load(fp);
} else return false;
} else return false;
if(!cartridge.load()) return false;
if(cartridge.region() == "NTSC") {

View File

@ -24,6 +24,8 @@ struct System {
auto serializeAll(serializer&) -> void;
auto serialize(serializer&) -> void;
Emulator::Memory::Readable<uint8> bios;
private:
Emulator::Interface* interface = nullptr;

View File

@ -0,0 +1,73 @@
auto VDP::background(uint8 hoffset, uint8 voffset) -> void {
if(!io.displayEnable) {
output.color = io.colorBackground;
return;
}
switch(io.videoMode) {
case 0: return graphics1(hoffset, voffset);
//case 1: return text1(hoffset, voffset);
case 2: return graphics2(hoffset, voffset);
case 3: return multicolor(hoffset, voffset);
default: output.color = 8; return; //medium red color to identify unimplemented modes
}
}
auto VDP::text1(uint8 hoffset, uint8 voffset) -> void {
}
auto VDP::graphics1(uint8 hoffset, uint8 voffset) -> void {
uint14 nameAddress;
nameAddress.bits( 0, 4) = hoffset.bits(3,7);
nameAddress.bits( 5, 9) = voffset.bits(3,7);
nameAddress.bits(10,13) = io.nameTableAddress;
uint8 pattern = vram.read(nameAddress);
uint14 patternAddress;
patternAddress.bits( 0, 2) = voffset.bits(0,2);
patternAddress.bits( 3,10) = pattern;
patternAddress.bits(11,13) = io.patternTableAddress;
uint14 colorAddress; //d5 = 0
colorAddress.bits(0, 4) = pattern.bits(3,7);
colorAddress.bits(6,13) = io.colorTableAddress;
uint8 color = vram.read(colorAddress);
uint3 index = hoffset ^ 7;
if(!vram.read(patternAddress).bit(index)) {
output.color = color.bits(0,3);
} else {
output.color = color.bits(4,7);
}
}
auto VDP::graphics2(uint8 hoffset, uint8 voffset) -> void {
uint14 nameAddress;
nameAddress.bits( 0, 4) = hoffset.bits(3,7);
nameAddress.bits( 5, 9) = voffset.bits(3,7);
nameAddress.bits(10,13) = io.nameTableAddress;
uint8 pattern = vram.read(nameAddress);
uint14 patternAddress;
patternAddress.bits(0, 2) = voffset.bits(0,2);
patternAddress.bits(3,10) = pattern;
if(voffset >= 64 && voffset <= 127) patternAddress.bit(11) = io.patternTableAddress.bit(0);
if(voffset >= 128 && voffset <= 191) patternAddress.bit(12) = io.patternTableAddress.bit(1);
uint14 colorAddress = patternAddress;
patternAddress.bit(13) = io.patternTableAddress.bit(2);
colorAddress.bit(13) = io.colorTableAddress.bit(7);
uint8 colorMask = io.colorTableAddress.bits(0,6) << 1 | 1;
uint8 color = vram.read(colorAddress);
uint3 index = hoffset ^ 7;
if(!vram.read(patternAddress).bit(index)) {
output.color = color.bits(0,3);
} else {
output.color = color.bits(4,7);
}
}
auto VDP::multicolor(uint8 hoffset, uint8 voffset) -> void {
}

79
higan/msx/vdp/io.cpp Normal file
View File

@ -0,0 +1,79 @@
auto VDP::status() -> uint8 {
io.controlLatch = 0;
uint8 data = 0x00;
data.bits(0,4) = io.spriteOverflowIndex;
data.bit(5) = io.spriteCollision;
data.bit(6) = io.spriteOverflow;
data.bit(7) = io.irqLine;
io.irqLine = 0;
return data;
}
auto VDP::data() -> uint8 {
io.controlLatch = 0;
uint8 data = io.vramLatch;
uint14 address = io.controlValue.bits(0,13)++;
io.vramLatch = vram.read(address);
return data;
}
auto VDP::data(uint8 data) -> void {
io.controlLatch = 0;
uint14 address = io.controlValue.bits(0,13)++;
vram.write(address, data);
}
auto VDP::control(uint8 data) -> void {
if(io.controlLatch == 0) {
io.controlLatch = 1;
io.controlValue.byte(0) = data;
return;
} else {
io.controlLatch = 0;
io.controlValue.byte(1) = data;
}
if(!io.controlValue.bit(15)) {
if(!io.controlValue.bit(14)) {
uint14 address = io.controlValue.bits(0,13)++;
io.vramLatch = vram.read(address);
}
return;
}
data = io.controlValue.bits(0,7);
switch(io.controlValue.bits(8,10)) {
case 0:
io.externalInput = data.bit(0);
io.videoMode.bit(1) = data.bit(1);
break;
case 1:
io.spriteZoom = data.bit(0);
io.spriteSize = data.bit(1);
io.videoMode.bit(2) = data.bit(3);
io.videoMode.bit(0) = data.bit(4);
io.irqEnable = data.bit(5);
io.displayEnable = data.bit(6);
io.ramMode = data.bit(7);
break;
case 2:
io.nameTableAddress = data.bits(0,3);
break;
case 3:
io.colorTableAddress = data.bits(0,7);
break;
case 4:
io.patternTableAddress = data.bits(0,2);
break;
case 5:
io.spriteAttributeTableAddress = data.bits(0,6);
break;
case 6:
io.spritePatternTableAddress = data.bits(0,2);
break;
case 7:
io.colorBackground = data.bits(0,3);
io.colorForeground = data.bits(4,7);
break;
}
}

58
higan/msx/vdp/sprites.cpp Normal file
View File

@ -0,0 +1,58 @@
auto VDP::sprite(uint8 voffset) -> void {
uint valid = 0;
uint limit = io.spriteSize ? 15 : 7;
for(uint index : range(4)) sprites[index].y = 0xd0;
uint14 attributeAddress;
attributeAddress.bits(7,13) = io.spriteAttributeTableAddress;
for(uint index : range(32)) {
uint8 y = vram.read(attributeAddress++);
if(y == 0xd0) break;
uint8 x = vram.read(attributeAddress++);
uint8 pattern = vram.read(attributeAddress++);
uint8 extra = vram.read(attributeAddress++);
if(extra.bit(7)) x -= 32;
y += 1;
if(voffset < y) continue;
if(voffset > y + limit) continue;
if(limit == 15) pattern.bits(0,1) = 0;
if(valid == 4) {
io.spriteOverflow = true;
io.spriteOverflowIndex = index;
break;
}
sprites[valid++] = {x, y, pattern, extra.bits(0,3)};
}
}
auto VDP::sprite(uint8 hoffset, uint8 voffset) -> void {
uint4 color;
uint limit = io.spriteSize ? 15 : 7;
for(uint n : range(4)) {
auto& o = sprites[n];
if(o.y == 0xd0) continue;
if(hoffset < o.x) continue;
if(hoffset > o.x + limit) continue;
uint x = hoffset - o.x;
uint y = voffset - o.y;
uint14 address;
address.bits( 0,10) = (o.pattern << 3) + (x >> 3 << 4) + (y & limit);
address.bits(11,13) = io.spritePatternTableAddress;
uint3 index = x ^ 7;
if(vram.read(address).bit(index)) {
if(color) { io.spriteCollision = true; break; }
color = o.color;
}
}
if(color) output.color = color;
}

View File

@ -1,10 +1,13 @@
#include <msx/msx.hpp>
//228 clocks/scanline
//456 clocks/scanline
namespace MSX {
VDP vdp;
#include "io.cpp"
#include "background.cpp"
#include "sprites.cpp"
#include "serialization.cpp"
auto VDP::Enter() -> void {
@ -12,11 +15,30 @@ auto VDP::Enter() -> void {
}
auto VDP::main() -> void {
step(1);
if(io.vcounter < 192) {
uint8 y = io.vcounter;
sprite(y);
auto line = buffer + y * 256;
for(uint8 x : range(256)) {
background(x, y);
sprite(x, y);
line[x] = output.color;
step(1);
}
step(200);
} else {
step(456);
}
io.vcounter++;
if(io.vcounter == 262) io.vcounter = 0;
if(io.vcounter == 0) io.irqLine = 0;
if(io.vcounter == 192) io.irqLine = 1, scheduler.exit(Scheduler::Event::Frame);
}
auto VDP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto VDP::refresh() -> void {
@ -24,7 +46,10 @@ auto VDP::refresh() -> void {
}
auto VDP::power() -> void {
create(VDP::Enter, system.colorburst());
create(VDP::Enter, system.colorburst() * 2);
vram.allocate(0x4000);
io = {};
}
}

View File

@ -9,11 +9,70 @@ struct VDP : Thread {
auto power() -> void;
//io.cpp
auto status() -> uint8;
auto data() -> uint8;
auto data(uint8) -> void;
auto control(uint8) -> void;
//background.cpp
auto background(uint8 hoffset, uint8 voffset) -> void;
auto text1(uint8 hoffset, uint8 voffset) -> void;
auto graphics1(uint8 hoffset, uint8 voffset) -> void;
auto graphics2(uint8 hoffset, uint8 voffset) -> void;
auto multicolor(uint8 hoffset, uint8 voffset) -> void;
//sprites.cpp
auto sprite(uint8 voffset) -> void;
auto sprite(uint8 hoffset, uint8 voffset) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
uint32 buffer[256 * 192];
Emulator::Memory::Writable<uint8> vram;
struct IO {
uint vcounter = 0;
uint hcounter = 0;
uint1 controlLatch;
uint16 controlValue;
uint8 vramLatch;
uint5 spriteOverflowIndex;
uint1 spriteCollision;
uint1 spriteOverflow;
uint1 irqLine;
uint1 externalInput;
uint3 videoMode;
uint1 spriteZoom;
uint1 spriteSize;
uint1 irqEnable;
uint1 displayEnable;
uint1 ramMode = 1; //0 = 4KB; 1 = 16KB
uint4 nameTableAddress;
uint8 colorTableAddress;
uint3 patternTableAddress;
uint7 spriteAttributeTableAddress;
uint3 spritePatternTableAddress;
uint4 colorBackground;
uint4 colorForeground;
} io;
struct Sprite {
uint8 x;
uint8 y;
uint8 pattern;
uint4 color;
} sprites[4];
struct Output {
uint4 color;
} output;
};
extern VDP vdp;

View File

@ -6,7 +6,7 @@ namespace NeoGeoPocket {
#include "neo-geo-pocket-color.cpp"
//todo: add correct values
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::LCD;
display.colors = 1;
@ -16,7 +16,7 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 240;
display.aspectCorrection = 1.0;
display.refreshRate = 60.0;
return {display};
return display;
}
auto Interface::color(uint32 color) -> uint64 {

View File

@ -19,7 +19,7 @@ struct ID {
};
struct Interface : Emulator::Interface {
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -7,7 +7,7 @@ Settings settings;
#include "pc-engine.cpp"
#include "supergrafx.cpp"
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 9;
@ -17,7 +17,7 @@ auto Interface::displays() -> vector<Display> {
display.internalHeight = 240;
display.aspectCorrection = 8.0 / 7.0;
display.refreshRate = (system.colorburst() * 6.0) / (262.0 * 1365.0);
return {display};
return display;
}
auto Interface::color(uint32 color) -> uint64 {

View File

@ -20,7 +20,7 @@ struct ID {
};
struct Interface : Emulator::Interface {
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

View File

@ -14,7 +14,7 @@ auto Interface::information() -> Information {
return information;
}
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::CRT;
display.colors = 1 << 19;
@ -25,7 +25,7 @@ auto Interface::displays() -> vector<Display> {
display.aspectCorrection = 8.0 / 7.0;
if(Region::NTSC()) display.refreshRate = system.cpuFrequency() / (262.0 * 1364.0);
if(Region::PAL()) display.refreshRate = system.cpuFrequency() / (312.0 * 1364.0);
return {display};
return display;
}
auto Interface::color(uint32 color) -> uint64 {

View File

@ -35,7 +35,7 @@ struct ID {
struct Interface : Emulator::Interface {
auto information() -> Information;
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto loaded() -> bool override;

Binary file not shown.

View File

@ -1 +1,5 @@
system name:MSX
memory
type: ROM
size: 0x8000
content: BIOS

View File

@ -1,26 +0,0 @@
auto AboutWindow::create() -> void {
setTitle({tr("About {0}", "bsnes"), " ..."});
setBackgroundColor({255, 255, 240});
layout.setPadding(10);
auto logo = image{Resource::Logo};
logo.alphaBlend(0xfffff0);
canvas.setIcon(logo);
tableLayout.setFont(Font().setBold());
tableLayout.setSize({2, 4});
tableLayout.column(0).setSpacing(4);
tableLayout.row(0).setSpacing(2);
tableLayout.row(1).setSpacing(2);
tableLayout.row(2).setSpacing(2);
versionLabel.setText({tr("Version"), ":"}).setAlignment(1.0);
versionValue.setText(Emulator::Version);
authorLabel.setText({tr("Author"), ":"}).setAlignment(1.0);
authorValue.setText(Emulator::Author).setToolTip("ビュウ");
licenseLabel.setText({tr("License"), ":"}).setAlignment(1.0);
licenseValue.setText(Emulator::License);
websiteLabel.setText({tr("Website"), ":"}).setAlignment(1.0);
websiteValue.setText(Emulator::Website);
setResizable(false);
setSize(layout.minimumSize());
setDismissable();
}

View File

@ -1,6 +1,4 @@
#include "../bsnes.hpp"
#include "about.cpp"
AboutWindow aboutWindow;
Presentation presentation;
auto Presentation::create() -> void {
@ -137,7 +135,14 @@ auto Presentation::create() -> void {
invoke("https://doc.byuu.org/bsnes/");
});
about.setIcon(Icon::Prompt::Question).setText({tr("About"), " ..."}).onActivate([&] {
aboutWindow.setCentered(*this).setVisible().setFocused();
AboutDialog()
.setLogo(Resource::Logo)
.setVersion(Emulator::Version)
.setAuthor("byuu")
.setLicense("GPLv3")
.setWebsite("https://byuu.org/")
.setParent(*this)
.show();
});
viewport.setDroppable().onDrop([&](vector<string> locations) {

View File

@ -1,23 +1,3 @@
struct AboutWindow : Window {
Application::Namespace tr{"AboutWindow"};
auto create() -> void;
VerticalLayout layout{this};
Canvas canvas{&layout, Size{400, 85}, 0};
TableLayout tableLayout{&layout, Size{~0, 0}};
Label versionLabel{&tableLayout, Size{~0, 0}};
Label versionValue{&tableLayout, Size{~0, 0}};
//
Label authorLabel{&tableLayout, Size{~0, 0}};
Label authorValue{&tableLayout, Size{~0, 0}};
//
Label licenseLabel{&tableLayout, Size{~0, 0}};
Label licenseValue{&tableLayout, Size{~0, 0}};
//
Label websiteLabel{&tableLayout, Size{~0, 0}};
Label websiteValue{&tableLayout, Size{~0, 0}};
};
struct Presentation : Window {
Application::Namespace tr{"Presentation"};
auto create() -> void;
@ -117,5 +97,4 @@ struct Presentation : Window {
Label spacerRight{&statusLayout, Size{8, ~0}, 0};
};
extern AboutWindow aboutWindow;
extern Presentation presentation;

View File

@ -207,7 +207,7 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
return {};
}
auto Program::videoRefresh(uint display, const uint32* data, uint pitch, uint width, uint height) -> void {
auto Program::videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void {
uint32_t* output;
uint length;
@ -255,7 +255,7 @@ auto Program::videoRefresh(uint display, const uint32* data, uint pitch, uint wi
}
}
auto Program::audioSample(const double* samples, uint channels) -> void {
auto Program::audioFrame(const double* samples, uint channels) -> void {
audio.output(samples);
}

View File

@ -18,7 +18,6 @@ auto Program::create() -> void {
presentation.create();
presentation.setVisible();
aboutWindow.create();
settingsWindow.create();
videoSettings.create();

View File

@ -9,8 +9,8 @@ struct Program : Lock, Emulator::Platform {
//platform.cpp
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
auto load(uint id, string name, string type, vector<string> options = {}) -> Emulator::Platform::Load override;
auto videoRefresh(uint display, const uint32* data, uint pitch, uint width, uint height) -> void override;
auto audioSample(const double* samples, uint channels) -> void override;
auto videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void override;
auto audioFrame(const double* samples, uint channels) -> void override;
auto inputPoll(uint port, uint device, uint input) -> int16 override;
auto inputRumble(uint port, uint device, uint input, bool enable) -> void override;

View File

@ -1,27 +0,0 @@
AboutWindow::AboutWindow() {
aboutWindow = this;
setTitle("About higan ...");
setBackgroundColor({255, 255, 240});
layout.setPadding(10);
auto logo = image{Resource::Logo};
logo.alphaBlend(0xfffff0);
canvas.setIcon(logo);
informationLeft.setFont(Font().setBold()).setAlignment(1.0).setText({
"Version:\n",
"Author:\n",
"License:\n",
"Website:"
});
informationRight.setFont(Font().setBold()).setAlignment(0.0).setText({
Emulator::Version, "\n",
Emulator::Author, "\n",
Emulator::License, "\n",
Emulator::Website
});
setResizable(false);
setSize(layout.minimumSize());
setCentered();
setDismissable();
}

View File

@ -1,6 +1,4 @@
#include "../higan.hpp"
#include "about.cpp"
unique_pointer<AboutWindow> aboutWindow;
unique_pointer<Presentation> presentation;
Presentation::Presentation() {
@ -131,7 +129,14 @@ Presentation::Presentation() {
invoke("https://doc.byuu.org/higan/credits/");
});
about.setIcon(Icon::Prompt::Question).setText("About ...").onActivate([&] {
aboutWindow->setCentered(*this).setVisible().setFocused();
AboutDialog()
.setLogo(Resource::Logo)
.setVersion(Emulator::Version)
.setAuthor("byuu")
.setLicense("GPLv3")
.setWebsite("https://byuu.org/")
.setParent(*this)
.show();
});
viewport.setDroppable().onDrop([&](vector<string> locations) {
@ -345,7 +350,7 @@ auto Presentation::resizeViewport() -> void {
uint height = 240;
if(emulator) {
auto display = emulator->displays().first();
auto display = emulator->display();
width = display.width;
height = display.height;
if(settings["View/AspectCorrection"].boolean()) width *= display.aspectCorrection;
@ -412,7 +417,7 @@ auto Presentation::resizeWindow() -> void {
uint statusHeight = settings["View/StatusBar"].boolean() ? StatusHeight : 0;
if(emulator) {
auto display = emulator->displays().first();
auto display = emulator->display();
width = display.width;
height = display.height;
if(settings["View/AspectCorrection"].boolean()) width *= display.aspectCorrection;

View File

@ -1,13 +1,3 @@
struct AboutWindow : Window {
AboutWindow();
VerticalLayout layout{this};
Canvas canvas{&layout, Size{399, 95}, 15};
HorizontalLayout informationLayout{&layout, Size{~0, 0}};
Label informationLeft{&informationLayout, Size{~0, 0}, 3};
Label informationRight{&informationLayout, Size{~0, 0}};
};
struct Presentation : Window {
enum : uint { StatusHeight = 24 };
@ -104,5 +94,4 @@ struct Presentation : Window {
Label spacerRight{&statusLayout, Size{8, ~0}, 0};
};
extern unique_pointer<AboutWindow> aboutWindow;
extern unique_pointer<Presentation> presentation;

View File

@ -50,14 +50,14 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
return {pathID, option};
}
auto Program::videoRefresh(uint displayID, const uint32* data, uint pitch, uint width, uint height) -> void {
auto Program::videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void {
uint32_t* output;
uint length;
pitch >>= 2;
if(!settings["View/Overscan"].boolean()) {
auto display = emulator->displays()[displayID];
auto display = emulator->display();
if(display.type == Emulator::Interface::Display::Type::CRT) {
uint overscanHorizontal = settings["View/Overscan/Horizontal"].natural();
uint overscanVertical = settings["View/Overscan/Vertical"].natural();
@ -92,7 +92,7 @@ auto Program::videoRefresh(uint displayID, const uint32* data, uint pitch, uint
}
}
auto Program::audioSample(const double* samples, uint channels) -> void {
auto Program::audioFrame(const double* samples, uint channels) -> void {
if(channels == 1) {
double stereo[] = {samples[0], samples[0]};
audio->output(stereo);

View File

@ -103,7 +103,6 @@ Program::Program(Arguments arguments) {
new SettingsManager;
new CheatDatabase;
new ToolsManager;
new AboutWindow;
updateVideoShader();
updateAudioDriver();

View File

@ -8,8 +8,8 @@ struct Program : Emulator::Platform {
auto path(uint id) -> string override;
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
auto load(uint id, string name, string type, vector<string> options = {}) -> Emulator::Platform::Load override;
auto videoRefresh(uint display, const uint32* data, uint pitch, uint width, uint height) -> void override;
auto audioSample(const double* samples, uint channels) -> void override;
auto videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void override;
auto audioFrame(const double* samples, uint channels) -> void override;
auto inputPoll(uint port, uint device, uint input) -> int16 override;
auto inputRumble(uint port, uint device, uint input, bool enable) -> void override;
auto dipSettings(Markup::Node node) -> uint override;

View File

@ -7,7 +7,7 @@ Settings settings;
#include "wonderswan-color.cpp"
#include "pocket-challenge-v2.cpp"
auto Interface::displays() -> vector<Display> {
auto Interface::display() -> Display {
Display display;
display.type = Display::Type::LCD;
display.colors = 1 << 12;
@ -21,7 +21,7 @@ auto Interface::displays() -> vector<Display> {
swap(display.width, display.height);
swap(display.internalWidth, display.internalHeight);
}
return {display};
return display;
}
//todo: the WonderSwan and PocketChallengeV2 interfaces should be generating grayscale shades

View File

@ -20,7 +20,7 @@ struct ID {
};
struct Interface : Emulator::Interface {
auto displays() -> vector<Display> override;
auto display() -> Display override;
auto color(uint32 color) -> uint64 override;
auto ports() -> vector<Port> override;

View File

@ -49,7 +49,9 @@ auto MessageDialog::_run() -> string {
Window window;
VerticalLayout layout{&window};
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 5};
Canvas messageIcon{&messageLayout, Size{16, 16}, 5};
VerticalLayout messageIconLayout{&messageLayout, Size{16, ~0}, 5};
Canvas messageIcon{&messageIconLayout, Size{16, 16}, 0};
Widget messageIconSpacer{&messageIconLayout, Size{16, ~0}};
Label messageText{&messageLayout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};

View File

@ -131,22 +131,22 @@ auto nall::main(Arguments arguments) -> void {
new ImportDialog;
new ErrorDialog;
if constexpr(platform() == Platform::MacOS) {
Application::Cocoa::onAbout([&] {
MessageDialog().setTitle("About icarus").setText({
"icarus\n\n"
"Author: byuu\n"
"License: GPLv3\n"
"Website: https://byuu.org/\n"
}).information();
});
Application::Cocoa::onPreferences([&] {
scanDialog->settingsButton.doActivate();
});
Application::Cocoa::onQuit([&] {
Application::quit();
});
}
#if defined(PLATFORM_MACOS)
Application::Cocoa::onAbout([&] {
MessageDialog().setTitle("About icarus").setText({
"icarus\n\n"
"Author: byuu\n"
"License: GPLv3\n"
"Website: https://byuu.org/\n"
}).information();
});
Application::Cocoa::onPreferences([&] {
scanDialog->settingsButton.doActivate();
});
Application::Cocoa::onQuit([&] {
Application::quit();
});
#endif
scanDialog->show();
Application::run();