Update to v099r04 release.

byuu says:

Changelog:
- lots of code cleanups to processor/r6502 (the switch.cpp file is only
  halfway done ...)
- lots of code cleanups to fc/cpu
- removed fc/input
- implemented fc/controller

hex_usr, you may not like this, but I want to keep the controller port
and expansion port interface separate, like I do with the SNES. I realize
the NES' is used more for controllers, and the SNES' more for hardware
expansions, but ... they're not compatible pinouts and you can't really
connect one to the other.

Right now, I've only implemented the controller portion. I'll have to
get to the peripheral portion later.

Also, the gamepad implementation there now may be wrong. It's based off
the Super Famicom version obviously. I'm not sure if the Famicom has
different behavior with latching $4016 writes, or not. But, it works in
Mega Man II, so it's a start.

Everyone, be sure to remap your controls, and then set port 1 -> gamepad
after loading your first Famicom game with the new WIP.
This commit is contained in:
Tim Allen 2016-06-18 16:04:32 +10:00
parent 44a8c5a2b4
commit 40abcfc4a5
46 changed files with 1269 additions and 1122 deletions

View File

@ -9,7 +9,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "099.03";
static const string Version = "099.04";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -1,13 +1,13 @@
processors += r6502
objects += fc-interface fc-system fc-scheduler fc-input
objects += fc-interface fc-system fc-scheduler fc-controller
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
objects += fc-cheat
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
obj/fc-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
obj/fc-scheduler.o: fc/scheduler/scheduler.cpp $(call rwildcard,fc/scheduler/)
obj/fc-input.o: fc/input/input.cpp $(call rwildcard,fc/input/)
obj/fc-controller.o: fc/controller/controller.cpp $(call rwildcard,fc/controller/)
obj/fc-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)

View File

