mirror of https://github.com/bsnes-emu/bsnes.git
Update to v101r34 release.
byuu says: Changelog: - PCE: emulated gamepad polling - PCE: emulated CPU interrupt sources - PCE: emulated timer - PCE: smarter emulation of ST0,ST1,ST2 instructions - PCE: better structuring of CPU, VDP IO registers - PCE: connected palette generation to the interface - PCE: emulated basic VDC timing - PCE: emulated VDC Vblank, Coincidence, and DMA completion IRQs - PCE: emulated VRAM, SATB DMA transfers - PCE: emulated VDC I/O registers Everything I've implemented today likely has lots of bugs, and is untested for obvious reasons. So basically, after I fix many horrendous bugs, it should now be possible to implement the VDC and start getting graphical output.
This commit is contained in:
parent
8499c64756
commit
f500426158
|
@ -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/";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Thread*> 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];
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
//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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -62,7 +62,7 @@ auto HuC6280::power() -> void {
|
|||
r.mpr[7] = 0x00;
|
||||
r.mdr = 0x00;
|
||||
r.p = 0x04;
|
||||
r.cs = 3;
|
||||
r.cs = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue