From d76c0c7e82810c547b0aa9cc8c4145985cbf97d7 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 20 Feb 2017 19:13:10 +1100 Subject: [PATCH] Update to v102r08 release. byuu says: Changelog: - PCE: restructured VCE, VDCs to run one scanline at a time - PCE: bound VDCs to 1365x262 timing (in order to decouple the VDCs from the VCE) - PCE: the two changes above allow save states to function; also grants a minor speed boost - PCE: added cheat code support (uses 21-bit bus addressing; compare byte will be useful here) - 68K: fixed `mov *,ccr` to read two bytes instead of one [Cydrak] - Z80: emulated /BUSREQ, /BUSACK; allows 68K to suspend the Z80 [Cydrak] - MD: emulated the Z80 executing instructions [Cydrak] - MD: emulated Z80 interrupts (triggered during each Vblank period) [Cydrak] - MD: emulated Z80 memory map [Cydrak] - MD: added stubs for PSG, YM2612 accesses [Cydrak] - MD: improved bus emulation [Cydrak] The PCE core is pretty much ready to go. The only major feature missing is FM modulation. The Mega Drive improvements let us start to see the splash screens for Langrisser II, Shining Force, Shining in the Darkness. I was hoping I could get them in-game, but no such luck. My Z80 implementation is probably flawed in some way ... now that I think about it, I believe I missed the BusAPU::reset() check for having been granted access to the Z80 first. But I doubt that's the problem. Next step is to implement Cydrak's PSG core into the Master System emulator. Once that's in, I'm going to add save states and cheat code support to the Master System core. Next, I'll add the PSG core into the Mega Drive. Then I'll add the 'easy' PCM part of the YM2612. Then the rest of the beastly YM2612 core. Then finally, cap things off with save state and cheat code support. Should be nearing a new release at that point. --- higan/emulator/emulator.hpp | 2 +- higan/md/apu/apu.cpp | 34 ++++++++++- higan/md/apu/apu.hpp | 11 ++++ higan/md/bus/bus.cpp | 73 +++++++++++++++++++---- higan/md/bus/bus.hpp | 5 ++ higan/md/psg/io.cpp | 2 + higan/md/psg/psg.cpp | 7 ++- higan/md/psg/psg.hpp | 3 + higan/md/vdp/vdp.cpp | 3 + higan/md/ym2612/io.cpp | 9 +++ higan/md/ym2612/ym2612.cpp | 5 +- higan/md/ym2612/ym2612.hpp | 7 +++ higan/pce/cpu/cpu.hpp | 1 + higan/pce/cpu/io.cpp | 6 ++ higan/pce/interface/interface.cpp | 6 +- higan/pce/interface/interface.hpp | 2 + higan/pce/pce.hpp | 3 + higan/pce/system/system.cpp | 1 + higan/pce/vce/vce.cpp | 58 +++++++++--------- higan/pce/vdc/vdc.cpp | 85 +++++++++++++-------------- higan/processor/m68k/instructions.cpp | 2 +- higan/processor/z80/memory.cpp | 16 +++++ higan/processor/z80/z80.cpp | 2 + higan/processor/z80/z80.hpp | 12 ++++ 24 files changed, 262 insertions(+), 93 deletions(-) create mode 100644 higan/md/psg/io.cpp create mode 100644 higan/md/ym2612/io.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index b70dbed8..681fe677 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.07"; + static const string Version = "102.08"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/apu/apu.cpp b/higan/md/apu/apu.cpp index 4cd21ae4..0720d799 100644 --- a/higan/md/apu/apu.cpp +++ b/higan/md/apu/apu.cpp @@ -9,7 +9,19 @@ auto APU::Enter() -> void { } auto APU::main() -> void { - step(1); + if(!state.enabled) return step(1); + + if(state.nmiLine) { + state.nmiLine = 0; //edge-sensitive + irq(0, 0x0066, 0xff); + } + + if(state.intLine) { + //level-sensitive + irq(1, 0x0038, 0xff); + } + + instruction(); } auto APU::step(uint clocks) -> void { @@ -17,10 +29,30 @@ auto APU::step(uint clocks) -> void { synchronize(cpu); } +auto APU::setNMI(bool value) -> void { + state.nmiLine = value; +} + +auto APU::setINT(bool value) -> void { + state.intLine = value; +} + +auto APU::enable(bool value) -> void { + if(state.enabled && !value) power(); + state.enabled = value; +} + auto APU::power() -> void { Z80::bus = &busAPU; 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 = {}; } } diff --git a/higan/md/apu/apu.hpp b/higan/md/apu/apu.hpp index 938513d7..ec0d7b3f 100644 --- a/higan/md/apu/apu.hpp +++ b/higan/md/apu/apu.hpp @@ -5,7 +5,18 @@ struct APU : Processor::Z80, Thread { auto main() -> void; auto step(uint clocks) -> void; + auto enable(bool) -> void; auto power() -> void; + + auto setNMI(bool value) -> void; + auto setINT(bool value) -> void; + +private: + struct State { + boolean enabled; + boolean nmiLine; + boolean intLine; + } state; }; extern APU apu; diff --git a/higan/md/bus/bus.cpp b/higan/md/bus/bus.cpp index 2708a36f..0247edda 100644 --- a/higan/md/bus/bus.cpp +++ b/higan/md/bus/bus.cpp @@ -8,8 +8,9 @@ BusAPU busAPU; 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 0x0000; - if(addr < 0xa10020) return readIO(addr); + 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]; @@ -18,8 +19,9 @@ auto BusCPU::readByte(uint24 addr) -> uint16 { auto BusCPU::readWord(uint24 addr) -> uint16 { if(addr < 0x400000) return cartridge.read(addr); if(addr < 0xa00000) return 0x0000; - if(addr < 0xa10000) return 0x0000; - if(addr < 0xa10020) return readIO(addr); + 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; @@ -29,20 +31,26 @@ auto BusCPU::readWord(uint24 addr) -> uint16 { 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; - if(addr < 0xa10020) return writeIO(addr, data); + 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 < 0xe00000) return vdp.write(addr & ~1, data << 8 | data << 0); + 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; } auto BusCPU::writeWord(uint24 addr, uint16 data) -> void { if(addr < 0x400000) return cartridge.write(addr, data); if(addr < 0xa00000) return; - if(addr < 0xa10000) return; - if(addr < 0xa10020) return writeIO(addr, data); + 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 < 0xe00000) return vdp.write(addr, data); + 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; } @@ -65,6 +73,8 @@ auto BusCPU::readIO(uint24 addr) -> uint16 { case 0xa10008: return peripherals.controllerPort1->readControl(); case 0xa1000a: return peripherals.controllerPort2->readControl(); case 0xa1000c: return peripherals.extensionPort->readControl(); + + case 0xa11000: return !busAPU.granted(); } return 0x0000; @@ -79,22 +89,63 @@ auto BusCPU::writeIO(uint24 addr, uint16 data) -> void { case 0xa10008: return peripherals.controllerPort1->writeControl(data); case 0xa1000a: return peripherals.controllerPort2->writeControl(data); case 0xa1000c: return peripherals.extensionPort->writeControl(data); + + case 0xa11100: return busAPU.request(data.bit(0)); + case 0xa11200: return apu.enable(data.bit(0)); } } // auto BusAPU::read(uint16 addr) -> uint8 { - return 0x00; + if((addr & 0xe000) == 0x0000) { + return ram[addr]; + } + + if(addr == 0x4000) return ym2612.readStatus(); + if(addr == 0x4001) return ym2612.readStatus(); + if(addr == 0x4002) return ym2612.readStatus(); + if(addr == 0x4003) return ym2612.readStatus(); + + if((addr & 0x8000) == 0x8000) { + return cartridge.read(bank << 15 | (addr & 0x7ffe)).byte(!addr.bit(0)); + } } auto BusAPU::write(uint16 addr, uint8 data) -> void { + if((addr & 0xe000) == 0x0000) { + ram[addr] = data; + return; + } + + if(addr == 0x4000) return ym2612.writeAddress(0 << 8 | data); + if(addr == 0x4001) return ym2612.writeData(data); + if(addr == 0x4002) return ym2612.writeAddress(1 << 8 | data); + if(addr == 0x4003) return ym2612.writeData(data); + + if(addr == 0x6000) { + //1-bit shift register + bank = data.bit(0) << 8 | bank >> 1; + return; + } + + if(addr == 0x7f11) return psg.write(data); + if(addr == 0x7f13) return psg.write(data); + if(addr == 0x7f15) return psg.write(data); + if(addr == 0x7f17) return psg.write(data); + + if((addr & 0x8000) == 0x8000) { + //todo: do 8-bit writes mirror to 16-bits? + return cartridge.write(bank << 15 | (addr & 0x7ffe), data << 8 | data << 0); + } } +//unused on Mega Drive auto BusAPU::in(uint8 addr) -> uint8 { return 0x00; } +//unused on Mega Drive auto BusAPU::out(uint8 addr, uint8 data) -> void { } diff --git a/higan/md/bus/bus.hpp b/higan/md/bus/bus.hpp index 57f470ef..542a6904 100644 --- a/higan/md/bus/bus.hpp +++ b/higan/md/bus/bus.hpp @@ -14,8 +14,13 @@ private: struct BusAPU : Processor::Z80::Bus { auto read(uint16 addr) -> uint8 override; auto write(uint16 addr, uint8 data) -> void override; + auto in(uint8 addr) -> uint8 override; auto out(uint8 addr, uint8 data) -> void override; + +private: + uint8 ram[8 * 1024]; + uint9 bank; }; extern BusCPU busCPU; diff --git a/higan/md/psg/io.cpp b/higan/md/psg/io.cpp new file mode 100644 index 00000000..bd4c579e --- /dev/null +++ b/higan/md/psg/io.cpp @@ -0,0 +1,2 @@ +auto PSG::write(uint8 data) -> void { +} diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index 3af5686d..20bfa81f 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -3,13 +3,14 @@ namespace MegaDrive { PSG psg; +#include "io.cpp" auto PSG::Enter() -> void { while(true) scheduler.synchronize(), psg.main(); } auto PSG::main() -> void { - stream->sample(0.0, 0.0); + stream->sample(0.0); step(1); } @@ -19,8 +20,8 @@ auto PSG::step(uint clocks) -> void { } auto PSG::power() -> void { - create(PSG::Enter, 52'000); //system.colorburst()); - stream = Emulator::audio.createStream(2, 52'000.0); + create(PSG::Enter, system.colorburst() / 16.0); + stream = Emulator::audio.createStream(1, system.colorburst() / 16.0); } } diff --git a/higan/md/psg/psg.hpp b/higan/md/psg/psg.hpp index 9b11454b..cd988d7c 100644 --- a/higan/md/psg/psg.hpp +++ b/higan/md/psg/psg.hpp @@ -8,6 +8,9 @@ struct PSG : Thread { auto step(uint clocks) -> void; auto power() -> void; + + //io.cpp + auto write(uint8 data) -> void; }; extern PSG psg; diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index bd44bb15..e465d99c 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -33,8 +33,10 @@ auto VDP::main() -> void { if(io.verticalBlankInterruptEnable) { cpu.raise(CPU::Interrupt::VerticalBlank); } + apu.setINT(true); } step(1710); + apu.setINT(false); } } @@ -43,6 +45,7 @@ auto VDP::step(uint clocks) -> void { dmaRun(); Thread::step(1); synchronize(cpu); + synchronize(apu); } } diff --git a/higan/md/ym2612/io.cpp b/higan/md/ym2612/io.cpp new file mode 100644 index 00000000..4423fdaa --- /dev/null +++ b/higan/md/ym2612/io.cpp @@ -0,0 +1,9 @@ +auto YM2612::readStatus() -> uint8 { + return nall::random(); +} + +auto YM2612::writeAddress(uint9 data) -> void { +} + +auto YM2612::writeData(uint8 data) -> void { +} diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index acfb3029..195d34ce 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -3,13 +3,15 @@ namespace MegaDrive { YM2612 ym2612; +#include "io.cpp" auto YM2612::Enter() -> void { while(true) scheduler.synchronize(), ym2612.main(); } auto YM2612::main() -> void { - step(1); + stream->sample(0.0, 0.0); + step(144); } auto YM2612::step(uint clocks) -> void { @@ -19,6 +21,7 @@ auto YM2612::step(uint clocks) -> void { auto YM2612::power() -> void { create(YM2612::Enter, system.colorburst() * 15.0 / 7.0); + stream = Emulator::audio.createStream(2, system.colorburst() * 15.0 / 7.0 / 144.0); } } diff --git a/higan/md/ym2612/ym2612.hpp b/higan/md/ym2612/ym2612.hpp index f15eb4be..328d1da5 100644 --- a/higan/md/ym2612/ym2612.hpp +++ b/higan/md/ym2612/ym2612.hpp @@ -1,11 +1,18 @@ //Yamaha YM2612 struct YM2612 : Thread { + shared_pointer stream; + static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; auto power() -> void; + + //io.cpp + auto readStatus() -> uint8; + auto writeAddress(uint9 data) -> void; + auto writeData(uint8 data) -> void; }; extern YM2612 ym2612; diff --git a/higan/pce/cpu/cpu.hpp b/higan/pce/cpu/cpu.hpp index 97f02eea..b7a52f36 100644 --- a/higan/pce/cpu/cpu.hpp +++ b/higan/pce/cpu/cpu.hpp @@ -13,6 +13,7 @@ struct CPU : Processor::HuC6280, Thread { //io.cpp auto read(uint8 bank, uint13 addr) -> uint8 override; + auto read_(uint8 bank, uint13 addr) -> uint8; auto write(uint8 bank, uint13 addr, uint8 data) -> void override; auto store(uint2 addr, uint8 data) -> void override; diff --git a/higan/pce/cpu/io.cpp b/higan/pce/cpu/io.cpp index 7970873a..ab68c4f1 100644 --- a/higan/pce/cpu/io.cpp +++ b/higan/pce/cpu/io.cpp @@ -1,4 +1,10 @@ auto CPU::read(uint8 bank, uint13 addr) -> uint8 { + auto data = read_(bank, addr); + if(auto result = cheat.find(bank << 13 | addr, data)) data = result(); + return data; +} + +auto CPU::read_(uint8 bank, uint13 addr) -> uint8 { //$00-7f HuCard if(!bank.bit(7)) { return cartridge.read(bank << 13 | addr); diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index 4a4cd23f..b0b0b6ec 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -11,7 +11,7 @@ Interface::Interface() { information.overscan = true; information.capability.states = true; - information.capability.cheats = false; + information.capability.cheats = true; Port controllerPort{ID::Port::Controller, "Controller Port"}; @@ -116,6 +116,10 @@ auto Interface::unserialize(serializer& s) -> bool { return system.unserialize(s); } +auto Interface::cheatSet(const string_vector& list) -> void { + cheat.assign(list); +} + auto Interface::cap(const string& name) -> bool { return false; } diff --git a/higan/pce/interface/interface.hpp b/higan/pce/interface/interface.hpp index ed6000b5..dc20b3e8 100644 --- a/higan/pce/interface/interface.hpp +++ b/higan/pce/interface/interface.hpp @@ -43,6 +43,8 @@ struct Interface : Emulator::Interface { auto serialize() -> serializer override; auto unserialize(serializer&) -> bool override; + auto cheatSet(const string_vector&) -> void override; + auto cap(const string& name) -> bool override; auto get(const string& name) -> any override; auto set(const string& name, const any& value) -> bool override; diff --git a/higan/pce/pce.hpp b/higan/pce/pce.hpp index 3a31dae5..9512e97d 100644 --- a/higan/pce/pce.hpp +++ b/higan/pce/pce.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -13,7 +14,9 @@ namespace PCEngine { #define platform Emulator::platform namespace File = Emulator::File; using Scheduler = Emulator::Scheduler; + using Cheat = Emulator::Cheat; extern Scheduler scheduler; + extern Cheat cheat; struct Thread : Emulator::Thread { auto create(auto (*entrypoint)() -> void, double frequency) -> void { diff --git a/higan/pce/system/system.cpp b/higan/pce/system/system.cpp index fec92d79..f5c44b82 100644 --- a/higan/pce/system/system.cpp +++ b/higan/pce/system/system.cpp @@ -4,6 +4,7 @@ namespace PCEngine { System system; Scheduler scheduler; +Cheat cheat; #include "peripherals.cpp" #include "serialization.cpp" diff --git a/higan/pce/vce/vce.cpp b/higan/pce/vce/vce.cpp index 373edd23..80a97c35 100644 --- a/higan/pce/vce/vce.cpp +++ b/higan/pce/vce/vce.cpp @@ -12,38 +12,36 @@ auto VCE::Enter() -> void { } auto VCE::main() -> void { - vdc0.frame(); - vdc1.frame(); - timing.vclock = 0; - - while(timing.vclock < 262) { - vdc0.scanline(); - vdc1.scanline(); - timing.hclock = 0; - - auto output = buffer + 1365 * timing.vclock; - - while(timing.hclock < 1360) { - uint9 color; - if(Model::PCEngine()) color = vdc0.bus(); - if(Model::SuperGrafx()) color = vpc.bus(timing.hclock); - color = cram.read(color); - - //*output++ = color; - //step(1); - - if(clock() >= 2) *output++ = color; - if(clock() >= 2) *output++ = color; - if(clock() >= 3) *output++ = color; - if(clock() >= 4) *output++ = color; - step(clock()); - } - - step(1365 - timing.hclock); - timing.vclock++; + if(timing.vclock == 0) { + vdc0.frame(); + vdc1.frame(); } - scheduler.exit(Scheduler::Event::Frame); + vdc0.scanline(); + vdc1.scanline(); + timing.hclock = 0; + + auto output = buffer + 1365 * timing.vclock; + + while(timing.hclock < 1360) { + uint9 color; + if(Model::PCEngine()) color = vdc0.bus(); + if(Model::SuperGrafx()) color = vpc.bus(timing.hclock); + color = cram.read(color); + + if(clock() >= 2) *output++ = color; + if(clock() >= 2) *output++ = color; + if(clock() >= 3) *output++ = color; + if(clock() >= 4) *output++ = color; + step(clock()); + } + + step(1365 - timing.hclock); + + if(++timing.vclock == 262) { + timing.vclock = 0; + scheduler.exit(Scheduler::Event::Frame); + } } auto VCE::step(uint clocks) -> void { diff --git a/higan/pce/vdc/vdc.cpp b/higan/pce/vdc/vdc.cpp index 8b0cb7e9..db9bf840 100644 --- a/higan/pce/vdc/vdc.cpp +++ b/higan/pce/vdc/vdc.cpp @@ -23,61 +23,58 @@ auto VDC::Enter() -> void { auto VDC::main() -> void { if(Model::PCEngine() && vdc1.active()) return step(frequency()); - timing.vpulse = false; - timing.vclock = 0; - timing.voffset = 0; - timing.vstart = max((uint8)2, timing.verticalDisplayStart) - 2; - timing.vlength = min(242, timing.verticalDisplayLength + 1); + if(timing.vclock == 0) { + timing.voffset = 0; + timing.vstart = max((uint8)2, timing.verticalDisplayStart) - 2; + timing.vlength = min(242, timing.verticalDisplayLength + 1); + } - while(!timing.vpulse) { - timing.hpulse = false; - timing.hclock = 0; - timing.hoffset = 0; - timing.hstart = timing.horizontalDisplayStart; - timing.hlength = (timing.horizontalDisplayLength + 1) << 3; + timing.hclock = 0; + timing.hoffset = 0; + timing.hstart = timing.horizontalDisplayStart; + timing.hlength = (timing.horizontalDisplayLength + 1) << 3; - if(timing.vclock >= timing.vstart && timing.voffset < timing.vlength) { - background.scanline(timing.voffset); - sprite.scanline(timing.voffset); + if(timing.vclock >= timing.vstart && timing.voffset < timing.vlength) { + background.scanline(timing.voffset); + sprite.scanline(timing.voffset); - step(timing.hstart); + step(timing.hstart); - while(timing.hoffset < timing.hlength) { - data = 0; - - background.run(timing.hoffset, timing.voffset); - sprite.run(timing.hoffset, timing.voffset); - - if(sprite.color && sprite.priority) { - data = 1 << 8 | sprite.palette << 4 | sprite.color << 0; - } else if(background.color) { - data = 0 << 8 | background.palette << 4 | background.color << 0; - } else if(sprite.color) { - data = 1 << 8 | sprite.palette << 4 | sprite.color << 0; - } - - step(vce.clock()); - timing.hoffset++; - } + while(timing.hclock < 1360 && timing.hoffset < timing.hlength) { data = 0; - if(timing.voffset == io.lineCoincidence - 64) { - irq.raise(IRQ::Line::LineCoincidence); + background.run(timing.hoffset, timing.voffset); + sprite.run(timing.hoffset, timing.voffset); + + if(sprite.color && sprite.priority) { + data = 1 << 8 | sprite.palette << 4 | sprite.color << 0; + } else if(background.color) { + data = 0 << 8 | background.palette << 4 | background.color << 0; + } else if(sprite.color) { + data = 1 << 8 | sprite.palette << 4 | sprite.color << 0; } - while(!timing.hpulse) step(1); - timing.vclock++; - timing.voffset++; - } else { - data = 0; - while(!timing.hpulse) step(1); - timing.vclock++; + step(vce.clock()); + timing.hoffset++; } - if(timing.vclock == timing.vstart + timing.vlength) { - irq.raise(IRQ::Line::Vblank); - dma.satbStart(); + if(timing.voffset == io.lineCoincidence - 64) { + irq.raise(IRQ::Line::LineCoincidence); } + + timing.voffset++; + } + + data = 0; + step(1365 - timing.hclock); + + if(timing.vclock == timing.vstart + timing.vlength) { + irq.raise(IRQ::Line::Vblank); + dma.satbStart(); + } + + if(++timing.vclock == 262) { + timing.vclock = 0; } } diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index a4e95ee5..492c4eab 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -706,7 +706,7 @@ auto M68K::instructionMOVE_FROM_SR(EffectiveAddress ea) -> void { } auto M68K::instructionMOVE_TO_CCR(EffectiveAddress ea) -> void { - auto data = read(ea); + auto data = read(ea); writeCCR(data); } diff --git a/higan/processor/z80/memory.cpp b/higan/processor/z80/memory.cpp index 39c2918a..187dfe4d 100644 --- a/higan/processor/z80/memory.cpp +++ b/higan/processor/z80/memory.cpp @@ -1,13 +1,25 @@ +auto Z80::yield() -> void { + //freeze Z80, allow external access until relinquished + if(bus->requested()) { + bus->grant(true); + while(bus->requested()) step(1); + bus->grant(false); + } +} + auto Z80::wait(uint clocks) -> void { + yield(); step(clocks); } auto Z80::opcode() -> uint8 { + yield(); step(4); return bus->read(r.pc++); } auto Z80::operand() -> uint8 { + yield(); step(3); return bus->read(r.pc++); } @@ -35,21 +47,25 @@ auto Z80::displace(uint16& x) -> uint16 { } auto Z80::read(uint16 addr) -> uint8 { + yield(); step(3); return bus->read(addr); } auto Z80::write(uint16 addr, uint8 data) -> void { + yield(); step(3); return bus->write(addr, data); } auto Z80::in(uint8 addr) -> uint8 { + yield(); step(4); return bus->in(addr); } auto Z80::out(uint8 addr, uint8 data) -> void { + yield(); step(4); return bus->out(addr, data); } diff --git a/higan/processor/z80/z80.cpp b/higan/processor/z80/z80.cpp index 8f403a1d..3ce14d05 100644 --- a/higan/processor/z80/z80.cpp +++ b/higan/processor/z80/z80.cpp @@ -12,6 +12,8 @@ namespace Processor { auto Z80::power() -> void { memory::fill(&r, sizeof(Registers)); r.hlp = &r.hl; + bus->request(false); + bus->grant(false); } auto Z80::irq(bool maskable, uint16 pc, uint8 extbus) -> bool { diff --git a/higan/processor/z80/z80.hpp b/higan/processor/z80/z80.hpp index 3c8feebd..4a19ee9a 100644 --- a/higan/processor/z80/z80.hpp +++ b/higan/processor/z80/z80.hpp @@ -6,10 +6,21 @@ namespace Processor { struct Z80 { struct Bus { + virtual auto requested() -> bool { return _requested; } + virtual auto granted() -> bool { return _granted; } + + virtual auto request(bool value) -> void { _requested = value; } + virtual auto grant(bool value) -> void { _granted = value; } + virtual auto read(uint16 addr) -> uint8 = 0; virtual auto write(uint16 addr, uint8 data) -> void = 0; + virtual auto in(uint8 addr) -> uint8 = 0; virtual auto out(uint8 addr, uint8 data) -> void = 0; + + private: + bool _requested; + bool _granted; }; virtual auto step(uint clocks) -> void = 0; @@ -21,6 +32,7 @@ struct Z80 { auto parity(uint8) const -> bool; //memory.cpp + auto yield() -> void; auto wait(uint clocks = 1) -> void; auto opcode() -> uint8; auto operand() -> uint8;