diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index f1f9b27b..c6b85b07 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,13 +12,13 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.16"; + static const string Version = "102.17"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; //incremented only when serialization format changes - static const string SerializerVersion = "101"; + static const string SerializerVersion = "102.17"; namespace Constants { namespace Colorburst { diff --git a/higan/gba/apu/apu.cpp b/higan/gba/apu/apu.cpp index 07fce221..4759afa9 100644 --- a/higan/gba/apu/apu.cpp +++ b/higan/gba/apu/apu.cpp @@ -18,9 +18,7 @@ auto APU::Enter() -> void { } auto APU::main() -> void { - for(auto n : range(64)) { - runsequencer(); - } + runsequencer(); int lsample = regs.bias.level - 0x0200; int rsample = regs.bias.level - 0x0200; @@ -64,7 +62,7 @@ auto APU::main() -> void { if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0; stream->sample(sclamp<16>(lsample << 6) / 32768.0, sclamp<16>(rsample << 6) / 32768.0); //should be <<5; use <<6 for added volume - step(512); + step(8); } auto APU::step(uint clocks) -> void { @@ -74,8 +72,9 @@ auto APU::step(uint clocks) -> void { auto APU::power() -> void { create(APU::Enter, 16'777'216); - stream = Emulator::audio.createStream(2, frequency() / 512.0); - //todo: run sequencer at higher frequency; add low-pass filter + stream = Emulator::audio.createStream(2, frequency() / 8.0); + stream->addLowPassFilter(20000.0, 3); + stream->addHighPassFilter(20.0, 3); square1.power(); square2.power(); diff --git a/higan/md/apu/apu.cpp b/higan/md/apu/apu.cpp index 229ace32..6192dde7 100644 --- a/higan/md/apu/apu.cpp +++ b/higan/md/apu/apu.cpp @@ -10,7 +10,9 @@ auto APU::Enter() -> void { } auto APU::main() -> void { - if(!state.enabled) return step(1); + if(!state.enabled) { + return step(1); + } if(state.nmiLine) { state.nmiLine = 0; //edge-sensitive @@ -39,7 +41,9 @@ auto APU::setINT(bool value) -> void { } auto APU::enable(bool value) -> void { - if(state.enabled && !value) power(); + //68K cannot disable the Z80 without bus access + if(!bus->granted() && !value) return; + if(state.enabled && !value) reset(); state.enabled = value; } @@ -48,12 +52,12 @@ auto APU::power() -> void { Z80::power(); create(APU::Enter, system.colorburst()); memory::fill(&state, sizeof(State)); +} - r.pc = 0x0000; - r.im = 0; - r.iff1 = 0; - r.iff2 = 0; - r.ir = {}; +auto APU::reset() -> void { + create(APU::Enter, system.colorburst()); + memory::fill(&r, sizeof(Registers)); + memory::fill(&state, sizeof(State)); } } diff --git a/higan/md/apu/apu.hpp b/higan/md/apu/apu.hpp index 5e65765d..1f685250 100644 --- a/higan/md/apu/apu.hpp +++ b/higan/md/apu/apu.hpp @@ -7,6 +7,7 @@ struct APU : Processor::Z80, Thread { auto enable(bool) -> void; auto power() -> void; + auto reset() -> void; auto setNMI(bool value) -> void; auto setINT(bool value) -> void; diff --git a/higan/md/bus/bus.cpp b/higan/md/bus/bus.cpp index 19e90443..1145fdc4 100644 --- a/higan/md/bus/bus.cpp +++ b/higan/md/bus/bus.cpp @@ -7,53 +7,59 @@ BusAPU busAPU; #include "serialization.cpp" auto BusCPU::readByte(uint24 addr) -> uint16 { - if(addr < 0x400000) return cartridge.read(addr & ~1).byte(!addr.bit(0)); - if(addr < 0xa00000) return 0x0000; - if(addr < 0xa10000) return busAPU.granted() ? busAPU.read(addr) : (uint8)0x0000; - if(addr < 0xa11000) return readIO(addr & ~0xff00); - if(addr < 0xa12000) return readIO(addr & ~0x00ff); - if(addr < 0xc00000) return 0x0000; - if(addr < 0xe00000) return vdp.read(addr & ~1).byte(!addr.bit(0)); - return ram[addr & 0xffff]; + if(addr >= 0x000000 && addr <= 0x3fffff) return cartridge.read(addr & ~1).byte(!addr.bit(0)); + if(addr >= 0xa00000 && addr <= 0xa0ffff) return busAPU.granted() ? busAPU.read(addr) : (uint8)0x0000; + if(addr >= 0xa10000 && addr <= 0xa10fff) return readIO(addr & ~0xff00); + if(addr >= 0xa11000 && addr <= 0xa11fff) return readIO(addr & ~0x00ff); + if(addr >= 0xa13000 && addr <= 0xa130ff) return cartridge.readIO(addr); + if(addr >= 0xc00000 && addr <= 0xdfffff) return vdp.read(addr & ~1).byte(!addr.bit(0)); + if(addr >= 0xe00000 && addr <= 0xffffff) { + return ram[addr & 0xffff]; + } + return 0x0000; } auto BusCPU::readWord(uint24 addr) -> uint16 { - if(addr < 0x400000) return cartridge.read(addr); - if(addr < 0xa00000) return 0x0000; - if(addr < 0xa10000) return busAPU.granted() ? busAPU.read(addr) : (uint8)0x0000; - if(addr < 0xa11000) return readIO(addr & ~0xff00) << 0; - if(addr < 0xa12000) return readIO(addr & ~0x00ff) << 8; - if(addr < 0xc00000) return 0x0000; - if(addr < 0xe00000) return vdp.read(addr); - uint16 data = ram[addr + 0 & 0xffff] << 8; - return data | ram[addr + 1 & 0xffff] << 0; + if(addr >= 0x000000 && addr <= 0x3fffff) return cartridge.read(addr); + if(addr >= 0xa00000 && addr <= 0xa0ffff) return busAPU.granted() ? busAPU.read(addr) : (uint8)0x0000; + if(addr >= 0xa10000 && addr <= 0xa10fff) return readIO(addr & ~0xff00) << 0; + if(addr >= 0xa11000 && addr <= 0xa11fff) return readIO(addr & ~0x00ff) << 8; + if(addr >= 0xa13000 && addr <= 0xa130ff) return cartridge.readIO(addr); + if(addr >= 0xc00000 && addr <= 0xdfffff) return vdp.read(addr); + if(addr >= 0xe00000 && addr <= 0xffffff) { + uint16 data = ram[addr + 0 & 0xffff] << 8; + return data | ram[addr + 1 & 0xffff] << 0; + } + return 0x0000; } auto BusCPU::writeByte(uint24 addr, uint16 data) -> void { - if(addr < 0x400000) return cartridge.write(addr & ~1, data << 8 | data << 0); - if(addr < 0xa00000) return; - if(addr < 0xa10000) return busAPU.granted() ? busAPU.write(addr, data) : (void)0; - if(addr < 0xa11000) return writeIO(addr & ~0xff00, data); - if(addr < 0xa12000) return writeIO(addr & ~0x00ff, data); - if(addr < 0xc00000) return; - if(addr < 0xc00010) return vdp.write(addr & ~1, data << 8 | data << 0); - if(addr < 0xc00018) return psg.write(data); - if(addr < 0xe00000) return; - ram[addr & 0xffff] = data; + if(addr >= 0x000000 && addr <= 0x3fffff) return cartridge.write(addr & ~1, data << 8 | data << 0); + if(addr >= 0xa00000 && addr <= 0xa0ffff) return busAPU.granted() ? busAPU.write(addr, data) : (void)0; + if(addr >= 0xa10000 && addr <= 0xa10fff) return writeIO(addr & ~0xff00, data); + if(addr >= 0xa11000 && addr <= 0xa11fff) return writeIO(addr & ~0x00ff, data); + if(addr >= 0xa13000 && addr <= 0xa130ff) return cartridge.writeIO(addr, data); + if(addr >= 0xc00000 && addr <= 0xc0000f) return vdp.write(addr & ~1, data << 8 | data << 0); + if(addr >= 0xc00010 && addr <= 0xc00017) return psg.write(data); + if(addr >= 0xe00000 && addr <= 0xffffff) { + ram[addr & 0xffff] = data; + return; + } } auto BusCPU::writeWord(uint24 addr, uint16 data) -> void { - if(addr < 0x400000) return cartridge.write(addr, data); - if(addr < 0xa00000) return; - if(addr < 0xa10000) return busAPU.granted() ? busAPU.write(addr, data) : (void)0; - if(addr < 0xa11000) return writeIO(addr & ~0xff00, data >> 0); - if(addr < 0xa12000) return writeIO(addr & ~0x00ff, data >> 8); - if(addr < 0xc00000) return; - if(addr < 0xc00010) return vdp.write(addr, data); - if(addr < 0xc00018) return psg.write(data); - if(addr < 0xe00000) return; - ram[addr + 0 & 0xffff] = data >> 8; - ram[addr + 1 & 0xffff] = data >> 0; + if(addr >= 0x000000 && addr <= 0x3fffff) return cartridge.write(addr, data); + if(addr >= 0xa00000 && addr <= 0xa0ffff) return busAPU.granted() ? busAPU.write(addr, data) : (void)0; + if(addr >= 0xa10000 && addr <= 0xa10fff) return writeIO(addr & ~0xff00, data >> 0); + if(addr >= 0xa11000 && addr <= 0xa11fff) return writeIO(addr & ~0x00ff, data >> 8); + if(addr >= 0xa13000 && addr <= 0xa130ff) return cartridge.writeIO(addr, data); + if(addr >= 0xc00000 && addr <= 0xc0000f) return vdp.write(addr, data); + if(addr >= 0xc00010 && addr <= 0xc00017) return psg.write(data); + if(addr >= 0xe00000 && addr <= 0xffffff) { + ram[addr + 0 & 0xffff] = data >> 8; + ram[addr + 1 & 0xffff] = data >> 0; + return; + } } // diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index 0c209e61..480bb489 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -66,23 +66,43 @@ auto Cartridge::unload() -> void { } auto Cartridge::power() -> void { + ramEnable = 1; + ramWritable = 1; + for(auto n : range(8)) bank[n] = n; } auto Cartridge::read(uint24 addr) -> uint16 { - if(addr.bit(21) && ram.size) { + if(addr.bit(21) && ram.size && ramEnable) { uint16 data = ram.data[addr + 0 & ram.mask] << 8; return data | ram.data[addr + 1 & ram.mask] << 0; } else { + addr = bank[addr >> 19 & 7] << 19 | (addr & 0x7ffff); 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) { + //emulating RAM write protect bit breaks some commercial software + if(addr.bit(21) && ram.size && ramEnable /* && ramWritable */) { ram.data[addr + 0 & ram.mask] = data >> 8; ram.data[addr + 1 & ram.mask] = data >> 0; } } +auto Cartridge::readIO(uint24 addr) -> uint16 { + return 0x0000; +} + +auto Cartridge::writeIO(uint24 addr, uint16 data) -> void { + if(addr == 0xa130f1) ramEnable = data.bit(0), ramWritable = data.bit(1); + if(addr == 0xa130f3) bank[1] = data; + if(addr == 0xa130f5) bank[2] = data; + if(addr == 0xa130f7) bank[3] = data; + if(addr == 0xa130f9) bank[4] = data; + if(addr == 0xa130fb) bank[5] = data; + if(addr == 0xa130fd) bank[6] = data; + if(addr == 0xa130ff) bank[7] = data; +} + } diff --git a/higan/md/cartridge/cartridge.hpp b/higan/md/cartridge/cartridge.hpp index 9bd3a7b0..23b8cbfe 100644 --- a/higan/md/cartridge/cartridge.hpp +++ b/higan/md/cartridge/cartridge.hpp @@ -12,6 +12,9 @@ struct Cartridge { auto read(uint24 addr) -> uint16; auto write(uint24 addr, uint16 data) -> void; + auto readIO(uint24 addr) -> uint16; + auto writeIO(uint24 addr, uint16 data) -> void; + //serialization.cpp auto serialize(serializer&) -> void; @@ -30,6 +33,10 @@ struct Cartridge { Memory rom; Memory ram; + + uint1 ramEnable; + uint1 ramWritable; + uint6 bank[8]; }; extern Cartridge cartridge; diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index 8a7b6b0d..27c4205f 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -6,23 +6,17 @@ CPU cpu; #include "serialization.cpp" auto CPU::Enter() -> void { - cpu.boot(); while(true) scheduler.synchronize(), cpu.main(); } -auto CPU::boot() -> void { - r.a[7] = bus->readWord(0) << 16 | bus->readWord(2) << 0; - r.pc = bus->readWord(4) << 16 | bus->readWord(6) << 0; -} - auto CPU::main() -> void { - #if 0 - static file fp; - if(!fp) fp.open({Path::user(), "Desktop/tracer.log"}, file::mode::write); - fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n"); - #endif - if(state.interruptPending) { + if(state.interruptPending.bit((uint)Interrupt::Reset)) { + state.interruptPending.bit((uint)Interrupt::Reset) = 0; + r.a[7] = bus->readWord(0) << 16 | bus->readWord(2) << 0; + r.pc = bus->readWord(4) << 16 | bus->readWord(6) << 0; + } + if(state.interruptPending.bit((uint)Interrupt::HorizontalBlank)) { if(4 > r.i) { state.interruptPending.bit((uint)Interrupt::HorizontalBlank) = 0; @@ -77,6 +71,7 @@ auto CPU::power() -> void { create(CPU::Enter, system.colorburst() * 15.0 / 7.0); memory::fill(&state, sizeof(State)); + state.interruptPending.bit((uint)Interrupt::Reset) = 1; } } diff --git a/higan/md/cpu/cpu.hpp b/higan/md/cpu/cpu.hpp index af78586b..95cd5ae1 100644 --- a/higan/md/cpu/cpu.hpp +++ b/higan/md/cpu/cpu.hpp @@ -2,6 +2,7 @@ struct CPU : Processor::M68K, Thread { enum class Interrupt : uint { + Reset, HorizontalBlank, VerticalBlank, }; @@ -9,7 +10,6 @@ struct CPU : Processor::M68K, Thread { using Thread::synchronize; static auto Enter() -> void; - auto boot() -> void; auto main() -> void; auto step(uint clocks) -> void override; auto synchronize() -> void; diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index 298e8550..d25c230d 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -130,6 +130,7 @@ auto VDP::writeControlPort(uint16 data) -> void { io.command.bits(2,5) = data.bits(4,7); io.address.bits(14,15) = data.bits(0,1); + if(!dma.io.enable) io.command.bit(5) = 0; if(dma.io.mode == 3) dma.io.wait = false; return; } diff --git a/higan/md/vdp/sprite.cpp b/higan/md/vdp/sprite.cpp index e7d575b9..ca7cf459 100644 --- a/higan/md/vdp/sprite.cpp +++ b/higan/md/vdp/sprite.cpp @@ -38,6 +38,7 @@ auto VDP::Sprite::scanline(uint y) -> void { uint7 link = 0; uint tiles = 0; + uint count = 0; do { auto& object = oam[link]; link = object.link; @@ -48,7 +49,7 @@ auto VDP::Sprite::scanline(uint y) -> void { objects.append(object); tiles += object.width >> 3; - } while(link && link < 80 && objects.size() < 20 && tiles < 40); + } while(link && link < 80 && objects.size() < 20 && tiles < 40 && ++count < 80); } auto VDP::Sprite::run(uint x, uint y) -> void { diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index a880e857..da9ee4de 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -139,7 +139,7 @@ auto YM2612::sample() -> void { } int voiceData = sclamp<14>(accumulator) & outMask; - if(dac.enable && (&channel == &channels[5])) voiceData = dac.sample << 6; + if(dac.enable && (&channel == &channels[5])) voiceData = (int)dac.sample - 0x80 << 6; if(channel.leftEnable ) left += voiceData; if(channel.rightEnable) right += voiceData; diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index e6c1c5fa..9afb359f 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -1121,8 +1121,12 @@ auto M68K::instructionSWAP(DataRegister with) -> void { } auto M68K::instructionTAS(EffectiveAddress with) -> void { - auto data = read(with); - write(with, data | 0x80); +//auto data = read(with); +//write(with, data | 0x80); + + //Mega Drive models 1&2 have a bug that prevents TAS write cycle from completing + //this bugged behavior is required for certain software to function correctly + auto data = read(with); r.c = 0; r.v = 0; @@ -1131,7 +1135,7 @@ auto M68K::instructionTAS(EffectiveAddress with) -> void { } auto M68K::instructionTRAP(uint4 vector) -> void { - exception(Exception::Trap, 32 + vector); + exception(Exception::Trap, 32 + vector, r.i); } auto M68K::instructionTRAPV() -> void { diff --git a/icarus/heuristics/mega-drive.cpp b/icarus/heuristics/mega-drive.cpp index a66fddbc..24baf7c3 100644 --- a/icarus/heuristics/mega-drive.cpp +++ b/icarus/heuristics/mega-drive.cpp @@ -9,10 +9,29 @@ struct MegaDriveCartridge { }; MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) { + if(size < 0x200) return; + + uint32_t ramFrom = 0; + ramFrom |= data[0x01b4] << 24; + ramFrom |= data[0x01b5] << 16; + ramFrom |= data[0x01b6] << 8; + ramFrom |= data[0x01b7] << 0; + ramFrom &= ~1; //for some reason, most games specify 00200001 as RAM start offset + + uint32_t ramTo = 0; + ramTo |= data[0x01b8] << 24; + ramTo |= data[0x01b9] << 16; + ramTo |= data[0x01ba] << 8; + ramTo |= data[0x01bb] << 0; + + uint32_t ramSize = ramTo - ramFrom; + if(ramSize > 0x020000) ramSize = 0; //sanity check + ramSize = bit::round(ramSize); + 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"); + if(ramSize) + manifest.append(" ram name=save.ram size=0x", hex(ramSize), " offset=0x", hex(ramFrom), "\n"); manifest.append("\n"); manifest.append("information\n"); manifest.append(" title: ", Location::prefix(location), "\n");