diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 8155071c..7c241d4a 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 = "101.33"; + static const string Version = "101.34"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/pce/controller/controller.hpp b/higan/pce/controller/controller.hpp index 6701fdc0..1684eeb9 100644 --- a/higan/pce/controller/controller.hpp +++ b/higan/pce/controller/controller.hpp @@ -5,7 +5,8 @@ struct Controller : Thread { static auto Enter() -> void; auto main() -> void; - virtual auto readData() -> uint8 { return 0x00; } + virtual auto readData() -> uint4 { return 0; } + virtual auto writeData(uint2) -> void {} }; #include "gamepad/gamepad.hpp" diff --git a/higan/pce/controller/gamepad/gamepad.cpp b/higan/pce/controller/gamepad/gamepad.cpp index ca1d7204..898bcc6c 100644 --- a/higan/pce/controller/gamepad/gamepad.cpp +++ b/higan/pce/controller/gamepad/gamepad.cpp @@ -1,6 +1,29 @@ Gamepad::Gamepad() { } -auto Gamepad::readData() -> uint8 { - return 0x00; +auto Gamepad::readData() -> uint4 { + if(clr) return 0; + + uint4 data; + + if(sel) { + data.bit(0) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Up); + data.bit(1) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Down); + data.bit(2) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Right); + data.bit(3) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Left); + if(data.bits(0,1) == 0) data.bits(0,1) = 3; //disallow up+down at the same time + if(data.bits(2,3) == 0) data.bits(2,3) = 3; //disallow left+right at the same time + } else { + data.bit(0) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, One); + data.bit(1) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Two); + data.bit(2) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Select); + data.bit(3) = !platform->inputPoll(ID::Port::Controller, ID::Device::Gamepad, Run); + } + + return data; +} + +auto Gamepad::writeData(uint2 data) -> void { + sel = data.bit(0); + clr = data.bit(1); } diff --git a/higan/pce/controller/gamepad/gamepad.hpp b/higan/pce/controller/gamepad/gamepad.hpp index faae0e12..3db24d20 100644 --- a/higan/pce/controller/gamepad/gamepad.hpp +++ b/higan/pce/controller/gamepad/gamepad.hpp @@ -5,5 +5,9 @@ struct Gamepad : Controller { Gamepad(); - auto readData() -> uint8 override; + auto readData() -> uint4 override; + auto writeData(uint2 data) -> void override; + + bool sel; + bool clr; }; diff --git a/higan/pce/cpu/cpu.cpp b/higan/pce/cpu/cpu.cpp index df42b27a..ed41bc73 100644 --- a/higan/pce/cpu/cpu.cpp +++ b/higan/pce/cpu/cpu.cpp @@ -3,6 +3,9 @@ namespace PCEngine { CPU cpu; +#include "io.cpp" +#include "irq.cpp" +#include "timer.cpp" auto CPU::Enter() -> void { while(true) scheduler.synchronize(), cpu.main(); @@ -16,11 +19,13 @@ auto CPU::main() -> void { } #endif + if(irq.pending()) interrupt(irq.vector()); instruction(); } auto CPU::step(uint clocks) -> void { Thread::step(clocks); + timer.step(clocks); synchronize(vdc); synchronize(psg); for(auto peripheral : peripherals) synchronize(*peripheral); @@ -28,33 +33,17 @@ auto CPU::step(uint clocks) -> void { auto CPU::power() -> void { HuC6280::power(); - create(CPU::Enter, system.colorburst() * 6.0); + create(CPU::Enter, system.colorburst() * 2.0); r.pc.byte(0) = read(0x1ffe); r.pc.byte(1) = read(0x1fff); -} -auto CPU::read(uint21 addr) -> uint8 { - if(!addr.bit(20)) return cartridge.read(addr); - uint8 bank = addr.bits(13,20); - addr = addr.bits(0,12); - if(bank >= 0xf8 && bank <= 0xfb) return ram[addr]; - if(bank == 0xff) return 0x00; //hardware - return 0xff; -} - -auto CPU::write(uint21 addr, uint8 data) -> void { - if(!addr.bit(20)) return cartridge.write(addr, data); - uint8 bank = addr.bits(13,20); - addr = addr.bits(0,12); - if(bank >= 0xf8 && bank <= 0xfb) { ram[addr] = data; return; } - if(bank == 0xff) return; //hardware -} - -auto CPU::st(uint2 addr, uint8 data) -> void { + memory::fill(&irq, sizeof(IRQ)); + memory::fill(&timer, sizeof(Timer)); } auto CPU::lastCycle() -> void { + irq.poll(); } } diff --git a/higan/pce/cpu/cpu.hpp b/higan/pce/cpu/cpu.hpp index 94f3624d..2f58befa 100644 --- a/higan/pce/cpu/cpu.hpp +++ b/higan/pce/cpu/cpu.hpp @@ -6,14 +6,55 @@ struct CPU : Processor::HuC6280, Thread { auto step(uint clocks) -> void override; auto power() -> void; - - auto read(uint21 addr) -> uint8 override; - auto write(uint21 addr, uint8 data) -> void override; - auto st(uint2 addr, uint8 data) -> void override; auto lastCycle() -> void override; + //io.cpp + auto read(uint21 addr) -> uint8 override; + auto write(uint21 addr, uint8 data) -> void override; + + //timer.cpp + auto timerStep(uint clocks) -> void; + vector peripherals; + struct IRQ { + enum class Line : uint { External, VDC, Timer }; + + //irq.cpp + auto pending() const -> bool; + auto vector() const -> uint16; + auto poll() -> void; + auto level(Line, bool = 1) -> void; + + private: + bool disableExternal; + bool disableVDC; + bool disableTimer; + + bool pendingExternal; + bool pendingVDC; + bool pendingTimer; + + bool pendingIRQ; + uint16 pendingVector; + + friend class CPU; + } irq; + + struct Timer { + //timer.cpp + auto start() -> void; + auto step(uint clocks) -> void; + + private: + bool enable; + uint7 latch; + uint7 value; + uint clock; + + friend class CPU; + } timer; + private: uint8 ram[0x2000]; }; diff --git a/higan/pce/cpu/io.cpp b/higan/pce/cpu/io.cpp new file mode 100644 index 00000000..2d543f8c --- /dev/null +++ b/higan/pce/cpu/io.cpp @@ -0,0 +1,147 @@ +auto CPU::read(uint21 addr) -> uint8 { + //$000000-0fffff HuCard + if(!addr.bit(20)) { + return cartridge.read(addr); + } + + uint8 bank = addr.bits(13,20); + addr = addr.bits(0,12); + + //$1f8000-1fbfff RAM + if(bank >= 0xf8 && bank <= 0xfb) { + return ram[addr]; + } + + //$1fe000-$1fffff Hardware + if(bank == 0xff) { + //$0000-03ff VDC + //$0400-07ff VCE + if((addr & 0x1800) == 0x0000) { + return vdc.read(addr); + } + + //$0800-0bff PSG + if((addr & 0x1c00) == 0x0800) { + return 0x00; + } + + //$0c00-0fff Timer + if((addr & 0x1c00) == 0x0c00) { + return timer.value; + } + + //$1000-13ff I/O + if((addr & 0x1c00) == 0x1000) { + return ( + PCEngine::peripherals.controllerPort->readData() << 0 + | 1 << 4 + | 1 << 5 + | 0 << 6 //device (0 = Turbografx-16; 1 = PC Engine) + | 1 << 7 //add-on (0 = CD-ROM; 1 = nothing) + ); + } + + //$1400-17ff IRQ + if((addr & 0x1c00) == 0x1400) { + if(addr.bits(0,1) == 2) { + return ( + irq.disableExternal << 0 + | irq.disableVDC << 1 + | irq.disableTimer << 2 + ); + } + + if(addr.bits(0,1) == 3) { + return ( + irq.pendingExternal << 0 + | irq.pendingVDC << 1 + | irq.pendingTimer << 2 + ); + } + } + + //$1800-1bff CD-ROM + if((addr & 0x1c00) == 0x1800) { + return 0xff; + } + + //$1c00-1fff unmapped + if((addr & 0x1c00) == 0x1c00) { + return 0xff; + } + } + + return 0x00; +} + +auto CPU::write(uint21 addr, uint8 data) -> void { + //$000000-0fffff HuCard + if(!addr.bit(20)) { + return cartridge.write(addr, data); + } + + uint8 bank = addr.bits(13,20); + addr = addr.bits(0,12); + + //$1f8000-1fbfff RAM + if(bank >= 0xf8 && bank <= 0xfb) { + ram[addr] = data; + return; + } + + //$1fe000-1fffff Hardware + if(bank == 0xff) { + //$0000-03ff VDC + //$0400-07ff VCE + if((addr & 0x1800) == 0x0000) { + return vdc.write(addr, data); + } + + //$0800-0bff PSG + if((addr & 0x1c00) == 0x0800) { + return; + } + + //$0c00-0fff Timer + if((addr & 0x1c00) == 0x0c00) { + if(!addr.bit(0)) { + timer.latch = data.bits(0,6); + } else { + timer.enable = data.bit(0); + if(timer.enable) timer.start(); + } + return; + } + + //$1000-13ff I/O + if((addr & 0x1c00) == 0x1000) { + PCEngine::peripherals.controllerPort->writeData(data.bits(0,1)); + return; + } + + //$1400-17ff IRQ + if((addr & 0x1c00) == 0x1400) { + if(addr.bits(0,1) == 2) { + irq.disableExternal = data.bit(0); + irq.disableVDC = data.bit(1); + irq.disableTimer = data.bit(2); + return; + } + + if(addr.bits(0,1) == 3) { + irq.level(IRQ::Line::Timer, 0); + return; + } + } + + //$1800-1bff CD-ROM + if((addr & 0x1c00) == 0x1800) { + return; + } + + //$1c00-1fff unmapped + if((addr & 0x1c00) == 0x1c00) { + return; + } + } +} diff --git a/higan/pce/cpu/irq.cpp b/higan/pce/cpu/irq.cpp new file mode 100644 index 00000000..04feead9 --- /dev/null +++ b/higan/pce/cpu/irq.cpp @@ -0,0 +1,37 @@ +auto CPU::IRQ::pending() const -> bool { + return pendingIRQ; +} + +auto CPU::IRQ::vector() const -> uint16 { + return pendingVector; +} + +auto CPU::IRQ::poll() -> void { + pendingIRQ = false; + if(cpu.r.p.i) return; + + if(!disableExternal && pendingExternal) { + pendingIRQ = true; + pendingVector = 0xfff6; + } else if(!disableVDC && pendingVDC) { + pendingIRQ = true; + pendingVector = 0xfff8; + } else if(!disableTimer && pendingTimer) { + pendingIRQ = true; + pendingVector = 0xfffa; + } +} + +auto CPU::IRQ::level(Line line, bool level) -> void { + if(line == Line::External) { + pendingExternal = level; + } + + if(line == Line::VDC) { + pendingVDC = level; + } + + if(line == Line::Timer) { + pendingTimer = level; + } +} diff --git a/higan/pce/cpu/timer.cpp b/higan/pce/cpu/timer.cpp new file mode 100644 index 00000000..5e0ce56f --- /dev/null +++ b/higan/pce/cpu/timer.cpp @@ -0,0 +1,16 @@ +auto CPU::Timer::start() -> void { + value = latch; + clock = 0; +} + +auto CPU::Timer::step(uint clocks) -> void { + if(!enable) return; + clock += clocks; + while(clock >= 1024) { + clock -= 1024; + if(!value--) { + value = latch; + cpu.irq.level(CPU::IRQ::Line::Timer, 1); + } + } +} diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index f1008503..fff0fcfc 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -65,7 +65,15 @@ auto Interface::videoColors() -> uint32 { } auto Interface::videoColor(uint32 color) -> uint64 { - return 0; + uint3 B = color.bits(0,2); + uint3 G = color.bits(3,5); + uint3 R = color.bits(6,8); + + uint64 r = image::normalize(R, 3, 16); + uint64 g = image::normalize(G, 3, 16); + uint64 b = image::normalize(B, 3, 16); + + return r << 32 | g << 16 | b << 0; } auto Interface::audioFrequency() -> double { diff --git a/higan/pce/vdc/dma.cpp b/higan/pce/vdc/dma.cpp new file mode 100644 index 00000000..93cef630 --- /dev/null +++ b/higan/pce/vdc/dma.cpp @@ -0,0 +1,38 @@ +auto VDC::DMA::step(uint clocks) -> void { + while(clocks--) { + if(vramActive) { + uint16 data = vdc.vramRead(source); + vdc.vramWrite(target, data); + sourceIncrementMode == 0 ? source++ : source--; + targetIncrementMode == 0 ? target++ : target--; + if(!--length) { + vramActive = false; + vdc.irq.raise(VDC::IRQ::Line::TransferVRAM); + } + } + + if(satbActive) { + uint16 data = vdc.vramRead(satbSource + satbTarget); + vdc.vramWrite(satbTarget++, data); + if(satbTarget == 256) { + satbActive = false; + satbPending = satbRepeat; + vdc.irq.raise(VDC::IRQ::Line::TransferSATB); + } + } + } +} + +auto VDC::DMA::vramStart() -> void { + vramActive = true; +} + +auto VDC::DMA::satbStart() -> void { + if(!satbPending) return; + satbActive = true; + satbTarget = 0; +} + +auto VDC::DMA::satbQueue() -> void { + satbPending = true; +} diff --git a/higan/pce/vdc/io.cpp b/higan/pce/vdc/io.cpp new file mode 100644 index 00000000..ce6b5dbb --- /dev/null +++ b/higan/pce/vdc/io.cpp @@ -0,0 +1,263 @@ +auto VDC::vramRead(uint16 addr) -> uint16 { + if(addr.bit(15)) return 0x00; + return vram[addr]; +} + +auto VDC::vramWrite(uint16 addr, uint16 data) -> void { + if(addr.bit(15)) return; + vram[addr] = data; +} + +auto VDC::read(uint11 addr) -> uint8 { + bool a0 = addr.bit(0); + if(!addr.bit(10)) { + //VDC + if(addr.bit(1) == 0) { + //SR + if(a0) return 0x00; + uint8 data; + data.bit(0) = irq.pendingCollision; + data.bit(1) = irq.pendingOverflow; + data.bit(2) = irq.pendingLineCoincidence; + data.bit(3) = irq.pendingTransferSATB; + data.bit(4) = irq.pendingTransferVRAM; + data.bit(5) = irq.pendingVblank; + irq.lower(); + return data; + } + + if(addr.bit(1) == 1) { + if(io.address == 0x02) { + //VRR + uint8 data = io.vramDataRead.byte(a0); + if(a0) { + io.vramAddressRead += io.vramAddressIncrement; + io.vramDataRead = vramRead(io.vramAddressRead); + } + return data; + } + } + } else { + //VCE + if(addr.bits(0,2) == 0x04) { + //CTR + uint8 data = cram[io.colorAddress].bits(0,7); + return data; + } + + if(addr.bits(0,2) == 0x05) { + //CTR + uint8 data = cram[io.colorAddress].bit(0); + io.colorAddress++; + return data; + } + } + + return 0x00; +} + +auto VDC::write(uint11 addr, uint8 data) -> void { + bool a0 = addr.bit(0); + if(!addr.bit(10)) { + //VDC + if(addr.bit(1) == 0) { + //AR + if(a0) return; + io.address = data.bits(0,4); + return; + } + + if(addr.bit(1) == 1) { + if(io.address == 0x00) { + //MAWR + io.vramAddressWrite.byte(a0) = data; + return; + } + + if(io.address == 0x01) { + //MARR + io.vramAddressRead.byte(a0) = data; + io.vramDataRead = vramRead(io.vramAddressRead); + return; + } + + if(io.address == 0x02) { + //VWR + io.vramDataWrite.byte(a0) = data; + if(a0) { + vramWrite(io.vramAddressWrite, io.vramDataWrite); + io.vramAddressWrite += io.vramAddressIncrement; + } + return; + } + + if(io.address == 0x05) { + //CR + if(!a0) { + irq.enableCollision = data.bit(0); + irq.enableOverflow = data.bit(1); + irq.enableLineCoincidence = data.bit(2); + irq.enableVblank = data.bit(3); + io.externalSync = data.bits(4,5); + io.spriteBlank = data.bit(6); + io.backgroundBlank = data.bit(7); + } else { + io.displayOutput = data.bits(0,1); + io.dramRefresh = data.bit(2); + if(data.bits(3,4) == 0) io.vramAddressIncrement = 0x01; + if(data.bits(3,4) == 1) io.vramAddressIncrement = 0x20; + if(data.bits(3,4) == 2) io.vramAddressIncrement = 0x40; + if(data.bits(3,4) == 3) io.vramAddressIncrement = 0x80; + } + return; + } + + if(io.address == 0x06) { + //RCR + io.lineCoincidence.byte(a0) = data; + return; + } + + if(io.address == 0x07) { + //BXR + io.backgroundHscroll.byte(a0) = data; + return; + } + + if(io.address == 0x08) { + //BYR + io.backgroundVscroll.byte(a0) = data; + return; + } + + if(io.address == 0x09) { + //MWR + if(a0) return; + io.vramAccess = data.bits(0,1); + io.spriteAccess = data.bits(2,3); + if(data.bits(4,5) == 0) io.backgroundWidth = 32; + if(data.bits(4,5) == 1) io.backgroundWidth = 64; + if(data.bits(4,5) == 2) io.backgroundWidth = 128; + if(data.bits(4,5) == 3) io.backgroundWidth = 128; + if(data.bit(6) == 0) io.backgroundHeight = 32; + if(data.bit(6) == 1) io.backgroundHeight = 64; + io.cgMode = data.bit(7); + return; + } + + if(io.address == 0x0a) { + //HSR + if(!a0) { + io.horizontalSyncWidth = data.bits(0,4); + } else { + io.horizontalDisplayStart = data.bits(0,6); + } + return; + } + + if(io.address == 0x0b) { + //HDR + if(!a0) { + io.horizontalDisplayWidth = data.bits(0,6); + } else { + io.horizontalDisplayEnd = data.bits(0,6); + } + return; + } + + if(io.address == 0x0c) { + //VPR + if(!a0) { + io.verticalSyncWidth = data.bits(0,4); + } else { + io.verticalDisplayStart = data.bits(0,7); + } + return; + } + + if(io.address == 0x0d) { + //VDR + io.verticalDisplayWidth.byte(a0) = data; + return; + } + + if(io.address == 0x0e) { + //VCR + if(a0) return; + io.verticalDisplayEnd = data.bits(0,7); + return; + } + + if(io.address == 0x0f) { + //DCR + if(a0) return; + irq.enableTransferVRAM = data.bit(0); + irq.enableTransferSATB = data.bit(1); + dma.sourceIncrementMode = data.bit(2); + dma.targetIncrementMode = data.bit(3); + dma.satbRepeat = data.bit(4); + return; + } + + if(io.address == 0x10) { + //SOUR + dma.source.byte(a0) = data; + return; + } + + if(io.address == 0x11) { + //DESR + dma.target.byte(a0) = data; + return; + } + + if(io.address == 0x12) { + //LENR + dma.length.byte(a0) = data; + if(a0) dma.vramStart(); + return; + } + + if(io.address == 0x13) { + //DVSSR + dma.satbSource.byte(a0) = data; + if(a0) dma.satbQueue(); + return; + } + } + } else { + //VCE + if(addr.bits(0,2) == 0x00) { + //CR + io.divisionRatio = data.bits(0,1); + io.colorBlur = data.bit(2); + io.grayscale = data.bit(7); + return; + } + + if(addr.bits(0,2) == 0x02) { + //CTA + io.colorAddress.bits(0,7) = data.bits(0,7); + return; + } + + if(addr.bits(0,2) == 0x03) { + //CTA + io.colorAddress.bit(8) = data.bit(0); + return; + } + + if(addr.bits(0,2) == 0x04) { + //CTW + cram[io.colorAddress].bits(0,7) = data.bits(0,7); + return; + } + + if(addr.bits(0,2) == 0x05) { + //CTW + cram[io.colorAddress].bit(8) = data.bit(0); + io.colorAddress++; + return; + } + } +} diff --git a/higan/pce/vdc/irq.cpp b/higan/pce/vdc/irq.cpp new file mode 100644 index 00000000..6f0c2cca --- /dev/null +++ b/higan/pce/vdc/irq.cpp @@ -0,0 +1,49 @@ +auto VDC::IRQ::poll() -> void { + bool pending = false; + pending |= pendingCollision; + pending |= pendingOverflow; + pending |= pendingLineCoincidence; + pending |= pendingVblank; + pending |= pendingTransferVRAM; + pending |= pendingTransferSATB; + cpu.irq.level(CPU::IRQ::Line::VDC, pending); +} + +auto VDC::IRQ::raise(Line line) -> void { + if(line == Line::Collision && enableCollision) { + pendingCollision = true; + } + + if(line == Line::Overflow && enableOverflow) { + pendingOverflow = true; + } + + if(line == Line::LineCoincidence && enableLineCoincidence) { + pendingLineCoincidence = true; + } + + if(line == Line::Vblank && enableVblank) { + pendingVblank = true; + } + + if(line == Line::TransferVRAM && enableTransferVRAM) { + pendingTransferVRAM = true; + } + + if(line == Line::TransferSATB && enableTransferSATB) { + pendingTransferSATB = true; + } + + poll(); +} + +auto VDC::IRQ::lower() -> void { + pendingCollision = false; + pendingOverflow = false; + pendingLineCoincidence = false; + pendingVblank = false; + pendingTransferVRAM = false; + pendingTransferSATB = false; + + poll(); +} diff --git a/higan/pce/vdc/vdc.cpp b/higan/pce/vdc/vdc.cpp index af155642..8c341872 100644 --- a/higan/pce/vdc/vdc.cpp +++ b/higan/pce/vdc/vdc.cpp @@ -3,18 +3,40 @@ namespace PCEngine { VDC vdc; +#include "io.cpp" +#include "irq.cpp" +#include "dma.cpp" auto VDC::Enter() -> void { while(true) scheduler.synchronize(), vdc.main(); } auto VDC::main() -> void { - step(system.colorburst() * 6.0 / 60.0); - scheduler.exit(Scheduler::Event::Frame); + //1365 cycles/scanline + for(uint x : range(256)) { + step(4); + } + step(341); + scanline(); +} + +auto VDC::scanline() -> void { + state.x = 0; + if(++state.y == 262) { + state.y = 0; + } + if(state.y == (io.lineCoincidence - 64)) irq.raise(IRQ::Line::LineCoincidence); + if(state.y == 240) { + irq.raise(IRQ::Line::Vblank); + dma.satbStart(); + scheduler.exit(Scheduler::Event::Frame); + } } auto VDC::step(uint clocks) -> void { + state.x += clocks; Thread::step(clocks); + dma.step(clocks); synchronize(cpu); } @@ -24,7 +46,15 @@ auto VDC::refresh() -> void { auto VDC::power() -> void { create(VDC::Enter, system.colorburst() * 6.0); + for(auto& pixel : buffer) pixel = 0; + for(auto& word : vram) word = 0x0000; + for(auto& word : satb) word = 0x0000; + for(auto& word : cram) word = 0x0000; + memory::fill(&state, sizeof(State)); + memory::fill(&irq, sizeof(IRQ)); + memory::fill(&dma, sizeof(DMA)); + memory::fill(&io, sizeof(IO)); } } diff --git a/higan/pce/vdc/vdc.hpp b/higan/pce/vdc/vdc.hpp index f6df4a58..8d699e7e 100644 --- a/higan/pce/vdc/vdc.hpp +++ b/higan/pce/vdc/vdc.hpp @@ -4,13 +4,150 @@ struct VDC : Thread { static auto Enter() -> void; auto main() -> void; + auto scanline() -> void; auto step(uint clocks) -> void; auto refresh() -> void; auto power() -> void; + //io.cpp + auto vramRead(uint16 addr) -> uint16; + auto vramWrite(uint16 addr, uint16 data) -> void; + + auto read(uint11 addr) -> uint8; + auto write(uint11 addr, uint8 data) -> void; + private: uint32 buffer[512 * 484]; + + uint16 vram[0x8000]; + uint16 satb[0x100]; + uint9 cram[0x200]; + + struct State { + uint x; + uint y; + } state; + + struct IRQ { + enum class Line : uint { + Collision, + Overflow, + LineCoincidence, + Vblank, + TransferVRAM, + TransferSATB, + }; + + //irq.cpp + auto poll() -> void; + auto raise(Line) -> void; + auto lower() -> void; + + bool enableCollision; + bool enableOverflow; + bool enableLineCoincidence; + bool enableVblank; + bool enableTransferVRAM; + bool enableTransferSATB; + + bool pendingCollision; + bool pendingOverflow; + bool pendingLineCoincidence; + bool pendingVblank; + bool pendingTransferVRAM; + bool pendingTransferSATB; + } irq; + + struct DMA { + //dma.cpp + auto step(uint clocks) -> void; + auto vramStart() -> void; + auto satbStart() -> void; + auto satbQueue() -> void; + + bool sourceIncrementMode; + bool targetIncrementMode; + bool satbRepeat; + uint16 source; + uint16 target; + uint16 length; + uint16 satbSource; + + bool vramActive; + bool satbActive; + bool satbPending; + uint16 satbTarget; + } dma; + + struct IO { + uint5 address; + + //VDC + + //$00 MAWR (W) + uint16 vramAddressWrite; + + //$01 MARR (W) + uint16 vramAddressRead; + + //$02 VWR (W) + //$02 VRR (R) + uint16 vramDataWrite; + uint16 vramDataRead; + + //$05 CR (W) + uint2 externalSync; + bool spriteBlank; + bool backgroundBlank; + uint2 displayOutput; + bool dramRefresh; + uint vramAddressIncrement; + + //$06 RCR + uint10 lineCoincidence; + + //$07 BXR + uint10 backgroundHscroll; + + //$08 BYR + uint9 backgroundVscroll; + + //$09 MWR + uint2 vramAccess; + uint2 spriteAccess; + uint backgroundWidth; + uint backgroundHeight; + bool cgMode; + + //$0a HSR + uint5 horizontalSyncWidth; + uint7 horizontalDisplayStart; + + //$0b HDR + uint7 horizontalDisplayWidth; + uint7 horizontalDisplayEnd; + + //$0c VPR + uint5 verticalSyncWidth; + uint8 verticalDisplayStart; + + //$0d VDR + uint9 verticalDisplayWidth; + + //$0e VCR + uint8 verticalDisplayEnd; + + //VCE + + //$00 CR + uint2 divisionRatio; + bool colorBlur; + bool grayscale; + + //$02 CTA + uint9 colorAddress; + } io; }; extern VDC vdc; diff --git a/higan/processor/huc6280/huc6280.cpp b/higan/processor/huc6280/huc6280.cpp index d93a5506..5f7c84ca 100644 --- a/higan/processor/huc6280/huc6280.cpp +++ b/higan/processor/huc6280/huc6280.cpp @@ -62,7 +62,7 @@ auto HuC6280::power() -> void { r.mpr[7] = 0x00; r.mdr = 0x00; r.p = 0x04; - r.cs = 3; + r.cs = 1; } } diff --git a/higan/processor/huc6280/huc6280.hpp b/higan/processor/huc6280/huc6280.hpp index 706e6f1c..d6abdeff 100644 --- a/higan/processor/huc6280/huc6280.hpp +++ b/higan/processor/huc6280/huc6280.hpp @@ -8,7 +8,6 @@ struct HuC6280 { virtual auto step(uint clocks) -> void = 0; virtual auto read(uint21 addr) -> uint8 = 0; virtual auto write(uint21 addr, uint8 data) -> void = 0; - virtual auto st(uint2, uint8) -> void = 0; virtual auto lastCycle() -> void = 0; auto power() -> void; @@ -26,6 +25,7 @@ struct HuC6280 { auto pull() -> uint8; //instruction.cpp + auto interrupt(uint16 vector) -> void; auto instruction() -> void; //instructions.cpp diff --git a/higan/processor/huc6280/instruction.cpp b/higan/processor/huc6280/instruction.cpp index cc81fffb..07d155a7 100644 --- a/higan/processor/huc6280/instruction.cpp +++ b/higan/processor/huc6280/instruction.cpp @@ -1,6 +1,18 @@ #define op(id, name, ...) case id: instruction_##name(__VA_ARGS__); return; #define fp(name) &HuC6280::name +auto HuC6280::interrupt(uint16 vector) -> void { + io(); + io(); + push(PC >> 8); + push(PC >> 0); + push(P); + PC.byte(0) = load(vector + 0); + PC.byte(1) = load(vector + 1); + D = 0; + I = 1; +} + auto HuC6280::instruction() -> void { auto code = opcode(); @@ -35,7 +47,7 @@ U op(0x0b, NOP) op(0x10, branch, N == 0) op(0x11, indirectYLoad, fp(ORA), A) op(0x12, indirectLoad, fp(ORA), A) - op(0x13, ST, 1) + op(0x13, ST, 2) op(0x14, zeropageModify, fp(TRB)) op(0x15, zeropageLoad, fp(ORA), A, X) op(0x16, zeropageModify, fp(ASL), X) @@ -51,7 +63,7 @@ U op(0x1b, NOP) op(0x20, JSR) op(0x21, indirectLoad, fp(AND), A, X) op(0x22, swap, A, X) - op(0x23, ST, 2) + op(0x23, ST, 3) op(0x24, zeropageLoad, fp(BIT), A) op(0x25, zeropageLoad, fp(AND), A) op(0x26, zeropageModify, fp(ROL)) diff --git a/higan/processor/huc6280/instructions.cpp b/higan/processor/huc6280/instructions.cpp index ea436da7..b3dea4f6 100644 --- a/higan/processor/huc6280/instructions.cpp +++ b/higan/processor/huc6280/instructions.cpp @@ -375,12 +375,12 @@ L push((PC - 1) >> 0); } auto HuC6280::instruction_CSL() -> void { - r.cs = 12; + r.cs = 4; L io(); } auto HuC6280::instruction_CSH() -> void { - r.cs = 3; + r.cs = 1; L io(); } @@ -457,7 +457,7 @@ auto HuC6280::instruction_ST(uint2 index) -> void { auto data = operand(); io(); L io(); - st(index, data); + write(0x1fe000 + index, data); } auto HuC6280::instruction_TAM() -> void {