diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 28f520ef..34cf8ee2 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.10"; + static const string Version = "102.11"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index fcf57089..7a1729e0 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -60,19 +60,28 @@ auto Cartridge::save() -> void { auto Cartridge::unload() -> void { delete[] rom.data; delete[] ram.data; - rom = Memory(); - ram = Memory(); + rom = {}; + ram = {}; } auto Cartridge::power() -> void { } auto Cartridge::read(uint24 addr) -> uint16 { - uint16 data = rom.data[addr + 0 & rom.mask] << 8; - return data | rom.data[addr + 1 & rom.mask] << 0; + if(addr.bit(21) && ram.size) { + uint16 data = ram.data[addr + 0 & ram.mask] << 8; + return data | ram.data[addr + 1 & ram.mask] << 0; + } else { + uint16 data = rom.data[addr + 0 & rom.mask] << 8; + return data | rom.data[addr + 1 & rom.mask] << 0; + } } auto Cartridge::write(uint24 addr, uint16 data) -> void { + if(addr.bit(21) && ram.size) { + ram.data[addr + 0 & ram.mask] = data >> 8; + ram.data[addr + 1 & ram.mask] = data >> 0; + } } } diff --git a/higan/md/psg/io.cpp b/higan/md/psg/io.cpp index d2c711a7..4752b908 100644 --- a/higan/md/psg/io.cpp +++ b/higan/md/psg/io.cpp @@ -29,6 +29,7 @@ auto PSG::write(uint8 data) -> void { case 4: { if(l) tone2.pitch.bits(0,3) = data.bits(0,3); else tone2.pitch.bits(4,9) = data.bits(0,5); + noise.pitch = tone2.pitch; break; } diff --git a/higan/md/psg/noise.cpp b/higan/md/psg/noise.cpp index d416e30d..978ecc89 100644 --- a/higan/md/psg/noise.cpp +++ b/higan/md/psg/noise.cpp @@ -1,23 +1,22 @@ auto PSG::Noise::run() -> void { - auto latch = clock; + if(--counter) return; - counter++; - if(rate == 0) output ^= !counter.bits(0,3); - if(rate == 1) output ^= !counter.bits(0,4); - if(rate == 2) output ^= !counter.bits(0,5); - if(rate == 3) output ^= psg.tone2.clock; + if(rate == 0) counter = 0x10; + if(rate == 1) counter = 0x20; + if(rate == 2) counter = 0x40; + if(rate == 3) counter = pitch; //shared with tone2 - if(!latch && clock) { + if(clock ^= 1) { //0->1 transition + output = lfsr.bit(0); auto eor = enable ? ~lfsr >> 3 : 0; lfsr = (lfsr ^ eor) << 15 | lfsr >> 1; } - - output = lfsr.bit(0); } auto PSG::Noise::power() -> void { volume = ~0; counter = 0; + pitch = 0; enable = 0; rate = 0; lfsr = 0x8000; diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index b57331ef..8e5a3da7 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -43,7 +43,7 @@ auto PSG::power() -> void { select = 0; lowpass = 0; for(auto n : range(15)) { - levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5; + levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5; } levels[15] = 0; diff --git a/higan/md/psg/psg.hpp b/higan/md/psg/psg.hpp index c9e802f8..8dcffda9 100644 --- a/higan/md/psg/psg.hpp +++ b/higan/md/psg/psg.hpp @@ -21,7 +21,6 @@ private: uint4 volume; uint10 counter; uint10 pitch; - uint1 clock; uint1 output; } tone0, tone1, tone2; @@ -31,7 +30,8 @@ private: auto power() -> void; uint4 volume; - uint6 counter; + uint10 counter; + uint10 pitch; uint1 enable; uint2 rate; uint16 lfsr; diff --git a/higan/md/psg/tone.cpp b/higan/md/psg/tone.cpp index 052b1831..bae975d4 100644 --- a/higan/md/psg/tone.cpp +++ b/higan/md/psg/tone.cpp @@ -1,8 +1,6 @@ auto PSG::Tone::run() -> void { - clock = 0; if(--counter) return; - clock = 1; counter = pitch; output ^= 1; } @@ -11,6 +9,5 @@ auto PSG::Tone::power() -> void { volume = ~0; counter = 0; pitch = 0; - clock = 0; output = 0; } diff --git a/higan/md/vdp/background.cpp b/higan/md/vdp/background.cpp index 47f12fc7..7d1661d9 100644 --- a/higan/md/vdp/background.cpp +++ b/higan/md/vdp/background.cpp @@ -13,7 +13,7 @@ auto VDP::Background::updateHorizontalScroll(uint y) -> void { address += (y & mask[io.horizontalScrollMode]) << 1; address += id == ID::PlaneB; - state.horizontalScroll = vdp.vram[address].bits(0,9); + state.horizontalScroll = vdp.vram.read(address).bits(0,9); } auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void { @@ -22,7 +22,7 @@ auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void { auto address = (x >> 4 & 0 - io.verticalScrollMode) << 1; address += id == ID::PlaneB; - state.verticalScroll = vdp.vsram[address]; + state.verticalScroll = vdp.vsram.read(address); } auto VDP::Background::nametableAddress() -> uint15 { @@ -62,13 +62,13 @@ auto VDP::Background::run(uint x, uint y) -> void { auto address = nametableAddress(); address += (tileY * width + tileX) & 0x0fff; - uint16 tileAttributes = vdp.vram[address]; + uint16 tileAttributes = vdp.vram.read(address); uint15 tileAddress = tileAttributes.bits(0,10) << 4; uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0); uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0); tileAddress += pixelY << 1 | pixelX >> 2; - uint16 tileData = vdp.vram[tileAddress]; + uint16 tileData = vdp.vram.read(tileAddress); uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); if(color) { output.color = tileAttributes.bits(13,14) << 4 | color; diff --git a/higan/md/vdp/dma.cpp b/higan/md/vdp/dma.cpp index 082ebde1..73341728 100644 --- a/higan/md/vdp/dma.cpp +++ b/higan/md/vdp/dma.cpp @@ -1,9 +1,10 @@ auto VDP::DMA::run() -> void { if(!io.enable || io.wait) return; - if(!vdp.io.command.bit(5)) return; + if(!vdp.io.command.bit(5)) return; if(io.mode <= 1) return load(); if(io.mode == 2) return fill(); + if(!vdp.io.command.bit(4)) return; if(io.mode == 3) return copy(); } @@ -20,21 +21,26 @@ auto VDP::DMA::load() -> void { } } +//todo: supposedly, this can also write to VSRAM and CRAM (undocumented) auto VDP::DMA::fill() -> void { - auto data = io.fill; - vdp.writeDataPort(data << 8 | data << 0); + if(vdp.io.command.bits(0,3) == 1) { + vdp.vram.writeByte(vdp.io.address, io.fill); + } io.source.bits(0,15)++; + vdp.io.address += vdp.io.dataIncrement; if(--io.length == 0) { vdp.io.command.bit(5) = 0; } } +//note: this can only copy to VRAM auto VDP::DMA::copy() -> void { - auto data = vdp.vram[io.source.bits(0,14)]; - vdp.writeDataPort(data); + auto data = vdp.vram.readByte(io.source); + vdp.vram.writeByte(vdp.io.address, data); io.source.bits(0,15)++; + vdp.io.address += vdp.io.dataIncrement; if(--io.length == 0) { vdp.io.command.bit(5) = 0; } diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index 28192d1f..6e5daab7 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -45,7 +45,7 @@ auto VDP::readDataPort() -> uint16 { //VRAM read if(io.command.bits(0,3) == 0) { auto address = io.address.bits(1,15); - auto data = vram[address]; + auto data = vram.read(address); io.address += io.dataIncrement; return data; } @@ -53,8 +53,7 @@ auto VDP::readDataPort() -> uint16 { //VSRAM read if(io.command.bits(0,3) == 4) { auto address = io.address.bits(1,6); - if(address >= 40) return 0x0000; - auto data = vsram[address]; + auto data = vsram.read(address); io.address += io.dataIncrement; return data; } @@ -62,7 +61,7 @@ auto VDP::readDataPort() -> uint16 { //CRAM read if(io.command.bits(0,3) == 8) { auto address = io.address.bits(1,6); - auto data = cram[address]; + auto data = cram.read(address); io.address += io.dataIncrement; return data.bits(0,2) << 1 | data.bits(3,5) << 2 | data.bits(6,8) << 3; } @@ -76,17 +75,15 @@ auto VDP::writeDataPort(uint16 data) -> void { //DMA VRAM fill if(dma.io.wait.lower()) { dma.io.fill = data >> 8; - return; + //falls through to memory write + //causes extra transfer to occur on VRAM fill operations } //VRAM write if(io.command.bits(0,3) == 1) { auto address = io.address.bits(1,15); if(io.address.bit(0)) data = data >> 8 | data << 8; - vram[address] = data; - if(address >= sprite.io.attributeAddress && address < sprite.io.attributeAddress + 320) { - sprite.write(address, data); - } + vram.write(address, data); io.address += io.dataIncrement; return; } @@ -94,9 +91,8 @@ auto VDP::writeDataPort(uint16 data) -> void { //VSRAM write if(io.command.bits(0,3) == 5) { auto address = io.address.bits(1,6); - if(address >= 40) return; //data format: ---- --yy yyyy yyyy - vsram[address] = data.bits(0,9); + vsram.write(address, data.bits(0,9)); io.address += io.dataIncrement; return; } @@ -105,7 +101,7 @@ auto VDP::writeDataPort(uint16 data) -> void { if(io.command.bits(0,3) == 3) { auto address = io.address.bits(1,6); //data format: ---- bbb- ggg- rrr- - cram[address] = data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6; + cram.write(address, data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6); io.address += io.dataIncrement; return; } diff --git a/higan/md/vdp/memory.cpp b/higan/md/vdp/memory.cpp new file mode 100644 index 00000000..aad8c0b2 --- /dev/null +++ b/higan/md/vdp/memory.cpp @@ -0,0 +1,38 @@ +auto VDP::VRAM::read(uint15 address) const -> uint16 { + return memory[address]; +} + +auto VDP::VRAM::write(uint15 address, uint16 data) -> void { + memory[address] = data; + if(address < vdp.sprite.io.attributeAddress) return; + if(address > vdp.sprite.io.attributeAddress + 319) return; + vdp.sprite.write(address, data); +} + +auto VDP::VRAM::readByte(uint16 address) const -> uint8 { + return read(address >> 1).byte(!address.bit(0)); +} + +auto VDP::VRAM::writeByte(uint16 address, uint8 data) -> void { + auto word = read(address >> 1); + word.byte(!address.bit(0)) = data; + write(address >> 1, word); +} + +auto VDP::VSRAM::read(uint6 address) const -> uint10 { + if(address >= 40) return 0x0000; + return memory[address]; +} + +auto VDP::VSRAM::write(uint6 address, uint10 data) -> void { + if(address >= 40) return; + memory[address] = data; +} + +auto VDP::CRAM::read(uint6 address) const -> uint9 { + return memory[address]; +} + +auto VDP::CRAM::write(uint6 address, uint9 data) -> void { + memory[address] = data; +} diff --git a/higan/md/vdp/render.cpp b/higan/md/vdp/render.cpp index ea456bb0..eaecb283 100644 --- a/higan/md/vdp/render.cpp +++ b/higan/md/vdp/render.cpp @@ -31,7 +31,7 @@ auto VDP::run() -> void { if(planeA.output.priority) if(auto color = planeA.output.color) output = color; if(sprite.output.priority) if(auto color = sprite.output.color) output = color; - outputPixel(cram[output]); + outputPixel(cram.read(output)); state.x++; } diff --git a/higan/md/vdp/sprite.cpp b/higan/md/vdp/sprite.cpp index 7743379e..e7d575b9 100644 --- a/higan/md/vdp/sprite.cpp +++ b/higan/md/vdp/sprite.cpp @@ -72,7 +72,7 @@ auto VDP::Sprite::run(uint x, uint y) -> void { uint pixelY = objectY & 7; tileAddress += pixelY << 1 | pixelX >> 2; - uint16 tileData = vdp.vram[tileAddress]; + uint16 tileData = vdp.vram.read(tileAddress); uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); if(color) { output.color = o.palette << 4 | color; diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 38f6ec67..52d7e5e1 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -3,6 +3,7 @@ namespace MegaDrive { VDP vdp; +#include "memory.cpp" #include "io.cpp" #include "dma.cpp" #include "render.cpp" diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index 2c1eb505..9ade08be 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -131,9 +131,38 @@ private: auto screenWidth() const -> uint { return io.tileWidth ? 320 : 256; } auto screenHeight() const -> uint { return io.overscan ? 240 : 224; } - uint16 vram[32768]; - uint9 cram[64]; - uint10 vsram[40]; + //video RAM + struct VRAM { + //memory.cpp + auto read(uint15 address) const -> uint16; + auto write(uint15 address, uint16 data) -> void; + + auto readByte(uint16 address) const -> uint8; + auto writeByte(uint16 address, uint8 data) -> void; + + private: + uint16 memory[32768]; + } vram; + + //vertical scroll RAM + struct VSRAM { + //memory.cpp + auto read(uint6 address) const -> uint10; + auto write(uint6 address, uint10 data) -> void; + + private: + uint10 memory[40]; + } vsram; + + //color RAM + struct CRAM { + //memory.cpp + auto read(uint6 address) const -> uint9; + auto write(uint6 address, uint9 data) -> void; + + private: + uint9 memory[64]; + } cram; struct IO { //command diff --git a/higan/ms/psg/io.cpp b/higan/ms/psg/io.cpp index 0612cf6c..487aa1d1 100644 --- a/higan/ms/psg/io.cpp +++ b/higan/ms/psg/io.cpp @@ -29,6 +29,7 @@ auto PSG::write(uint8 data) -> void { case 4: { if(l) tone2.pitch.bits(0,3) = data.bits(0,3); else tone2.pitch.bits(4,9) = data.bits(0,5); + noise.pitch = tone2.pitch; break; } diff --git a/higan/ms/psg/noise.cpp b/higan/ms/psg/noise.cpp index 08675afb..8b22453e 100644 --- a/higan/ms/psg/noise.cpp +++ b/higan/ms/psg/noise.cpp @@ -1,23 +1,22 @@ auto PSG::Noise::run() -> void { - auto latch = clock; + if(--counter) return; - counter++; - if(rate == 0) output ^= !counter.bits(0,3); - if(rate == 1) output ^= !counter.bits(0,4); - if(rate == 2) output ^= !counter.bits(0,5); - if(rate == 3) output ^= psg.tone2.clock; + if(rate == 0) counter = 0x10; + if(rate == 1) counter = 0x20; + if(rate == 2) counter = 0x40; + if(rate == 3) counter = pitch; //shared with tone2 - if(!latch && clock) { + if(clock ^= 1) { //0->1 transition + output = lfsr.bit(0); auto eor = enable ? ~lfsr >> 3 : 0; lfsr = (lfsr ^ eor) << 15 | lfsr >> 1; } - - output = lfsr.bit(0); } auto PSG::Noise::power() -> void { volume = ~0; counter = 0; + pitch = 0; enable = 0; rate = 0; lfsr = 0x8000; diff --git a/higan/ms/psg/psg.cpp b/higan/ms/psg/psg.cpp index d800d4ae..5b7adc52 100644 --- a/higan/ms/psg/psg.cpp +++ b/higan/ms/psg/psg.cpp @@ -57,7 +57,7 @@ auto PSG::power() -> void { lowpassLeft = 0; lowpassRight = 0; for(auto n : range(15)) { - levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5; + levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5; } levels[15] = 0; diff --git a/higan/ms/psg/psg.hpp b/higan/ms/psg/psg.hpp index dfe41db3..8f885ff0 100644 --- a/higan/ms/psg/psg.hpp +++ b/higan/ms/psg/psg.hpp @@ -28,7 +28,6 @@ private: uint4 volume; uint10 counter; uint10 pitch; - uint1 clock; uint1 output; uint1 left; @@ -44,7 +43,8 @@ private: auto serialize(serializer&) -> void; uint4 volume; - uint6 counter; + uint10 counter; + uint10 pitch; uint1 enable; uint2 rate; uint16 lfsr; diff --git a/higan/ms/psg/serialization.cpp b/higan/ms/psg/serialization.cpp index b71592c0..9c2adf28 100644 --- a/higan/ms/psg/serialization.cpp +++ b/higan/ms/psg/serialization.cpp @@ -16,7 +16,6 @@ auto PSG::Tone::serialize(serializer& s) -> void { s.integer(volume); s.integer(counter); s.integer(pitch); - s.integer(clock); s.integer(output); s.integer(left); @@ -26,6 +25,7 @@ auto PSG::Tone::serialize(serializer& s) -> void { auto PSG::Noise::serialize(serializer& s) -> void { s.integer(volume); s.integer(counter); + s.integer(pitch); s.integer(enable); s.integer(rate); s.integer(lfsr); diff --git a/higan/ms/psg/tone.cpp b/higan/ms/psg/tone.cpp index 35877323..552f703b 100644 --- a/higan/ms/psg/tone.cpp +++ b/higan/ms/psg/tone.cpp @@ -1,8 +1,6 @@ auto PSG::Tone::run() -> void { - clock = 0; if(--counter) return; - clock = 1; counter = pitch; output ^= 1; } @@ -11,7 +9,6 @@ auto PSG::Tone::power() -> void { volume = ~0; counter = 0; pitch = 0; - clock = 0; output = 0; left = 1; diff --git a/icarus/heuristics/mega-drive.cpp b/icarus/heuristics/mega-drive.cpp index e3a98dc3..a66fddbc 100644 --- a/icarus/heuristics/mega-drive.cpp +++ b/icarus/heuristics/mega-drive.cpp @@ -11,6 +11,8 @@ struct MegaDriveCartridge { MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) { manifest.append("board\n"); manifest.append(" rom name=program.rom size=0x", hex(size), "\n"); + if(size <= 0x200000) + manifest.append(" ram name=save.ram size=0x8000\n"); manifest.append("\n"); manifest.append("information\n"); manifest.append(" title: ", Location::prefix(location), "\n");