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 {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -5,7 +5,8 @@ struct Controller : Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> 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"
|
#include "gamepad/gamepad.hpp"
|
||||||
|
|
|
@ -1,6 +1,29 @@
|
||||||
Gamepad::Gamepad() {
|
Gamepad::Gamepad() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Gamepad::readData() -> uint8 {
|
auto Gamepad::readData() -> uint4 {
|
||||||
return 0x00;
|
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();
|
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 {
|
namespace PCEngine {
|
||||||
|
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
|
#include "io.cpp"
|
||||||
|
#include "irq.cpp"
|
||||||
|
#include "timer.cpp"
|
||||||
|
|
||||||
auto CPU::Enter() -> void {
|
auto CPU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), cpu.main();
|
while(true) scheduler.synchronize(), cpu.main();
|
||||||
|
@ -16,11 +19,13 @@ auto CPU::main() -> void {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(irq.pending()) interrupt(irq.vector());
|
||||||
instruction();
|
instruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::step(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
Thread::step(clocks);
|
Thread::step(clocks);
|
||||||
|
timer.step(clocks);
|
||||||
synchronize(vdc);
|
synchronize(vdc);
|
||||||
synchronize(psg);
|
synchronize(psg);
|
||||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||||
|
@ -28,33 +33,17 @@ auto CPU::step(uint clocks) -> void {
|
||||||
|
|
||||||
auto CPU::power() -> void {
|
auto CPU::power() -> void {
|
||||||
HuC6280::power();
|
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(0) = read(0x1ffe);
|
||||||
r.pc.byte(1) = read(0x1fff);
|
r.pc.byte(1) = read(0x1fff);
|
||||||
}
|
|
||||||
|
|
||||||
auto CPU::read(uint21 addr) -> uint8 {
|
memory::fill(&irq, sizeof(IRQ));
|
||||||
if(!addr.bit(20)) return cartridge.read(addr);
|
memory::fill(&timer, sizeof(Timer));
|
||||||
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 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::lastCycle() -> void {
|
auto CPU::lastCycle() -> void {
|
||||||
|
irq.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,55 @@ struct CPU : Processor::HuC6280, Thread {
|
||||||
auto step(uint clocks) -> void override;
|
auto step(uint clocks) -> void override;
|
||||||
|
|
||||||
auto power() -> void;
|
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;
|
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;
|
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:
|
private:
|
||||||
uint8 ram[0x2000];
|
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 {
|
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 {
|
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 {
|
namespace PCEngine {
|
||||||
|
|
||||||
VDC vdc;
|
VDC vdc;
|
||||||
|
#include "io.cpp"
|
||||||
|
#include "irq.cpp"
|
||||||
|
#include "dma.cpp"
|
||||||
|
|
||||||
auto VDC::Enter() -> void {
|
auto VDC::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), vdc.main();
|
while(true) scheduler.synchronize(), vdc.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::main() -> void {
|
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);
|
scheduler.exit(Scheduler::Event::Frame);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto VDC::step(uint clocks) -> void {
|
auto VDC::step(uint clocks) -> void {
|
||||||
|
state.x += clocks;
|
||||||
Thread::step(clocks);
|
Thread::step(clocks);
|
||||||
|
dma.step(clocks);
|
||||||
synchronize(cpu);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +46,15 @@ auto VDC::refresh() -> void {
|
||||||
|
|
||||||
auto VDC::power() -> void {
|
auto VDC::power() -> void {
|
||||||
create(VDC::Enter, system.colorburst() * 6.0);
|
create(VDC::Enter, system.colorburst() * 6.0);
|
||||||
|
|
||||||
for(auto& pixel : buffer) pixel = 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 {
|
struct VDC : Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto scanline() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto refresh() -> void;
|
auto refresh() -> void;
|
||||||
|
|
||||||
auto power() -> 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:
|
private:
|
||||||
uint32 buffer[512 * 484];
|
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;
|
extern VDC vdc;
|
||||||
|
|
|
@ -62,7 +62,7 @@ auto HuC6280::power() -> void {
|
||||||
r.mpr[7] = 0x00;
|
r.mpr[7] = 0x00;
|
||||||
r.mdr = 0x00;
|
r.mdr = 0x00;
|
||||||
r.p = 0x04;
|
r.p = 0x04;
|
||||||
r.cs = 3;
|
r.cs = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ struct HuC6280 {
|
||||||
virtual auto step(uint clocks) -> void = 0;
|
virtual auto step(uint clocks) -> void = 0;
|
||||||
virtual auto read(uint21 addr) -> uint8 = 0;
|
virtual auto read(uint21 addr) -> uint8 = 0;
|
||||||
virtual auto write(uint21 addr, uint8 data) -> void = 0;
|
virtual auto write(uint21 addr, uint8 data) -> void = 0;
|
||||||
virtual auto st(uint2, uint8) -> void = 0;
|
|
||||||
virtual auto lastCycle() -> void = 0;
|
virtual auto lastCycle() -> void = 0;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
@ -26,6 +25,7 @@ struct HuC6280 {
|
||||||
auto pull() -> uint8;
|
auto pull() -> uint8;
|
||||||
|
|
||||||
//instruction.cpp
|
//instruction.cpp
|
||||||
|
auto interrupt(uint16 vector) -> void;
|
||||||
auto instruction() -> void;
|
auto instruction() -> void;
|
||||||
|
|
||||||
//instructions.cpp
|
//instructions.cpp
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
#define op(id, name, ...) case id: instruction_##name(__VA_ARGS__); return;
|
#define op(id, name, ...) case id: instruction_##name(__VA_ARGS__); return;
|
||||||
#define fp(name) &HuC6280::name
|
#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 HuC6280::instruction() -> void {
|
||||||
auto code = opcode();
|
auto code = opcode();
|
||||||
|
|
||||||
|
@ -35,7 +47,7 @@ U op(0x0b, NOP)
|
||||||
op(0x10, branch, N == 0)
|
op(0x10, branch, N == 0)
|
||||||
op(0x11, indirectYLoad, fp(ORA), A)
|
op(0x11, indirectYLoad, fp(ORA), A)
|
||||||
op(0x12, indirectLoad, fp(ORA), A)
|
op(0x12, indirectLoad, fp(ORA), A)
|
||||||
op(0x13, ST, 1)
|
op(0x13, ST, 2)
|
||||||
op(0x14, zeropageModify, fp(TRB))
|
op(0x14, zeropageModify, fp(TRB))
|
||||||
op(0x15, zeropageLoad, fp(ORA), A, X)
|
op(0x15, zeropageLoad, fp(ORA), A, X)
|
||||||
op(0x16, zeropageModify, fp(ASL), X)
|
op(0x16, zeropageModify, fp(ASL), X)
|
||||||
|
@ -51,7 +63,7 @@ U op(0x1b, NOP)
|
||||||
op(0x20, JSR)
|
op(0x20, JSR)
|
||||||
op(0x21, indirectLoad, fp(AND), A, X)
|
op(0x21, indirectLoad, fp(AND), A, X)
|
||||||
op(0x22, swap, A, X)
|
op(0x22, swap, A, X)
|
||||||
op(0x23, ST, 2)
|
op(0x23, ST, 3)
|
||||||
op(0x24, zeropageLoad, fp(BIT), A)
|
op(0x24, zeropageLoad, fp(BIT), A)
|
||||||
op(0x25, zeropageLoad, fp(AND), A)
|
op(0x25, zeropageLoad, fp(AND), A)
|
||||||
op(0x26, zeropageModify, fp(ROL))
|
op(0x26, zeropageModify, fp(ROL))
|
||||||
|
|
|
@ -375,12 +375,12 @@ L push((PC - 1) >> 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::instruction_CSL() -> void {
|
auto HuC6280::instruction_CSL() -> void {
|
||||||
r.cs = 12;
|
r.cs = 4;
|
||||||
L io();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::instruction_CSH() -> void {
|
auto HuC6280::instruction_CSH() -> void {
|
||||||
r.cs = 3;
|
r.cs = 1;
|
||||||
L io();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +457,7 @@ auto HuC6280::instruction_ST(uint2 index) -> void {
|
||||||
auto data = operand();
|
auto data = operand();
|
||||||
io();
|
io();
|
||||||
L io();
|
L io();
|
||||||
st(index, data);
|
write(0x1fe000 + index, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::instruction_TAM() -> void {
|
auto HuC6280::instruction_TAM() -> void {
|
||||||
|
|
Loading…
Reference in New Issue