@ -68,7 +68,7 @@ auto APU::tick() -> void {
}
auto APU::set_irq_line() -> void {
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
cpu.apuLine(frame.irq_pending || dmc.irq_pending);
}
auto APU::set_sample(int16 sample) -> void {
@ -109,8 +109,10 @@ auto APU::reset() -> void {
set_irq_line();
}
auto APU::read(uint16 addr) -> uint8 {
if(addr == 0x4015) {
auto APU::readIO(uint16 addr) -> uint8 {
switch(addr) {
case 0x4015: {
uint8 result = 0x00;
result |= pulse[0].length_counter ? 0x01 : 0;
result |= pulse[1].length_counter ? 0x02 : 0;
@ -126,34 +128,40 @@ auto APU::read(uint16 addr) -> uint8 {
return result;
}
}
return cpu.mdr();
}
auto APU::write(uint16 addr, uint8 data) -> void {
auto APU::writeIO(uint16 addr, uint8 data) -> void {
const uint n = (addr >> 2) & 1; //pulse#
switch(addr) {
case 0x4000: case 0x4004:
case 0x4000: case 0x4004: {
pulse[n].duty = data >> 6;
pulse[n].envelope.loop_mode = data & 0x20;
pulse[n].envelope.use_speed_as_volume = data & 0x10;
pulse[n].envelope.speed = data & 0x0f;
break;
return;
}
case 0x4001: case 0x4005:
case 0x4001: case 0x4005: {
pulse[n].sweep.enable = data & 0x80;
pulse[n].sweep.period = (data & 0x70) >> 4;
pulse[n].sweep.decrement = data & 0x08;
pulse[n].sweep.shift = data & 0x07;
pulse[n].sweep.reload = true;
break;
return;
}
case 0x4002: case 0x4006:
case 0x4002: case 0x4006: {
pulse[n].period = (pulse[n].period & 0x0700) | (data << 0);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x0700) | (data << 0);
break;
return;
}
case 0x4003: case 0x4007:
case 0x4003: case 0x4007: {
pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x00ff) | (data << 8);
@ -163,18 +171,21 @@ auto APU::write(uint16 addr, uint8 data) -> void {
if(enabled_channels & (1 << n)) {
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
return;
}
case 0x4008:
case 0x4008: {
triangle.halt_length_counter = data & 0x80;
triangle.linear_length = data & 0x7f;
break;
return;
}
case 0x400a:
case 0x400a: {
triangle.period = (triangle.period & 0x0700) | (data << 0);
break;
return;
}
case 0x400b:
case 0x400b: {
triangle.period = (triangle.period & 0x00ff) | (data << 8);
triangle.reload_linear = true;
@ -182,49 +193,57 @@ auto APU::write(uint16 addr, uint8 data) -> void {
if(enabled_channels & (1 << 2)) {
triangle.length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
return;
}
case 0x400c:
case 0x400c: {
noise.envelope.loop_mode = data & 0x20;
noise.envelope.use_speed_as_volume = data & 0x10;
noise.envelope.speed = data & 0x0f;
break;
return;
}
case 0x400e:
case 0x400e: {
noise.short_mode = data & 0x80;
noise.period = data & 0x0f;
break;
return;
}
case 0x400f:
case 0x400f: {
noise.envelope.reload_decay = true;
if(enabled_channels & (1 << 3)) {
noise.length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
return;
}
case 0x4010:
case 0x4010: {
dmc.irq_enable = data & 0x80;
dmc.loop_mode = data & 0x40;
dmc.period = data & 0x0f;
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
set_irq_line();
break;
return;
}
case 0x4011:
case 0x4011: {
dmc.dac_latch = data & 0x7f;
break;
return;
}
case 0x4012:
case 0x4012: {
dmc.addr_latch = data;
break;
return;
}
case 0x4013:
case 0x4013: {
dmc.length_latch = data;
break;
return;
}
case 0x4015:
case 0x4015: {
if((data & 0x01) == 0) pulse[0].length_counter = 0;
if((data & 0x02) == 0) pulse[1].length_counter = 0;
if((data & 0x04) == 0) triangle.length_counter = 0;
@ -235,9 +254,10 @@ auto APU::write(uint16 addr, uint8 data) -> void {
set_irq_line();
enabled_channels = data & 0x1f;
break;
return;
}
case 0x4017:
case 0x4017: {
frame.mode = data >> 6;
frame.counter = 0;
@ -247,7 +267,9 @@ auto APU::write(uint16 addr, uint8 data) -> void {
set_irq_line();
}
frame.divider = FrameCounter::NtscPeriod;
break;
return;
}
}
}

View File

@ -12,8 +12,8 @@ struct APU : Thread {
auto power() -> void;
auto reset() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto readIO(uint16 addr) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void;
auto serialize(serializer&) -> void;

View File

@ -8,8 +8,8 @@ auto APU::DMC::start() -> void {
auto APU::DMC::stop() -> void {
length_counter = 0;
dma_delay_counter = 0;
cpu.set_rdy_line(1);
cpu.set_rdy_addr(false);
cpu.rdyLine(1);
cpu.rdyAddr(false);
}
auto APU::DMC::clock() -> uint8 {
@ -19,10 +19,10 @@ auto APU::DMC::clock() -> uint8 {
dma_delay_counter--;
if(dma_delay_counter == 1) {
cpu.set_rdy_addr(true, 0x8000 | read_addr);
cpu.rdyAddr(true, 0x8000 | read_addr);
} else if(dma_delay_counter == 0) {
cpu.set_rdy_line(1);
cpu.set_rdy_addr(false);
cpu.rdyLine(1);
cpu.rdyAddr(false);
dma_buffer = cpu.mdr();
have_dma_buffer = true;
@ -61,7 +61,7 @@ auto APU::DMC::clock() -> uint8 {
}
if(length_counter > 0 && have_dma_buffer == false && dma_delay_counter == 0) {
cpu.set_rdy_line(0);
cpu.rdyLine(0);
dma_delay_counter = 4;
}

View File

@ -7,7 +7,7 @@ struct BandaiFCG : Board {
auto main() -> void {
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
cpu.irqLine(1);
irq_counter_enable = false;
}
}
@ -47,7 +47,7 @@ struct BandaiFCG : Board {
mirror = data & 0x03;
break;
case 0x0a:
cpu.set_irq_line(0);
cpu.irqLine(0);
irq_counter_enable = data & 0x01;
irq_counter = irq_latch;
break;
@ -58,7 +58,7 @@ struct BandaiFCG : Board {
irq_latch = (irq_latch & 0x00ff) | (data << 8);
break;
case 0x0d:
//TODO: serial EEPROM support
//todo: serial EEPROM support
break;
}
}

View File

@ -46,7 +46,7 @@ struct Sunsoft5B : Board {
auto main() -> void {
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(irq_enable);
cpu.irqLine(irq_enable);
}
}
@ -108,7 +108,7 @@ struct Sunsoft5B : Board {
case 13:
irq_enable = data & 0x80;
irq_counter_enable = data & 0x01;
if(irq_enable == 0) cpu.set_irq_line(0);
if(irq_enable == 0) cpu.irqLine(0);
break;
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;

View File

@ -4,7 +4,7 @@ struct MMC3 : Chip {
auto main() -> void {
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
tick();
}

View File

@ -7,7 +7,7 @@ struct MMC5 : Chip {
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
cpu.set_irq_line(irq_enable && irq_pending);
cpu.irqLine(irq_enable && irq_pending);
tick();
}

View File

@ -4,7 +4,7 @@ struct MMC6 : Chip {
auto main() -> void {
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
tick();
}

View File

@ -20,7 +20,7 @@ struct VRC3 : Chip {
}
}
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
tick();
}

View File

@ -27,7 +27,7 @@ struct VRC4 : Chip {
}
}
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
tick();
}

View File

@ -100,7 +100,7 @@ struct VRC6 : Chip {
}
}
}
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
pulse1.clock();
pulse2.clock();

View File

@ -29,7 +29,7 @@ struct VRC7 : Chip {
}
}
}
cpu.set_irq_line(irq_line);
cpu.irqLine(irq_line);
tick();
}

View File

@ -0,0 +1,26 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "gamepad/gamepad.cpp"
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
Controller::~Controller() {
}
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
}
}
auto Controller::main() -> void {
step(1);
}
}

View File

@ -0,0 +1,33 @@
//Famicom controller port pinout:
// ____
// | \
// |(7) \
// |(2)(1)|
// |(3)(5)|
// |(4)(6)|
// |______|
//
// pin name port1 port2
// 1: +5v
// 2: clock $4016 read $4016.d0 write
// 3: latch $4016.d0 write $4016.d0 write
// 4: data0 $4016.d0 read $4017.d0 read
// 5: data3 $4016.d3 read $4017.d3 read
// 6: data4 $4016.d4 read $4017.d4 read
// 7: gnd
struct Controller : Cothread {
enum : bool { Port1 = 0, Port2 = 1 };
Controller(bool port);
virtual ~Controller();
static auto Enter() -> void;
virtual auto main() -> void;
virtual auto data() -> uint3 { return 0; }
virtual auto latch(bool data) -> void {}
const bool port;
};
#include "gamepad/gamepad.hpp"

View File

@ -0,0 +1,37 @@
Gamepad::Gamepad(bool port) : Controller(port) {
}
auto Gamepad::data() -> uint3 {
if(counter >= 8) return 1;
if(latched == 1) return interface->inputPoll(port, Device::Gamepad, A);
switch(counter++) {
case 0: return a;
case 1: return b;
case 2: return select;
case 3: return start;
case 4: return up && !down;
case 5: return down && !up;
case 6: return left && !right;
case 7: return right && !left;
}
unreachable;
}
auto Gamepad::latch(bool data) -> void {
if(latched == data) return;
latched = data;
counter = 0;
if(latched == 0) {
auto id = Device::Gamepad;
a = interface->inputPoll(port, id, A);
b = interface->inputPoll(port, id, B);
select = interface->inputPoll(port, id, Select);
start = interface->inputPoll(port, id, Start);
up = interface->inputPoll(port, id, Up);
down = interface->inputPoll(port, id, Down);
left = interface->inputPoll(port, id, Left);
right = interface->inputPoll(port, id, Right);
}
}

View File

@ -0,0 +1,22 @@
struct Gamepad : Controller {
enum : uint {
Up, Down, Left, Right, B, A, Select, Start,
};
Gamepad(bool port);
auto data() -> uint3;
auto latch(bool data) -> void;
private:
bool latched = 0;
uint counter = 0;
bool a = 0;
bool b = 0;
bool select = 0;
bool start = 0;
bool up = 0;
bool down = 0;
bool left = 0;
bool right = 0;
};

View File

@ -2,6 +2,7 @@
namespace Famicom {
#include "memory.cpp"
#include "timing.cpp"
#include "serialization.cpp"
CPU cpu;
@ -11,19 +12,24 @@ auto CPU::Enter() -> void {
}
auto CPU::main() -> void {
if(status.interrupt_pending) return interrupt();
exec();
if(io.interruptPending) return interrupt();
instruction();
}
auto CPU::add_clocks(uint clocks) -> void {
auto CPU::step(uint clocks) -> void {
apu.clock -= clocks;
if(apu.clock < 0 && !scheduler.synchronizing()) co_switch(apu.thread);
if(apu.clock < 0) co_switch(apu.thread);
ppu.clock -= clocks;
if(ppu.clock < 0 && !scheduler.synchronizing()) co_switch(ppu.thread);
if(ppu.clock < 0) co_switch(ppu.thread);
cartridge.clock -= clocks;
if(cartridge.clock < 0 && !scheduler.synchronizing()) co_switch(cartridge.thread);
if(cartridge.clock < 0) co_switch(cartridge.thread);
for(auto peripheral : peripherals) {
peripheral->clock -= clocks * (uint64)peripheral->frequency;
if(peripheral->clock < 0) co_switch(peripheral->thread);
}
}
auto CPU::power() -> void {
@ -43,59 +49,8 @@ auto CPU::reset() -> void {
regs.pc = bus.read(0xfffc) << 0;
regs.pc |= bus.read(0xfffd) << 8;
status.interrupt_pending = false;
status.nmi_pending = false;
status.nmi_line = 0;
status.irq_line = 0;
status.irq_apu_line = 0;
status.rdy_line = 1;
status.rdy_addr_valid = false;
status.rdy_addr_value = 0x0000;
status.oam_dma_pending = false;
status.oam_dma_page = 0x00;
status.controller_latch = false;
status.controller_port0 = 0;
status.controller_port1 = 0;
}
auto CPU::debugger_read(uint16 addr) -> uint8 {
return bus.read(addr);
}
auto CPU::ram_read(uint16 addr) -> uint8 {
return ram[addr & 0x07ff];
}
auto CPU::ram_write(uint16 addr, uint8 data) -> void {
ram[addr & 0x07ff] = data;
}
auto CPU::read(uint16 addr) -> uint8 {
if(addr == 0x4016) {
return (mdr() & 0xc0) | input.data(0);
}
if(addr == 0x4017) {
return (mdr() & 0xc0) | input.data(1);
}
return apu.read(addr);
}
auto CPU::write(uint16 addr, uint8 data) -> void {
if(addr == 0x4014) {
status.oam_dma_page = data;
status.oam_dma_pending = true;
}
if(addr == 0x4016) {
input.latch(data & 0x01);
}
return apu.write(addr, data);
memory::fill(&io, sizeof(IO));
io.rdyLine = 1;
}
}

View File

@ -1,57 +1,56 @@
struct CPU : Processor::R6502, Thread {
static auto Enter() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto reset() -> void;
auto debugger_read(uint16 addr) -> uint8;
//memory.cpp
auto readRAM(uint11 addr) -> uint8;
auto writeRAM(uint11 addr, uint8 data) -> void;
auto ram_read(uint16 addr) -> uint8;
auto ram_write(uint16 addr, uint8 data) -> void;
auto readIO(uint16 addr) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto readDebugger(uint16 addr) -> uint8 override;
auto serialize(serializer&) -> void;
//timing.cpp
auto op_read(uint16 addr) -> uint8;
auto op_write(uint16 addr, uint8 data) -> void;
auto last_cycle() -> void;
auto nmi(uint16& vector) -> void;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto lastCycle() -> void override;
auto nmi(uint16& vector) -> void override;
auto oam_dma() -> void;
auto oamdma() -> void;
auto set_nmi_line(bool) -> void;
auto set_irq_line(bool) -> void;
auto set_irq_apu_line(bool) -> void;
auto nmiLine(bool) -> void;
auto irqLine(bool) -> void;
auto apuLine(bool) -> void;
auto set_rdy_line(bool) -> void;
auto set_rdy_addr(bool valid, uint16 value = 0) -> void;
auto rdyLine(bool) -> void;
auto rdyAddr(bool valid, uint16 value = 0) -> void;
//protected:
vector<Thread*> peripherals;
uint8 ram[0x0800];
struct Status {
bool interrupt_pending;
bool nmi_pending;
bool nmi_line;
bool irq_line;
bool irq_apu_line;
struct IO {
bool interruptPending;
bool nmiPending;
bool nmiLine;
bool irqLine;
bool apuLine;
bool rdy_line;
bool rdy_addr_valid;
uint16 rdy_addr_value;
bool rdyLine;
bool rdyAddrValid;
uint16 rdyAddrValue;
bool oam_dma_pending;
uint8 oam_dma_page;
bool controller_latch;
uint controller_port0;
uint controller_port1;
} status;
bool oamdmaPending;
uint8 oamdmaPage;
} io;
};
extern CPU cpu;

49
higan/fc/cpu/memory.cpp Normal file
View File

@ -0,0 +1,49 @@
auto CPU::readRAM(uint11 addr) -> uint8 {
return ram[addr];
}
auto CPU::writeRAM(uint11 addr, uint8 data) -> void {
ram[addr] = data;
}
auto CPU::readIO(uint16 addr) -> uint8 {
switch(addr) {
case 0x4016: {
auto data = Famicom::peripherals.controllerPort1->data();
return (mdr() & 0xc0) | data.bit(2) << 4 | data.bit(1) << 3 | data.bit(0) << 0;
}
case 0x4017: {
auto data = Famicom::peripherals.controllerPort2->data();
return (mdr() & 0xc0) | data.bit(2) << 4 | data.bit(1) << 3 | data.bit(0) << 0;
}
}
return apu.readIO(addr);
}
auto CPU::writeIO(uint16 addr, uint8 data) -> void {
switch(addr) {
case 0x4014: {
io.oamdmaPage = data;
io.oamdmaPending = true;
return;
}
case 0x4016: {
Famicom::peripherals.controllerPort1->latch(data.bit(0));
Famicom::peripherals.controllerPort2->latch(data.bit(0));
return;
}
}
return apu.writeIO(addr, data);
}
auto CPU::readDebugger(uint16 addr) -> uint8 {
return bus.read(addr);
}

View File

@ -4,20 +4,16 @@ auto CPU::serialize(serializer& s) -> void {
s.array(ram);
s.integer(status.interrupt_pending);
s.integer(status.nmi_pending);
s.integer(status.nmi_line);
s.integer(status.irq_line);
s.integer(status.irq_apu_line);
s.integer(io.interruptPending);
s.integer(io.nmiPending);
s.integer(io.nmiLine);
s.integer(io.irqLine);
s.integer(io.apuLine);
s.integer(status.rdy_line);
s.integer(status.rdy_addr_valid);
s.integer(status.rdy_addr_value);
s.integer(io.rdyLine);
s.integer(io.rdyAddrValid);
s.integer(io.rdyAddrValue);
s.integer(status.oam_dma_pending);
s.integer(status.oam_dma_page);
s.integer(status.controller_latch);
s.integer(status.controller_port0);
s.integer(status.controller_port1);
s.integer(io.oamdmaPending);
s.integer(io.oamdmaPage);
}

View File

@ -1,64 +1,64 @@
auto CPU::op_read(uint16 addr) -> uint8 {
if(status.oam_dma_pending) {
status.oam_dma_pending = false;
op_read(addr);
oam_dma();
auto CPU::read(uint16 addr) -> uint8 {
if(io.oamdmaPending) {
io.oamdmaPending = false;
read(addr);
oamdma();
}
while(status.rdy_line == 0) {
regs.mdr = bus.read(status.rdy_addr_valid ? status.rdy_addr_value : addr);
add_clocks(12);
while(io.rdyLine == 0) {
regs.mdr = bus.read(io.rdyAddrValid ? io.rdyAddrValue : addr);
step(12);
}
regs.mdr = bus.read(addr);
add_clocks(12);
step(12);
return regs.mdr;
}
auto CPU::op_write(uint16 addr, uint8 data) -> void {
auto CPU::write(uint16 addr, uint8 data) -> void {
bus.write(addr, regs.mdr = data);
add_clocks(12);
step(12);
}
auto CPU::last_cycle() -> void {
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
auto CPU::lastCycle() -> void {
io.interruptPending = ((io.irqLine | io.apuLine) & ~regs.p.i) | io.nmiPending;
}
auto CPU::nmi(uint16& vector) -> void {
if(status.nmi_pending) {
status.nmi_pending = false;
if(io.nmiPending) {
io.nmiPending = false;
vector = 0xfffa;
}
}
auto CPU::oam_dma() -> void {
auto CPU::oamdma() -> void {
for(uint n : range(256)) {
uint8 data = op_read((status.oam_dma_page << 8) + n);
op_write(0x2004, data);
uint8 data = read(io.oamdmaPage << 8 | n);
write(0x2004, data);
}
}
auto CPU::set_nmi_line(bool line) -> void {
auto CPU::nmiLine(bool line) -> void {
//edge-sensitive (0->1)
if(!status.nmi_line && line) status.nmi_pending = true;
status.nmi_line = line;
if(!io.nmiLine && line) io.nmiPending = true;
io.nmiLine = line;
}
auto CPU::set_irq_line(bool line) -> void {
auto CPU::irqLine(bool line) -> void {
//level-sensitive
status.irq_line = line;
io.irqLine = line;
}
auto CPU::set_irq_apu_line(bool line) -> void {
auto CPU::apuLine(bool line) -> void {
//level-sensitive
status.irq_apu_line = line;
io.apuLine = line;
}
auto CPU::set_rdy_line(bool line) -> void {
status.rdy_line = line;
auto CPU::rdyLine(bool line) -> void {
io.rdyLine = line;
}
auto CPU::set_rdy_addr(bool valid, uint16 value) -> void {
status.rdy_addr_valid = valid;
status.rdy_addr_value = value;
auto CPU::rdyAddr(bool valid, uint16 value) -> void {
io.rdyAddrValid = valid;
io.rdyAddrValue = value;
}

View File

@ -37,15 +37,29 @@ namespace Famicom {
int64 clock = 0;
};
#include <fc/system/system.hpp>
struct Cothread : Thread {
auto step(uint clocks) -> void;
auto synchronizeCPU() -> void;
};
#include <fc/scheduler/scheduler.hpp>
#include <fc/input/input.hpp>
#include <fc/controller/controller.hpp>
#include <fc/system/system.hpp>
#include <fc/memory/memory.hpp>
#include <fc/cartridge/cartridge.hpp>
#include <fc/cpu/cpu.hpp>
#include <fc/apu/apu.hpp>
#include <fc/ppu/ppu.hpp>
#include <fc/cheat/cheat.hpp>
inline auto Cothread::step(uint clocks) -> void {
clock += clocks * (uint64)cpu.frequency;
synchronizeCPU();
}
inline auto Cothread::synchronizeCPU() -> void {
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
}
#include <fc/interface/interface.hpp>

View File

@ -1,56 +0,0 @@
#include <fc/fc.hpp>
namespace Famicom {
#include "serialization.cpp"
Input input;
auto Input::latch(bool data) -> void {
latchdata = data;
if(latchdata == 1) {
counter1 = 0;
counter2 = 0;
}
}
auto Input::data(bool port) -> bool {
//table to convert native button ordering to Emulator::Interface ordering
static const uint lookup[] = {5, 4, 6, 7, 0, 1, 2, 3};
bool result = 0;
if(port == 0) {
if(port1 == Device::Joypad) {
if(counter1 >= 8) return 1;
result = interface->inputPoll(0, 0u, lookup[counter1]);
if(latchdata == 0) counter1++;
}
}
if(port == 1) {
if(port2 == Device::Joypad) {
if(counter2 >= 8) return 1;
result = interface->inputPoll(1, 0u, lookup[counter2]);
if(latchdata == 0) counter2++;
}
}
return result;
}
auto Input::connect(bool port, Device device) -> void {
if(port == 0) port1 = device, counter1 = 0;
if(port == 1) port2 = device, counter2 = 0;
}
auto Input::power() -> void {
}
auto Input::reset() -> void {
latchdata = 0;
counter1 = 0;
counter2 = 0;
}
}

View File

@ -1,25 +0,0 @@
struct Input {
enum class Device : uint {
Joypad,
None,
};
auto latch(bool data) -> void;
auto data(bool port) -> bool;
auto connect(bool port, Device device) -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
private:
Device port1;
Device port2;
bool latchdata;
uint counter1;
uint counter2;
};
extern Input input;

View File

@ -1,8 +0,0 @@
auto Input::serialize(serializer& s) -> void {
s.integer((uint&)port1);
s.integer((uint&)port2);
s.integer(latchdata);
s.integer(counter1);
s.integer(counter2);
}

View File

@ -21,7 +21,11 @@ Interface::Interface() {
media.append({ID::Famicom, "Famicom", "fc", true});
{ Device device{0, ID::Port1 | ID::Port2, "Controller"};
{ Device device{0, ID::ControllerPort1 | ID::ControllerPort2, "None"};
devices.append(device);
}
{ Device device{1, ID::ControllerPort1 | ID::ControllerPort2, "Gamepad"};
device.inputs.append({0, 0, "Up" });
device.inputs.append({1, 0, "Down" });
device.inputs.append({2, 0, "Left" });
@ -190,6 +194,10 @@ auto Interface::unload() -> void {
system.unload();
}
auto Interface::connect(uint port, uint device) -> void {
Famicom::peripherals.connect(port, device);
}
auto Interface::power() -> void {
system.power();
}

View File

@ -16,9 +16,10 @@ struct ID {
CharacterRAM,
};
enum : uint {
Port1 = 1,
Port2 = 2,
enum : uint { //bitmasks
ControllerPort1 = 1,
ControllerPort2 = 2,
ExpansionPort = 4,
};
};
@ -41,6 +42,7 @@ struct Interface : Emulator::Interface {
auto save(uint id, const stream& stream) -> void;
auto unload() -> void;
auto connect(uint port, uint device) -> void;
auto power() -> void;
auto reset() -> void;
auto run() -> void;
@ -61,6 +63,10 @@ private:
struct Settings {
bool colorEmulation = true;
bool scanlineEmulation = true;
uint controllerPort1 = 0;
uint controllerPort2 = 0;
uint expansionPort = 0;
};
extern Interface* interface;

View File

@ -13,9 +13,9 @@ Bus bus;
auto Bus::read(uint16 addr) -> uint8 {
uint8 data = cartridge.prg_read(addr);
if(addr <= 0x1fff) data = cpu.ram_read(addr);
else if(addr <= 0x3fff) data = ppu.read(addr);
else if(addr <= 0x4017) data = cpu.read(addr);
if(addr <= 0x1fff) data = cpu.readRAM(addr);
else if(addr <= 0x3fff) data = ppu.readIO(addr);
else if(addr <= 0x4017) data = cpu.readIO(addr);
if(cheat.enable()) {
if(auto result = cheat.find(addr, data)) return result();
@ -26,9 +26,9 @@ auto Bus::read(uint16 addr) -> uint8 {
auto Bus::write(uint16 addr, uint8 data) -> void {
cartridge.prg_write(addr, data);
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
if(addr <= 0x3fff) return ppu.write(addr, data);
if(addr <= 0x4017) return cpu.write(addr, data);
if(addr <= 0x1fff) return cpu.writeRAM(addr, data);
if(addr <= 0x3fff) return ppu.writeIO(addr, data);
if(addr <= 0x4017) return cpu.writeIO(addr, data);
}
}

View File

@ -17,16 +17,16 @@ auto PPU::main() -> void {
auto PPU::tick() -> void {
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
if(status.ly == 241 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag);
if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0;
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0;
if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 261 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
if(status.ly == 261 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag);
clock += 4;
if(clock >= 0) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
status.lx++;
}
@ -97,7 +97,7 @@ auto PPU::reset() -> void {
for(auto& n : oam ) n = 0;
}
auto PPU::read(uint16 addr) -> uint8 {
auto PPU::readIO(uint16 addr) -> uint8 {
uint8 result = 0x00;
switch(addr & 7) {
@ -108,7 +108,7 @@ auto PPU::read(uint16 addr) -> uint8 {
result |= status.mdr & 0x1f;
status.address_latch = 0;
status.nmi_hold = 0;
cpu.set_nmi_line(status.nmi_flag = 0);
cpu.nmiLine(status.nmi_flag = 0);
break;
case 4: //OAMDATA
result = oam[status.oam_addr];
@ -134,7 +134,7 @@ auto PPU::read(uint16 addr) -> uint8 {
return result;
}
auto PPU::write(uint16 addr, uint8 data) -> void {
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
status.mdr = data;
switch(addr & 7) {
@ -146,7 +146,7 @@ auto PPU::write(uint16 addr, uint8 data) -> void {
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
status.vram_increment = (data & 0x04) ? 32 : 1;
status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
cpu.set_nmi_line(status.nmi_enable && status.nmi_hold && status.nmi_flag);
cpu.nmiLine(status.nmi_enable && status.nmi_hold && status.nmi_flag);
return;
case 1: //PPUMASK
status.emphasis = data >> 5;

View File

@ -10,8 +10,8 @@ struct PPU : Thread {
auto power() -> void;
auto reset() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto readIO(uint16 addr) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void;
auto ciram_read(uint16 addr) -> uint8;
auto ciram_write(uint16 addr, uint8 data) -> void;

View File

@ -24,8 +24,8 @@ auto Scheduler::exit(Event event_) -> void {
}
auto Scheduler::synchronize(cothread_t thread) -> void {
if(thread == ppu.thread) {
while(enter(Mode::SynchronizePPU) != Event::Synchronize);
if(thread == cpu.thread) {
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
} else {
resume = thread;
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
@ -33,8 +33,8 @@ auto Scheduler::synchronize(cothread_t thread) -> void {
}
auto Scheduler::synchronize() -> void {
if(co_active() == ppu.thread && mode == Mode::SynchronizePPU) return exit(Event::Synchronize);
if(co_active() != ppu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
}
auto Scheduler::synchronizing() const -> bool {

View File

@ -1,7 +1,7 @@
struct Scheduler {
enum class Mode : uint {
Run,
SynchronizePPU,
SynchronizeCPU,
SynchronizeAll,
};

View File

@ -0,0 +1,46 @@
Peripherals peripherals;
auto Peripherals::unload() -> void {
delete controllerPort1;
delete controllerPort2;
controllerPort1 = nullptr;
controllerPort2 = nullptr;
}
auto Peripherals::reset() -> void {
connect(Port::Controller1, settings.controllerPort1);
connect(Port::Controller2, settings.controllerPort2);
}
auto Peripherals::connect(uint port, uint device) -> void {
if(port == Port::Controller1) {
settings.controllerPort1 = device;
if(!system.loaded()) return;
delete controllerPort1;
switch(device) { default:
case Device::None: controllerPort1 = new Controller(0); break;
case Device::Gamepad: controllerPort1 = new Gamepad(0); break;
}
}
if(port == Port::Controller2) {
settings.controllerPort2 = device;
if(!system.loaded()) return;
delete controllerPort2;
switch(device) { default:
case Device::None: controllerPort2 = new Controller(1); break;
case Device::Gamepad: controllerPort2 = new Gamepad(1); break;
}
}
if(port == Port::Expansion) {
settings.expansionPort = device;
if(!system.loaded()) return;
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort1);
cpu.peripherals.append(controllerPort2);
}

View File

@ -0,0 +1,25 @@
struct Port { enum : uint {
Controller1,
Controller2,
Expansion,
};};
struct Device { enum : uint {
None,
//controller port peripherals
Gamepad,
//expansion port peripherals
};};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint device) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
};
extern Peripherals peripherals;

View File

@ -37,7 +37,6 @@ auto System::serialize(serializer& s) -> void {
auto System::serializeAll(serializer& s) -> void {
system.serialize(s);
input.serialize(s);
cartridge.serialize(s);
cpu.serialize(s);
apu.serialize(s);

View File

@ -2,21 +2,23 @@
namespace Famicom {
#include "peripherals.cpp"
#include "video.cpp"
#include "serialization.cpp"
System system;
auto System::loaded() const -> bool { return _loaded; }
auto System::run() -> void {
scheduler.enter();
}
auto System::runToSave() -> void {
scheduler.synchronize(ppu.thread);
scheduler.synchronize(cpu.thread);
scheduler.synchronize(apu.thread);
scheduler.synchronize(ppu.thread);
scheduler.synchronize(cartridge.thread);
for(auto peripheral : cpu.peripherals) {
scheduler.synchronize(peripheral->thread);
}
}
auto System::load() -> void {
@ -29,6 +31,7 @@ auto System::load() -> void {
auto System::unload() -> void {
if(!loaded()) return;
peripherals.unload();
cartridge.unload();
_loaded = false;
}
@ -38,7 +41,6 @@ auto System::power() -> void {
cpu.power();
apu.power();
ppu.power();
input.reset();
reset();
}
@ -55,14 +57,12 @@ auto System::reset() -> void {
cpu.reset();
apu.reset();
ppu.reset();
input.reset();
scheduler.reset();
peripherals.reset();
}
auto System::init() -> void {
assert(interface != nullptr);
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::None);
}
auto System::term() -> void {

View File

@ -1,5 +1,7 @@
#include "peripherals.hpp"
struct System {
auto loaded() const -> bool;
auto loaded() const -> bool { return _loaded; }
auto run() -> void;
auto runToSave() -> void;

View File

@ -0,0 +1,148 @@
auto R6502::fp_adc() {
int result = regs.a + rd + regs.p.c;
regs.p.v = ~(regs.a ^ rd) & (regs.a ^ result) & 0x80;
regs.p.c = (result > 0xff);
regs.p.n = (result & 0x80);
regs.p.z = ((uint8)result == 0);
regs.a = result;
}
auto R6502::fp_and() {
regs.a &= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_asl() {
regs.p.c = rd & 0x80;
rd <<= 1;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_bit() {
regs.p.n = (rd & 0x80);
regs.p.v = (rd & 0x40);
regs.p.z = ((rd & regs.a) == 0);
}
auto R6502::fp_cmp() {
int r = regs.a - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::fp_cpx() {
int r = regs.x - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::fp_cpy() {
int r = regs.y - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::fp_dec() {
rd--;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_eor() {
regs.a ^= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_inc() {
rd++;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_lda() {
regs.a = rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_ldx() {
regs.x = rd;
regs.p.n = (regs.x & 0x80);
regs.p.z = (regs.x == 0);
}
auto R6502::fp_ldy() {
regs.y = rd;
regs.p.n = (regs.y & 0x80);
regs.p.z = (regs.y == 0);
}
auto R6502::fp_lsr() {
regs.p.c = rd & 0x01;
rd >>= 1;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_ora() {
regs.a |= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_rla() {
uint carry = (uint)regs.p.c;
regs.p.c = regs.a & 0x80;
regs.a = (regs.a << 1) | carry;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_rol() {
uint carry = (uint)regs.p.c;
regs.p.c = rd & 0x80;
rd = (rd << 1) | carry;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_ror() {
uint carry = (uint)regs.p.c << 7;
regs.p.c = rd & 0x01;
rd = carry | (rd >> 1);
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::fp_rra() {
uint carry = (uint)regs.p.c << 7;
regs.p.c = regs.a & 0x01;
regs.a = carry | (regs.a >> 1);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_sbc() {
rd ^= 0xff;
return fp_adc();
}
auto R6502::fp_sla() {
regs.p.c = regs.a & 0x80;
regs.a <<= 1;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::fp_sra() {
regs.p.c = regs.a & 0x01;
regs.a >>= 1;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}

View File

@ -1,25 +1,24 @@
auto R6502::disassemble() -> string {
string output = {hex(regs.pc, 4L), " "};
auto abs = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L)}; };
auto abx = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L), ",x"}; };
auto aby = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L), ",y"}; };
auto iab = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L), ")"}; };
auto imm = [&]() -> string { return {"#$", hex(debugger_read(regs.pc + 1), 2L)}; };
auto abs = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L)}; };
auto abx = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L), ",x"}; };
auto aby = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L), ",y"}; };
auto iab = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L), ")"}; };
auto imm = [&]() -> string { return {"#$", hex(readDebugger(regs.pc + 1), 2L)}; };
auto imp = [&]() -> string { return {""}; };
auto izx = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 1), 2L), ",x)"}; };
auto izy = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 1), 2L), "),y"}; };
auto rel = [&]() -> string { return {"$", hex((regs.pc + 2) + (int8)debugger_read(regs.pc + 1), 4L)}; };
auto zpg = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L)}; };
auto zpx = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L), ",x"}; };
auto zpy = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L), ",y"}; };
auto izx = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 1), 2L), ",x)"}; };
auto izy = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 1), 2L), "),y"}; };
auto rel = [&]() -> string { return {"$", hex((regs.pc + 2) + (int8)readDebugger(regs.pc + 1), 4L)}; };
auto zpg = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L)}; };
auto zpx = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L), ",x"}; };
auto zpy = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L), ",y"}; };
#define op(byte, prefix, mode) \
case byte: output.append(#prefix, " ", mode()); \
break
uint8 opcode = debugger_read(regs.pc);
switch(opcode) {
switch(auto opcode = readDebugger(regs.pc)) {
op(0x00, brk, imm);
op(0x01, ora, izx);
op(0x05, ora, zpg);

View File

@ -1,452 +1,296 @@
//opcode functions
//================
auto R6502::opf_adc() {
int result = regs.a + rd + regs.p.c;
regs.p.v = ~(regs.a ^ rd) & (regs.a ^ result) & 0x80;
regs.p.c = (result > 0xff);
regs.p.n = (result & 0x80);
regs.p.z = ((uint8)result == 0);
regs.a = result;
}
auto R6502::opf_and() {
regs.a &= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_asl() {
regs.p.c = rd & 0x80;
rd <<= 1;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_bit() {
regs.p.n = (rd & 0x80);
regs.p.v = (rd & 0x40);
regs.p.z = ((rd & regs.a) == 0);
}
auto R6502::opf_cmp() {
int r = regs.a - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::opf_cpx() {
int r = regs.x - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::opf_cpy() {
int r = regs.y - rd;
regs.p.n = (r & 0x80);
regs.p.z = (uint8)(r == 0);
regs.p.c = (r >= 0);
}
auto R6502::opf_dec() {
rd--;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_eor() {
regs.a ^= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_inc() {
rd++;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_lda() {
regs.a = rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_ldx() {
regs.x = rd;
regs.p.n = (regs.x & 0x80);
regs.p.z = (regs.x == 0);
}
auto R6502::opf_ldy() {
regs.y = rd;
regs.p.n = (regs.y & 0x80);
regs.p.z = (regs.y == 0);
}
auto R6502::opf_lsr() {
regs.p.c = rd & 0x01;
rd >>= 1;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_ora() {
regs.a |= rd;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_rla() {
uint carry = (uint)regs.p.c;
regs.p.c = regs.a & 0x80;
regs.a = (regs.a << 1) | carry;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_rol() {
uint carry = (uint)regs.p.c;
regs.p.c = rd & 0x80;
rd = (rd << 1) | carry;
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_ror() {
uint carry = (uint)regs.p.c << 7;
regs.p.c = rd & 0x01;
rd = carry | (rd >> 1);
regs.p.n = (rd & 0x80);
regs.p.z = (rd == 0);
}
auto R6502::opf_rra() {
uint carry = (uint)regs.p.c << 7;
regs.p.c = regs.a & 0x01;
regs.a = carry | (regs.a >> 1);
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_sbc() {
rd ^= 0xff;
return opf_adc();
}
auto R6502::opf_sla() {
regs.p.c = regs.a & 0x80;
regs.a <<= 1;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
auto R6502::opf_sra() {
regs.p.c = regs.a & 0x01;
regs.a >>= 1;
regs.p.n = (regs.a & 0x80);
regs.p.z = (regs.a == 0);
}
//opcode implementations
//======================
auto R6502::opi_branch(bool condition) {
if(condition == false) {
L rd = op_readpci();
auto R6502::op_branch(bool condition) {
if(!condition) {
L rd = readPC();
} else {
rd = op_readpci();
rd = readPC();
aa = regs.pc + (int8)rd;
op_page(regs.pc, aa);
L op_readpc();
ioPage(regs.pc, aa);
L io();
regs.pc = aa;
}
}
auto R6502::opi_clear_flag(uint bit) {
L op_readpc();
auto R6502::op_clear_flag(uint bit) {
L io();
regs.p &= ~(1 << bit);
}
auto R6502::opi_decrement(uint8& r) {
L op_readpc();
auto R6502::op_decrement(uint8& r) {
L io();
r--;
regs.p.n = (r & 0x80);
regs.p.z = (r == 0);
}
auto R6502::opi_increment(uint8& r) {
L op_readpc();
auto R6502::op_increment(uint8& r) {
L io();
r++;
regs.p.n = (r & 0x80);
regs.p.z = (r == 0);
}
auto R6502::opi_pull(uint8& r) {
op_readpc();
op_readpc();
L r = op_readsp();
auto R6502::op_pull(uint8& r) {
io();
io();
L r = readSP();
regs.p.n = (r & 0x80);
regs.p.z = (r == 0);
}
auto R6502::opi_push(uint8& r) {
op_readpc();
L op_writesp(r);
auto R6502::op_push(uint8& r) {
io();
L writeSP(r);
}
auto R6502::opi_read_absolute(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
L rd = op_read(abs.w);
auto R6502::op_read_absolute(fp op) {
abs.l = readPC();
abs.h = readPC();
L rd = read(abs.w);
call(op);
}
auto R6502::opi_read_absolute_x(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page(abs.w, abs.w + regs.x);
L rd = op_read(abs.w + regs.x);
auto R6502::op_read_absolute_x(fp op) {
abs.l = readPC();
abs.h = readPC();
ioPage(abs.w, abs.w + regs.x);
L rd = read(abs.w + regs.x);
call(op);
}
auto R6502::opi_read_absolute_y(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page(abs.w, abs.w + regs.y);
L rd = op_read(abs.w + regs.y);
auto R6502::op_read_absolute_y(fp op) {
abs.l = readPC();
abs.h = readPC();
ioPage(abs.w, abs.w + regs.y);
L rd = read(abs.w + regs.y);
call(op);
}
auto R6502::opi_read_immediate(fp op) {
L rd = op_readpci();
auto R6502::op_read_immediate(fp op) {
L rd = readPC();
call(op);
}
auto R6502::opi_read_indirect_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
abs.l = op_readzp(zp++ + regs.x);
abs.h = op_readzp(zp++ + regs.x);
L rd = op_read(abs.w);
auto R6502::op_read_indirect_zero_page_x(fp op) {
zp = readPC();
readZP(zp);
abs.l = readZP(zp++ + regs.x);
abs.h = readZP(zp++ + regs.x);
L rd = read(abs.w);
call(op);
}
auto R6502::opi_read_indirect_zero_page_y(fp op) {
rd = op_readpci();
abs.l = op_readzp(rd++);
abs.h = op_readzp(rd++);
op_page(abs.w, abs.w + regs.y);
L rd = op_read(abs.w + regs.y);
auto R6502::op_read_indirect_zero_page_y(fp op) {
rd = readPC();
abs.l = readZP(rd++);
abs.h = readZP(rd++);
ioPage(abs.w, abs.w + regs.y);
L rd = read(abs.w + regs.y);
call(op);
}
auto R6502::opi_read_zero_page(fp op) {
zp = op_readpci();
L rd = op_readzp(zp);
auto R6502::op_read_zero_page(fp op) {
zp = readPC();
L rd = readZP(zp);
call(op);
}
auto R6502::opi_read_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
L rd = op_readzp(zp + regs.x);
auto R6502::op_read_zero_page_x(fp op) {
zp = readPC();
readZP(zp);
L rd = readZP(zp + regs.x);
call(op);
}
auto R6502::opi_read_zero_page_y(fp op) {
zp = op_readpci();
op_readzp(zp);
L rd = op_readzp(zp + regs.y);
auto R6502::op_read_zero_page_y(fp op) {
zp = readPC();
readZP(zp);
L rd = readZP(zp + regs.y);
call(op);
}
auto R6502::opi_rmw_absolute(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
rd = op_read(abs.w);
op_write(abs.w, rd);
auto R6502::op_rmw_absolute(fp op) {
abs.l = readPC();
abs.h = readPC();
rd = read(abs.w);
write(abs.w, rd);
call(op);
L op_write(abs.w, rd);
L write(abs.w, rd);
}
auto R6502::opi_rmw_absolute_x(fp op) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page_always(abs.w, abs.w + regs.x);
rd = op_read(abs.w + regs.x);
op_write(abs.w + regs.x, rd);
auto R6502::op_rmw_absolute_x(fp op) {
abs.l = readPC();
abs.h = readPC();
ioPageAlways(abs.w, abs.w + regs.x);
rd = read(abs.w + regs.x);
write(abs.w + regs.x, rd);
call(op);
L op_write(abs.w + regs.x, rd);
L write(abs.w + regs.x, rd);
}
auto R6502::opi_rmw_zero_page(fp op) {
zp = op_readpci();
rd = op_readzp(zp);
op_writezp(zp, rd);
auto R6502::op_rmw_zero_page(fp op) {
zp = readPC();
rd = readZP(zp);
writeZP(zp, rd);
call(op);
L op_writezp(zp, rd);
L writeZP(zp, rd);
}
auto R6502::opi_rmw_zero_page_x(fp op) {
zp = op_readpci();
op_readzp(zp);
rd = op_readzp(zp + regs.x);
op_writezp(zp + regs.x, rd);
auto R6502::op_rmw_zero_page_x(fp op) {
zp = readPC();
readZP(zp);
rd = readZP(zp + regs.x);
writeZP(zp + regs.x, rd);
call(op);
L op_writezp(zp + regs.x, rd);
L writeZP(zp + regs.x, rd);
}
auto R6502::opi_set_flag(uint bit) {
L op_readpc();
auto R6502::op_set_flag(uint bit) {
L io();
regs.p |= 1 << bit;
}
auto R6502::opi_shift(fp op) {
L op_readpc();
auto R6502::op_shift(fp op) {
L io();
call(op);
}
auto R6502::opi_store_absolute(uint8& r) {
abs.l = op_readpci();
abs.h = op_readpci();
L op_write(abs.w, r);
auto R6502::op_store_absolute(uint8& r) {
abs.l = readPC();
abs.h = readPC();
L write(abs.w, r);
}
auto R6502::opi_store_absolute_x(uint8& r) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page_always(abs.w, abs.w + regs.x);
L op_write(abs.w + regs.x, r);
auto R6502::op_store_absolute_x(uint8& r) {
abs.l = readPC();
abs.h = readPC();
ioPageAlways(abs.w, abs.w + regs.x);
L write(abs.w + regs.x, r);
}
auto R6502::opi_store_absolute_y(uint8& r) {
abs.l = op_readpci();
abs.h = op_readpci();
op_page_always(abs.w, abs.w + regs.y);
L op_write(abs.w + regs.y, r);
auto R6502::op_store_absolute_y(uint8& r) {
abs.l = readPC();
abs.h = readPC();
ioPageAlways(abs.w, abs.w + regs.y);
L write(abs.w + regs.y, r);
}
auto R6502::opi_store_indirect_zero_page_x(uint8& r) {
zp = op_readpci();
op_readzp(zp);
abs.l = op_readzp(zp++ + regs.x);
abs.h = op_readzp(zp++ + regs.x);
L op_write(abs.w, r);
auto R6502::op_store_indirect_zero_page_x(uint8& r) {
zp = readPC();
readZP(zp);
abs.l = readZP(zp++ + regs.x);
abs.h = readZP(zp++ + regs.x);
L write(abs.w, r);
}
auto R6502::opi_store_indirect_zero_page_y(uint8& r) {
rd = op_readpci();
abs.l = op_readzp(rd++);
abs.h = op_readzp(rd++);
op_page_always(abs.w, abs.w + regs.y);
L op_write(abs.w + regs.y, r);
auto R6502::op_store_indirect_zero_page_y(uint8& r) {
rd = readPC();
abs.l = readZP(rd++);
abs.h = readZP(rd++);
ioPageAlways(abs.w, abs.w + regs.y);
L write(abs.w + regs.y, r);
}
auto R6502::opi_store_zero_page(uint8& r) {
zp = op_readpci();
L op_writezp(zp, r);
auto R6502::op_store_zero_page(uint8& r) {
zp = readPC();
L writeZP(zp, r);
}
auto R6502::opi_store_zero_page_x(uint8& r) {
zp = op_readpci();
op_readzp(zp);
L op_writezp(zp + regs.x, r);
auto R6502::op_store_zero_page_x(uint8& r) {
zp = readPC();
readZP(zp);
L writeZP(zp + regs.x, r);
}
auto R6502::opi_store_zero_page_y(uint8& r) {
zp = op_readpci();
op_readzp(zp);
L op_writezp(zp + regs.y, r);
auto R6502::op_store_zero_page_y(uint8& r) {
zp = readPC();
readZP(zp);
L writeZP(zp + regs.y, r);
}
auto R6502::opi_transfer(uint8& s, uint8& d, bool flag) {
L op_readpc();
auto R6502::op_transfer(uint8& s, uint8& d, bool flag) {
L io();
d = s;
if(flag == false) return;
if(!flag) return;
regs.p.n = (d & 0x80);
regs.p.z = (d == 0);
}
//opcodes
//=======
//
auto R6502::op_brk() {
op_readpci();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
op_writesp(regs.p | 0x30);
abs.l = op_read(0xfffe);
readPC();
writeSP(regs.pc >> 8);
writeSP(regs.pc >> 0);
writeSP(regs.p | 0x30);
abs.l = read(0xfffe);
regs.p.i = 1;
regs.p.d = 0;
L abs.h = op_read(0xffff);
L abs.h = read(0xffff);
regs.pc = abs.w;
}
auto R6502::op_jmp_absolute() {
abs.l = op_readpci();
L abs.h = op_readpci();
abs.l = readPC();
L abs.h = readPC();
regs.pc = abs.w;
}
auto R6502::op_jmp_indirect_absolute() {
abs.l = op_readpci();
abs.h = op_readpci();
iabs.l = op_read(abs.w); abs.l++;
L iabs.h = op_read(abs.w); abs.l++;
abs.l = readPC();
abs.h = readPC();
iabs.l = read(abs.w); abs.l++;
L iabs.h = read(abs.w); abs.l++;
regs.pc = iabs.w;
}
auto R6502::op_jsr_absolute() {
abs.l = op_readpci();
abs.h = op_readpci();
op_readpc();
abs.l = readPC();
abs.h = readPC();
io();
regs.pc--;
op_writesp(regs.pc >> 8);
L op_writesp(regs.pc >> 0);
writeSP(regs.pc >> 8);
L writeSP(regs.pc >> 0);
regs.pc = abs.w;
}
auto R6502::op_nop() {
L op_readpc();
L io();
}
auto R6502::op_php() {
op_readpc();
L op_writesp(regs.p | 0x30);
io();
L writeSP(regs.p | 0x30);
}
auto R6502::op_plp() {
op_readpc();
op_readpc();
L regs.p = op_readsp();
io();
io();
L regs.p = readSP();
}
auto R6502::op_rti() {
op_readpc();
op_readpc();
regs.p = op_readsp();
abs.l = op_readsp();
L abs.h = op_readsp();
io();
io();
regs.p = readSP();
abs.l = readSP();
L abs.h = readSP();
regs.pc = abs.w;
}
auto R6502::op_rts() {
op_readpc();
op_readpc();
abs.l = op_readsp();
abs.h = op_readsp();
L op_readpc();
io();
io();
abs.l = readSP();
abs.h = readSP();
L io();
regs.pc = ++abs.w;
}
//illegal opcodes
//===============
auto R6502::opill_arr_immediate() {
L rd = op_readpci();
auto R6502::op_arr_immediate() {
L rd = readPC();
regs.a &= rd;
regs.a = (regs.p.c << 7) | (regs.a >> 1);
regs.p.n = (regs.a & 0x80);
@ -455,34 +299,34 @@ L rd = op_readpci();
regs.p.v = regs.p.c ^ ((regs.a >> 5) & 1);
}
auto R6502::opill_nop_absolute() {
abs.l = op_readpci();
abs.h = op_readpci();
L op_readpc();
auto R6502::op_nop_absolute() {
abs.l = readPC();
abs.h = readPC();
L io();
}
auto R6502::opill_nop_absolute_x() {
abs.l = op_readpci();
abs.h = op_readpci();
op_page(abs.w, abs.w + regs.x);
L op_readpc();
auto R6502::op_nop_absolute_x() {
abs.l = readPC();
abs.h = readPC();
ioPage(abs.w, abs.w + regs.x);
L io();
}
auto R6502::opill_nop_immediate() {
L rd = op_readpc();
auto R6502::op_nop_immediate() {
L io();
}
auto R6502::opill_nop_implied() {
L op_readpc();
auto R6502::op_nop_implied() {
L io();
}
auto R6502::opill_nop_zero_page() {
zp = op_readpci();
L op_readzp(zp);
auto R6502::op_nop_zero_page() {
zp = readPC();
L readZP(zp);
}
auto R6502::opill_nop_zero_page_x() {
zp = op_readpci();
op_readzp(zp);
L op_readzp(zp + regs.x);
auto R6502::op_nop_zero_page_x() {
zp = readPC();
readZP(zp);
L readZP(zp + regs.x);
}

View File

@ -1,35 +1,36 @@
auto R6502::op_readpc() -> uint8 {
return op_read(regs.pc);
auto R6502::io() -> uint8 {
return read(regs.pc);
}
auto R6502::op_readpci() -> uint8 {
return op_read(regs.pc++);
auto R6502::readPC() -> uint8 {
return read(regs.pc++);
}
auto R6502::op_readsp() -> uint8 {
return op_read(0x0100 | ++regs.s);
auto R6502::readSP() -> uint8 {
return read(0x0100 | ++regs.s);
}
auto R6502::op_readzp(uint8 addr) -> uint8 {
return op_read(addr);
auto R6502::readZP(uint8 addr) -> uint8 {
return read(addr);
}
//
auto R6502::op_writesp(uint8 data) -> void {
op_write(0x0100 | regs.s--, data);
auto R6502::writeSP(uint8 data) -> void {
write(0x0100 | regs.s--, data);
}
auto R6502::op_writezp(uint8 addr, uint8 data) -> void {
op_write(addr, data);
auto R6502::writeZP(uint8 addr, uint8 data) -> void {
write(addr, data);
}
//
auto R6502::op_page(uint16 x, uint16 y) -> void {
if((x & 0xff00) != (y & 0xff00)) op_read((x & 0xff00) | (y & 0x00ff));
auto R6502::ioPage(uint16 x, uint16 y) -> void {
if(x >> 8 == y >> 8) return;
read((x & 0xff00) | (y & 0x00ff));
}
auto R6502::op_page_always(uint16 x, uint16 y) -> void {
op_read((x & 0xff00) | (y & 0x00ff));
auto R6502::ioPageAlways(uint16 x, uint16 y) -> void {
read((x & 0xff00) | (y & 0x00ff));
}

View File

@ -3,12 +3,13 @@
namespace Processor {
#define I
#define L last_cycle();
#define L lastCycle();
#define call(op) (this->*op)()
#include "memory.cpp"
#include "algorithms.cpp"
#include "instructions.cpp"
#include "switch.cpp"
#include "disassembler.cpp"
#include "serialization.cpp"
@ -31,210 +32,20 @@ auto R6502::reset() -> void {
}
auto R6502::interrupt() -> void {
op_readpc();
op_readpc();
op_writesp(regs.pc >> 8);
op_writesp(regs.pc >> 0);
op_writesp(regs.p | 0x20);
io();
io();
writeSP(regs.pc >> 8);
writeSP(regs.pc >> 0);
writeSP(regs.p | 0x20);
uint16 vector = 0xfffe; //IRQ
nmi(vector);
abs.l = op_read(vector++);
abs.l = read(vector++);
regs.p.i = 1;
regs.p.d = 0;
L abs.h = op_read(vector++);
L abs.h = read(vector++);
regs.pc = abs.w;
}
auto R6502::exec() -> void {
uint8 opcode = op_readpci();
switch(opcode) {
case 0x00: return op_brk();
case 0x01: return opi_read_indirect_zero_page_x(&R6502::opf_ora);
I case 0x04: return opill_nop_zero_page();
case 0x05: return opi_read_zero_page(&R6502::opf_ora);
case 0x06: return opi_rmw_zero_page(&R6502::opf_asl);
case 0x08: return op_php();
case 0x09: return opi_read_immediate(&R6502::opf_ora);
case 0x0a: return opi_shift(&R6502::opf_sla);
I case 0x0c: return opill_nop_absolute();
case 0x0d: return opi_read_absolute(&R6502::opf_ora);
case 0x0e: return opi_rmw_absolute(&R6502::opf_asl);
case 0x10: return opi_branch(regs.p.n == 0);
case 0x11: return opi_read_indirect_zero_page_y(&R6502::opf_ora);
I case 0x14: return opill_nop_zero_page_x();
case 0x15: return opi_read_zero_page_x(&R6502::opf_ora);
case 0x16: return opi_rmw_zero_page_x(&R6502::opf_asl);
case 0x18: return opi_clear_flag(regs.p.c.bit);
case 0x19: return opi_read_absolute_y(&R6502::opf_ora);
I case 0x1a: return opill_nop_implied();
I case 0x1c: return opill_nop_absolute_x();
case 0x1d: return opi_read_absolute_x(&R6502::opf_ora);
case 0x1e: return opi_rmw_absolute_x(&R6502::opf_asl);
case 0x20: return op_jsr_absolute();
case 0x21: return opi_read_indirect_zero_page_x(&R6502::opf_and);
case 0x24: return opi_read_zero_page(&R6502::opf_bit);
case 0x25: return opi_read_zero_page(&R6502::opf_and);
case 0x26: return opi_rmw_zero_page(&R6502::opf_rol);
case 0x28: return op_plp();
case 0x29: return opi_read_immediate(&R6502::opf_and);
case 0x2a: return opi_shift(&R6502::opf_rla);
case 0x2c: return opi_read_absolute(&R6502::opf_bit);
case 0x2d: return opi_read_absolute(&R6502::opf_and);
case 0x2e: return opi_rmw_absolute(&R6502::opf_rol);
case 0x30: return opi_branch(regs.p.n == 1);
case 0x31: return opi_read_indirect_zero_page_y(&R6502::opf_and);
I case 0x34: return opill_nop_zero_page_x();
case 0x35: return opi_read_zero_page_x(&R6502::opf_and);
case 0x36: return opi_rmw_zero_page_x(&R6502::opf_rol);
case 0x38: return opi_set_flag(regs.p.c.bit);
case 0x39: return opi_read_absolute_y(&R6502::opf_and);
I case 0x3a: return opill_nop_implied();
I case 0x3c: return opill_nop_absolute_x();
case 0x3d: return opi_read_absolute_x(&R6502::opf_and);
case 0x3e: return opi_rmw_absolute_x(&R6502::opf_rol);
case 0x40: return op_rti();
case 0x41: return opi_read_indirect_zero_page_x(&R6502::opf_eor);
I case 0x44: return opill_nop_zero_page();
case 0x45: return opi_read_zero_page(&R6502::opf_eor);
case 0x46: return opi_rmw_zero_page(&R6502::opf_lsr);
case 0x48: return opi_push(regs.a);
case 0x49: return opi_read_immediate(&R6502::opf_eor);
case 0x4a: return opi_shift(&R6502::opf_sra);
case 0x4c: return op_jmp_absolute();
case 0x4d: return opi_read_absolute(&R6502::opf_eor);
case 0x4e: return opi_rmw_absolute(&R6502::opf_lsr);
case 0x50: return opi_branch(regs.p.v == 0);
case 0x51: return opi_read_indirect_zero_page_y(&R6502::opf_eor);
I case 0x54: return opill_nop_zero_page_x();
case 0x55: return opi_read_zero_page_x(&R6502::opf_eor);
case 0x56: return opi_rmw_zero_page_x(&R6502::opf_lsr);
case 0x58: return opi_clear_flag(regs.p.i.bit);
case 0x59: return opi_read_absolute_y(&R6502::opf_eor);
I case 0x5a: return opill_nop_implied();
I case 0x5c: return opill_nop_absolute_x();
case 0x5d: return opi_read_absolute_x(&R6502::opf_eor);
case 0x5e: return opi_rmw_absolute_x(&R6502::opf_lsr);
case 0x60: return op_rts();
case 0x61: return opi_read_indirect_zero_page_x(&R6502::opf_adc);
I case 0x64: return opill_nop_zero_page();
case 0x65: return opi_read_zero_page(&R6502::opf_adc);
case 0x66: return opi_rmw_zero_page(&R6502::opf_ror);
case 0x68: return opi_pull(regs.a);
case 0x69: return opi_read_immediate(&R6502::opf_adc);
case 0x6a: return opi_shift(&R6502::opf_rra);
I case 0x6b: return opill_arr_immediate();
case 0x6c: return op_jmp_indirect_absolute();
case 0x6d: return opi_read_absolute(&R6502::opf_adc);
case 0x6e: return opi_rmw_absolute(&R6502::opf_ror);
case 0x70: return opi_branch(regs.p.v == 1);
I case 0x74: return opill_nop_zero_page_x();
case 0x71: return opi_read_indirect_zero_page_y(&R6502::opf_adc);
case 0x75: return opi_read_zero_page_x(&R6502::opf_adc);
case 0x76: return opi_rmw_zero_page_x(&R6502::opf_ror);
case 0x78: return opi_set_flag(regs.p.i.bit);
case 0x79: return opi_read_absolute_y(&R6502::opf_adc);
I case 0x7a: return opill_nop_implied();
I case 0x7c: return opill_nop_absolute_x();
case 0x7d: return opi_read_absolute_x(&R6502::opf_adc);
case 0x7e: return opi_rmw_absolute_x(&R6502::opf_ror);
I case 0x80: return opill_nop_absolute();
case 0x81: return opi_store_indirect_zero_page_x(regs.a);
I case 0x82: return opill_nop_immediate();
case 0x84: return opi_store_zero_page(regs.y);
case 0x85: return opi_store_zero_page(regs.a);
case 0x86: return opi_store_zero_page(regs.x);
case 0x88: return opi_decrement(regs.y);
I case 0x89: return opill_nop_immediate();
case 0x8a: return opi_transfer(regs.x, regs.a, 1);
case 0x8c: return opi_store_absolute(regs.y);
case 0x8d: return opi_store_absolute(regs.a);
case 0x8e: return opi_store_absolute(regs.x);
case 0x90: return opi_branch(regs.p.c == 0);
case 0x91: return opi_store_indirect_zero_page_y(regs.a);
case 0x94: return opi_store_zero_page_x(regs.y);
case 0x95: return opi_store_zero_page_x(regs.a);
case 0x96: return opi_store_zero_page_y(regs.x);
case 0x98: return opi_transfer(regs.y, regs.a, 1);
case 0x99: return opi_store_absolute_y(regs.a);
case 0x9a: return opi_transfer(regs.x, regs.s, 0);
case 0x9d: return opi_store_absolute_x(regs.a);
case 0xa0: return opi_read_immediate(&R6502::opf_ldy);
case 0xa1: return opi_read_indirect_zero_page_x(&R6502::opf_lda);
case 0xa2: return opi_read_immediate(&R6502::opf_ldx);
case 0xa4: return opi_read_zero_page(&R6502::opf_ldy);
case 0xa5: return opi_read_zero_page(&R6502::opf_lda);
case 0xa6: return opi_read_zero_page(&R6502::opf_ldx);
case 0xa8: return opi_transfer(regs.a, regs.y, 1);
case 0xa9: return opi_read_immediate(&R6502::opf_lda);
case 0xaa: return opi_transfer(regs.a, regs.x, 1);
case 0xac: return opi_read_absolute(&R6502::opf_ldy);
case 0xad: return opi_read_absolute(&R6502::opf_lda);
case 0xae: return opi_read_absolute(&R6502::opf_ldx);
case 0xb0: return opi_branch(regs.p.c == 1);
case 0xb1: return opi_read_indirect_zero_page_y(&R6502::opf_lda);
case 0xb4: return opi_read_zero_page_x(&R6502::opf_ldy);
case 0xb5: return opi_read_zero_page_x(&R6502::opf_lda);
case 0xb6: return opi_read_zero_page_y(&R6502::opf_ldx);
case 0xb8: return opi_clear_flag(regs.p.v.bit);
case 0xb9: return opi_read_absolute_y(&R6502::opf_lda);
case 0xba: return opi_transfer(regs.s, regs.x, 1);
case 0xbc: return opi_read_absolute_x(&R6502::opf_ldy);
case 0xbd: return opi_read_absolute_x(&R6502::opf_lda);
case 0xbe: return opi_read_absolute_y(&R6502::opf_ldx);
case 0xc0: return opi_read_immediate(&R6502::opf_cpy);
case 0xc1: return opi_read_indirect_zero_page_x(&R6502::opf_cmp);
I case 0xc2: return opill_nop_immediate();
case 0xc4: return opi_read_zero_page(&R6502::opf_cpy);
case 0xc5: return opi_read_zero_page(&R6502::opf_cmp);
case 0xc6: return opi_rmw_zero_page(&R6502::opf_dec);
case 0xc8: return opi_increment(regs.y);
case 0xc9: return opi_read_immediate(&R6502::opf_cmp);
case 0xca: return opi_decrement(regs.x);
case 0xcc: return opi_read_absolute(&R6502::opf_cpy);
case 0xcd: return opi_read_absolute(&R6502::opf_cmp);
case 0xce: return opi_rmw_absolute(&R6502::opf_dec);
case 0xd0: return opi_branch(regs.p.z == 0);
case 0xd1: return opi_read_indirect_zero_page_y(&R6502::opf_cmp);
I case 0xd4: return opill_nop_zero_page_x();
case 0xd5: return opi_read_zero_page_x(&R6502::opf_cmp);
case 0xd6: return opi_rmw_zero_page_x(&R6502::opf_dec);
case 0xd8: return opi_clear_flag(regs.p.d.bit);
case 0xd9: return opi_read_absolute_y(&R6502::opf_cmp);
I case 0xda: return opill_nop_implied();
I case 0xdc: return opill_nop_absolute_x();
case 0xdd: return opi_read_absolute_x(&R6502::opf_cmp);
case 0xde: return opi_rmw_absolute_x(&R6502::opf_dec);
case 0xe0: return opi_read_immediate(&R6502::opf_cpx);
case 0xe1: return opi_read_indirect_zero_page_x(&R6502::opf_sbc);
I case 0xe2: return opill_nop_immediate();
case 0xe4: return opi_read_zero_page(&R6502::opf_cpx);
case 0xe5: return opi_read_zero_page(&R6502::opf_sbc);
case 0xe6: return opi_rmw_zero_page(&R6502::opf_inc);
case 0xe8: return opi_increment(regs.x);
case 0xe9: return opi_read_immediate(&R6502::opf_sbc);
case 0xea: return op_nop();
I case 0xeb: return opi_read_immediate(&R6502::opf_sbc);
case 0xec: return opi_read_absolute(&R6502::opf_cpx);
case 0xed: return opi_read_absolute(&R6502::opf_sbc);
case 0xee: return opi_rmw_absolute(&R6502::opf_inc);
case 0xf0: return opi_branch(regs.p.z == 1);
case 0xf1: return opi_read_indirect_zero_page_y(&R6502::opf_sbc);
I case 0xf4: return opill_nop_zero_page_x();
case 0xf5: return opi_read_zero_page_x(&R6502::opf_sbc);
case 0xf6: return opi_rmw_zero_page_x(&R6502::opf_inc);
case 0xf8: return opi_set_flag(regs.p.d.bit);
case 0xf9: return opi_read_absolute_y(&R6502::opf_sbc);
I case 0xfa: return opill_nop_implied();
I case 0xfc: return opill_nop_absolute_x();
case 0xfd: return opi_read_absolute_x(&R6502::opf_sbc);
case 0xfe: return opi_rmw_absolute_x(&R6502::opf_inc);
}
//unimplemented opcode
return op_nop();
}
#undef I
#undef L
#undef call

View File

@ -7,88 +7,87 @@
namespace Processor {
struct R6502 {
virtual auto op_read(uint16 addr) -> uint8 = 0;
virtual auto op_write(uint16 addr, uint8 data) -> void = 0;
virtual auto last_cycle() -> void = 0;
virtual auto read(uint16 addr) -> uint8 = 0;
virtual auto write(uint16 addr, uint8 data) -> void = 0;
virtual auto lastCycle() -> void = 0;
virtual auto nmi(uint16& vector) -> void = 0;
virtual auto debugger_read(uint16 addr) -> uint8 { return 0u; }
virtual auto readDebugger(uint16 addr) -> uint8 { return 0u; }
auto mdr() const -> uint8;
auto power() -> void;
auto reset() -> void;
auto interrupt() -> void;
auto exec() -> void;
auto instruction() -> void;
auto serialize(serializer&) -> void;
//memory.cpp
auto op_readpc() -> uint8;
auto op_readpci() -> uint8;
auto op_readsp() -> uint8;
auto op_readzp(uint8 addr) -> uint8;
auto io() -> uint8;
auto readPC() -> uint8;
auto readSP() -> uint8;
auto readZP(uint8 addr) -> uint8;
auto op_writesp(uint8 data) -> void;
auto op_writezp(uint8 addr, uint8 data) -> void;
auto writeSP(uint8 data) -> void;
auto writeZP(uint8 addr, uint8 data) -> void;
auto op_page(uint16 x, uint16 y) -> void;
auto op_page_always(uint16 x, uint16 y) -> void;
auto ioPage(uint16 x, uint16 y) -> void;
auto ioPageAlways(uint16 x, uint16 y) -> void;
//instructions.cpp
auto opf_asl();
auto opf_adc();
auto opf_and();
auto opf_bit();
auto opf_cmp();
auto opf_cpx();
auto opf_cpy();
auto opf_dec();
auto opf_eor();
auto opf_inc();
auto opf_lda();
auto opf_ldx();
auto opf_ldy();
auto opf_lsr();
auto opf_ora();
auto opf_rla();
auto opf_rol();
auto opf_ror();
auto opf_rra();
auto opf_sbc();
auto opf_sla();
auto opf_sra();
using fp = auto (R6502::*)() -> void;
auto fp_asl();
auto fp_adc();
auto fp_and();
auto fp_bit();
auto fp_cmp();
auto fp_cpx();
auto fp_cpy();
auto fp_dec();
auto fp_eor();
auto fp_inc();
auto fp_lda();
auto fp_ldx();
auto fp_ldy();
auto fp_lsr();
auto fp_ora();
auto fp_rla();
auto fp_rol();
auto fp_ror();
auto fp_rra();
auto fp_sbc();
auto fp_sla();
auto fp_sra();
auto opi_branch(bool condition);
auto opi_clear_flag(uint bit);
auto opi_decrement(uint8& r);
auto opi_increment(uint8& r);
auto opi_pull(uint8& r);
auto opi_push(uint8& r);
auto opi_read_absolute(fp);
auto opi_read_absolute_x(fp);
auto opi_read_absolute_y(fp);
auto opi_read_immediate(fp);
auto opi_read_indirect_zero_page_x(fp);
auto opi_read_indirect_zero_page_y(fp);
auto opi_read_zero_page(fp);
auto opi_read_zero_page_x(fp);
auto opi_read_zero_page_y(fp);
auto opi_rmw_absolute(fp);
auto opi_rmw_absolute_x(fp);
auto opi_rmw_zero_page(fp);
auto opi_rmw_zero_page_x(fp);
auto opi_set_flag(uint bit);
auto opi_shift(fp);
auto opi_store_absolute(uint8& r);
auto opi_store_absolute_x(uint8& r);
auto opi_store_absolute_y(uint8& r);
auto opi_store_indirect_zero_page_x(uint8& r);
auto opi_store_indirect_zero_page_y(uint8& r);
auto opi_store_zero_page(uint8& r);
auto opi_store_zero_page_x(uint8& r);
auto opi_store_zero_page_y(uint8& r);
auto opi_transfer(uint8& s, uint8& d, bool flag);
auto op_branch(bool condition);
auto op_clear_flag(uint bit);
auto op_decrement(uint8& r);
auto op_increment(uint8& r);
auto op_pull(uint8& r);
auto op_push(uint8& r);
auto op_read_absolute(fp);
auto op_read_absolute_x(fp);
auto op_read_absolute_y(fp);
auto op_read_immediate(fp);
auto op_read_indirect_zero_page_x(fp);
auto op_read_indirect_zero_page_y(fp);
auto op_read_zero_page(fp);
auto op_read_zero_page_x(fp);
auto op_read_zero_page_y(fp);
auto op_rmw_absolute(fp);
auto op_rmw_absolute_x(fp);
auto op_rmw_zero_page(fp);
auto op_rmw_zero_page_x(fp);
auto op_set_flag(uint bit);
auto op_shift(fp);
auto op_store_absolute(uint8& r);
auto op_store_absolute_x(uint8& r);
auto op_store_absolute_y(uint8& r);
auto op_store_indirect_zero_page_x(uint8& r);
auto op_store_indirect_zero_page_y(uint8& r);
auto op_store_zero_page(uint8& r);
auto op_store_zero_page_x(uint8& r);
auto op_store_zero_page_y(uint8& r);
auto op_transfer(uint8& s, uint8& d, bool flag);
auto op_brk();
auto op_jmp_absolute();
@ -100,13 +99,13 @@ struct R6502 {
auto op_rti();
auto op_rts();
auto opill_arr_immediate();
auto opill_nop_absolute();
auto opill_nop_absolute_x();
auto opill_nop_immediate();
auto opill_nop_implied();
auto opill_nop_zero_page();
auto opill_nop_zero_page_x();
auto op_arr_immediate();
auto op_nop_absolute();
auto op_nop_absolute_x();
auto op_nop_immediate();
auto op_nop_implied();
auto op_nop_zero_page();
auto op_nop_zero_page_x();
//disassembler.cpp
auto disassemble() -> string;

View File

@ -0,0 +1,195 @@
#define I //prefix highlights illegal instructions
#define op(id, name, ...) case id: return op_##name(__VA_ARGS__);
#define fp(name) &R6502::fp_##name
auto R6502::instruction() -> void {
switch(readPC()) {
op(0x00, brk)
op(0x01, read_indirect_zero_page_x, fp(ora))
I op(0x04, nop_zero_page)
op(0x05, read_zero_page, fp(ora))
op(0x06, rmw_zero_page, fp(asl))
op(0x08, php)
op(0x09, read_immediate, fp(ora))
op(0x0a, shift, fp(sla))
I op(0x0c, nop_absolute)
op(0x0d, read_absolute, fp(ora))
op(0x0e, rmw_absolute, fp(asl))
op(0x10, branch, regs.p.n == 0)
op(0x11, read_indirect_zero_page_y, fp(ora))
I op(0x14, nop_zero_page_x)
op(0x15, read_zero_page_x, fp(ora))
op(0x16, rmw_zero_page_x, fp(asl))
op(0x18, clear_flag, regs.p.c.bit)
op(0x19, read_absolute_y, fp(ora))
I op(0x1a, nop_implied)
I op(0x1c, nop_absolute_x)
op(0x1d, read_absolute_x, fp(ora))
op(0x1e, rmw_absolute_x, fp(asl))
op(0x20, jsr_absolute)
op(0x21, read_indirect_zero_page_x, fp(and))
op(0x24, read_zero_page, fp(bit))
op(0x25, read_zero_page, fp(and))
op(0x26, rmw_zero_page, fp(rol))
op(0x28, plp)
op(0x29, read_immediate, fp(and))
op(0x2a, shift, fp(rla))
op(0x2c, read_absolute, fp(bit))
op(0x2d, read_absolute, fp(and))
op(0x2e, rmw_absolute, fp(rol))
op(0x30, branch, regs.p.n == 1)
op(0x31, read_indirect_zero_page_y, fp(and))
I op(0x34, nop_zero_page_x)
op(0x35, read_zero_page_x, fp(and))
op(0x36, rmw_zero_page_x, fp(rol))
op(0x38, set_flag, regs.p.c.bit)
op(0x39, read_absolute_y, fp(and))
I op(0x3a, nop_implied)
I op(0x3c, nop_absolute_x)
op(0x3d, read_absolute_x, fp(and))
op(0x3e, rmw_absolute_x, fp(rol))
op(0x40, rti)
op(0x41, read_indirect_zero_page_x, fp(eor))
I op(0x44, nop_zero_page)
op(0x45, read_zero_page, fp(eor))
op(0x46, rmw_zero_page, fp(lsr))
op(0x48, push, regs.a)
op(0x49, read_immediate, fp(eor))
op(0x4a, shift, fp(sra))
op(0x4c, jmp_absolute)
op(0x4d, read_absolute, fp(eor))
op(0x4e, rmw_absolute, fp(lsr))
op(0x50, branch, regs.p.v == 0)
op(0x51, read_indirect_zero_page_y, fp(eor))
I op(0x54, nop_zero_page_x)
op(0x55, read_zero_page_x, fp(eor))
op(0x56, rmw_zero_page_x, fp(lsr))
op(0x58, clear_flag, regs.p.i.bit)
op(0x59, read_absolute_y, fp(eor))
I op(0x5a, nop_implied)
I op(0x5c, nop_absolute_x)
op(0x5d, read_absolute_x, fp(eor))
op(0x5e, rmw_absolute_x, fp(lsr))
op(0x60, rts)
op(0x61, read_indirect_zero_page_x, fp(adc))
I op(0x64, nop_zero_page)
op(0x65, read_zero_page, fp(adc))
op(0x66, rmw_zero_page, fp(ror))
op(0x68, pull, regs.a)
op(0x69, read_immediate, fp(adc))
op(0x6a, shift, fp(rra))
I op(0x6b, arr_immediate)
op(0x6c, jmp_indirect_absolute)
op(0x6d, read_absolute, fp(adc))
op(0x6e, rmw_absolute, fp(ror))
op(0x70, branch, regs.p.v == 1)
op(0x71, read_indirect_zero_page_y, fp(adc))
I op(0x74, nop_zero_page_x)
op(0x75, read_zero_page_x, fp(adc))
op(0x76, rmw_zero_page_x, fp(ror))
op(0x78, set_flag, regs.p.i.bit)
op(0x79, read_absolute_y, fp(adc))
I op(0x7a, nop_implied)
I op(0x7c, nop_absolute_x)
op(0x7d, read_absolute_x, fp(adc))
op(0x7e, rmw_absolute_x, fp(ror))
I case 0x80: return op_nop_absolute();
case 0x81: return op_store_indirect_zero_page_x(regs.a);
I case 0x82: return op_nop_immediate();
case 0x84: return op_store_zero_page(regs.y);
case 0x85: return op_store_zero_page(regs.a);
case 0x86: return op_store_zero_page(regs.x);
case 0x88: return op_decrement(regs.y);
I case 0x89: return op_nop_immediate();
case 0x8a: return op_transfer(regs.x, regs.a, 1);
case 0x8c: return op_store_absolute(regs.y);
case 0x8d: return op_store_absolute(regs.a);
case 0x8e: return op_store_absolute(regs.x);
case 0x90: return op_branch(regs.p.c == 0);
case 0x91: return op_store_indirect_zero_page_y(regs.a);
case 0x94: return op_store_zero_page_x(regs.y);
case 0x95: return op_store_zero_page_x(regs.a);
case 0x96: return op_store_zero_page_y(regs.x);
case 0x98: return op_transfer(regs.y, regs.a, 1);
case 0x99: return op_store_absolute_y(regs.a);
case 0x9a: return op_transfer(regs.x, regs.s, 0);
case 0x9d: return op_store_absolute_x(regs.a);
case 0xa0: return op_read_immediate(fp(ldy));
case 0xa1: return op_read_indirect_zero_page_x(fp(lda));
case 0xa2: return op_read_immediate(fp(ldx));
case 0xa4: return op_read_zero_page(fp(ldy));
case 0xa5: return op_read_zero_page(fp(lda));
case 0xa6: return op_read_zero_page(fp(ldx));
case 0xa8: return op_transfer(regs.a, regs.y, 1);
case 0xa9: return op_read_immediate(fp(lda));
case 0xaa: return op_transfer(regs.a, regs.x, 1);
case 0xac: return op_read_absolute(fp(ldy));
case 0xad: return op_read_absolute(fp(lda));
case 0xae: return op_read_absolute(fp(ldx));
case 0xb0: return op_branch(regs.p.c == 1);
case 0xb1: return op_read_indirect_zero_page_y(fp(lda));
case 0xb4: return op_read_zero_page_x(fp(ldy));
case 0xb5: return op_read_zero_page_x(fp(lda));
case 0xb6: return op_read_zero_page_y(fp(ldx));
case 0xb8: return op_clear_flag(regs.p.v.bit);
case 0xb9: return op_read_absolute_y(fp(lda));
case 0xba: return op_transfer(regs.s, regs.x, 1);
case 0xbc: return op_read_absolute_x(fp(ldy));
case 0xbd: return op_read_absolute_x(fp(lda));
case 0xbe: return op_read_absolute_y(fp(ldx));
case 0xc0: return op_read_immediate(fp(cpy));
case 0xc1: return op_read_indirect_zero_page_x(fp(cmp));
I case 0xc2: return op_nop_immediate();
case 0xc4: return op_read_zero_page(fp(cpy));
case 0xc5: return op_read_zero_page(fp(cmp));
case 0xc6: return op_rmw_zero_page(fp(dec));
case 0xc8: return op_increment(regs.y);
case 0xc9: return op_read_immediate(fp(cmp));
case 0xca: return op_decrement(regs.x);
case 0xcc: return op_read_absolute(fp(cpy));
case 0xcd: return op_read_absolute(fp(cmp));
case 0xce: return op_rmw_absolute(fp(dec));
case 0xd0: return op_branch(regs.p.z == 0);
case 0xd1: return op_read_indirect_zero_page_y(fp(cmp));
I case 0xd4: return op_nop_zero_page_x();
case 0xd5: return op_read_zero_page_x(fp(cmp));
case 0xd6: return op_rmw_zero_page_x(fp(dec));
case 0xd8: return op_clear_flag(regs.p.d.bit);
case 0xd9: return op_read_absolute_y(fp(cmp));
I case 0xda: return op_nop_implied();
I case 0xdc: return op_nop_absolute_x();
case 0xdd: return op_read_absolute_x(fp(cmp));
case 0xde: return op_rmw_absolute_x(fp(dec));
case 0xe0: return op_read_immediate(fp(cpx));
case 0xe1: return op_read_indirect_zero_page_x(fp(sbc));
I case 0xe2: return op_nop_immediate();
case 0xe4: return op_read_zero_page(fp(cpx));
case 0xe5: return op_read_zero_page(fp(sbc));
case 0xe6: return op_rmw_zero_page(fp(inc));
case 0xe8: return op_increment(regs.x);
case 0xe9: return op_read_immediate(fp(sbc));
case 0xea: return op_nop();
I case 0xeb: return op_read_immediate(fp(sbc));
case 0xec: return op_read_absolute(fp(cpx));
case 0xed: return op_read_absolute(fp(sbc));
case 0xee: return op_rmw_absolute(fp(inc));
case 0xf0: return op_branch(regs.p.z == 1);
case 0xf1: return op_read_indirect_zero_page_y(fp(sbc));
I case 0xf4: return op_nop_zero_page_x();
case 0xf5: return op_read_zero_page_x(fp(sbc));
case 0xf6: return op_rmw_zero_page_x(fp(inc));
case 0xf8: return op_set_flag(regs.p.d.bit);
case 0xf9: return op_read_absolute_y(fp(sbc));
I case 0xfa: return op_nop_implied();
I case 0xfc: return op_nop_absolute_x();
case 0xfd: return op_read_absolute_x(fp(sbc));
case 0xfe: return op_rmw_absolute_x(fp(inc));
}
//unimplemented opcode
return op_nop();
}
#undef I
#undef op
#undef fp