mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
44a8c5a2b4
commit
40abcfc4a5
|
@ -9,7 +9,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 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/";
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
processors += r6502
|
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-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||||
objects += fc-cheat
|
objects += fc-cheat
|
||||||
|
|
||||||
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
|
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-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
|
||||||
obj/fc-scheduler.o: fc/scheduler/scheduler.cpp $(call rwildcard,fc/scheduler/)
|
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-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
|
||||||
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
|
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
|
||||||
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
|
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
|
||||||
|
|
|
@ -68,7 +68,7 @@ auto APU::tick() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::set_irq_line() -> 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 {
|
auto APU::set_sample(int16 sample) -> void {
|
||||||
|
@ -109,8 +109,10 @@ auto APU::reset() -> void {
|
||||||
set_irq_line();
|
set_irq_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::read(uint16 addr) -> uint8 {
|
auto APU::readIO(uint16 addr) -> uint8 {
|
||||||
if(addr == 0x4015) {
|
switch(addr) {
|
||||||
|
|
||||||
|
case 0x4015: {
|
||||||
uint8 result = 0x00;
|
uint8 result = 0x00;
|
||||||
result |= pulse[0].length_counter ? 0x01 : 0;
|
result |= pulse[0].length_counter ? 0x01 : 0;
|
||||||
result |= pulse[1].length_counter ? 0x02 : 0;
|
result |= pulse[1].length_counter ? 0x02 : 0;
|
||||||
|
@ -126,34 +128,40 @@ auto APU::read(uint16 addr) -> uint8 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return cpu.mdr();
|
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#
|
const uint n = (addr >> 2) & 1; //pulse#
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x4000: case 0x4004:
|
|
||||||
|
case 0x4000: case 0x4004: {
|
||||||
pulse[n].duty = data >> 6;
|
pulse[n].duty = data >> 6;
|
||||||
pulse[n].envelope.loop_mode = data & 0x20;
|
pulse[n].envelope.loop_mode = data & 0x20;
|
||||||
pulse[n].envelope.use_speed_as_volume = data & 0x10;
|
pulse[n].envelope.use_speed_as_volume = data & 0x10;
|
||||||
pulse[n].envelope.speed = data & 0x0f;
|
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.enable = data & 0x80;
|
||||||
pulse[n].sweep.period = (data & 0x70) >> 4;
|
pulse[n].sweep.period = (data & 0x70) >> 4;
|
||||||
pulse[n].sweep.decrement = data & 0x08;
|
pulse[n].sweep.decrement = data & 0x08;
|
||||||
pulse[n].sweep.shift = data & 0x07;
|
pulse[n].sweep.shift = data & 0x07;
|
||||||
pulse[n].sweep.reload = true;
|
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].period = (pulse[n].period & 0x0700) | (data << 0);
|
||||||
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_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].period = (pulse[n].period & 0x00ff) | (data << 8);
|
||||||
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_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)) {
|
if(enabled_channels & (1 << n)) {
|
||||||
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
|
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4008:
|
case 0x4008: {
|
||||||
triangle.halt_length_counter = data & 0x80;
|
triangle.halt_length_counter = data & 0x80;
|
||||||
triangle.linear_length = data & 0x7f;
|
triangle.linear_length = data & 0x7f;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x400a:
|
case 0x400a: {
|
||||||
triangle.period = (triangle.period & 0x0700) | (data << 0);
|
triangle.period = (triangle.period & 0x0700) | (data << 0);
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x400b:
|
case 0x400b: {
|
||||||
triangle.period = (triangle.period & 0x00ff) | (data << 8);
|
triangle.period = (triangle.period & 0x00ff) | (data << 8);
|
||||||
|
|
||||||
triangle.reload_linear = true;
|
triangle.reload_linear = true;
|
||||||
|
@ -182,49 +193,57 @@ auto APU::write(uint16 addr, uint8 data) -> void {
|
||||||
if(enabled_channels & (1 << 2)) {
|
if(enabled_channels & (1 << 2)) {
|
||||||
triangle.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
triangle.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x400c:
|
case 0x400c: {
|
||||||
noise.envelope.loop_mode = data & 0x20;
|
noise.envelope.loop_mode = data & 0x20;
|
||||||
noise.envelope.use_speed_as_volume = data & 0x10;
|
noise.envelope.use_speed_as_volume = data & 0x10;
|
||||||
noise.envelope.speed = data & 0x0f;
|
noise.envelope.speed = data & 0x0f;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x400e:
|
case 0x400e: {
|
||||||
noise.short_mode = data & 0x80;
|
noise.short_mode = data & 0x80;
|
||||||
noise.period = data & 0x0f;
|
noise.period = data & 0x0f;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x400f:
|
case 0x400f: {
|
||||||
noise.envelope.reload_decay = true;
|
noise.envelope.reload_decay = true;
|
||||||
|
|
||||||
if(enabled_channels & (1 << 3)) {
|
if(enabled_channels & (1 << 3)) {
|
||||||
noise.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
noise.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4010:
|
case 0x4010: {
|
||||||
dmc.irq_enable = data & 0x80;
|
dmc.irq_enable = data & 0x80;
|
||||||
dmc.loop_mode = data & 0x40;
|
dmc.loop_mode = data & 0x40;
|
||||||
dmc.period = data & 0x0f;
|
dmc.period = data & 0x0f;
|
||||||
|
|
||||||
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
|
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
|
||||||
set_irq_line();
|
set_irq_line();
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4011:
|
case 0x4011: {
|
||||||
dmc.dac_latch = data & 0x7f;
|
dmc.dac_latch = data & 0x7f;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4012:
|
case 0x4012: {
|
||||||
dmc.addr_latch = data;
|
dmc.addr_latch = data;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4013:
|
case 0x4013: {
|
||||||
dmc.length_latch = data;
|
dmc.length_latch = data;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4015:
|
case 0x4015: {
|
||||||
if((data & 0x01) == 0) pulse[0].length_counter = 0;
|
if((data & 0x01) == 0) pulse[0].length_counter = 0;
|
||||||
if((data & 0x02) == 0) pulse[1].length_counter = 0;
|
if((data & 0x02) == 0) pulse[1].length_counter = 0;
|
||||||
if((data & 0x04) == 0) triangle.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();
|
set_irq_line();
|
||||||
enabled_channels = data & 0x1f;
|
enabled_channels = data & 0x1f;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x4017:
|
case 0x4017: {
|
||||||
frame.mode = data >> 6;
|
frame.mode = data >> 6;
|
||||||
|
|
||||||
frame.counter = 0;
|
frame.counter = 0;
|
||||||
|
@ -247,7 +267,9 @@ auto APU::write(uint16 addr, uint8 data) -> void {
|
||||||
set_irq_line();
|
set_irq_line();
|
||||||
}
|
}
|
||||||
frame.divider = FrameCounter::NtscPeriod;
|
frame.divider = FrameCounter::NtscPeriod;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ struct APU : Thread {
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
|
|
||||||
auto read(uint16 addr) -> uint8;
|
auto readIO(uint16 addr) -> uint8;
|
||||||
auto write(uint16 addr, uint8 data) -> void;
|
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ auto APU::DMC::start() -> void {
|
||||||
auto APU::DMC::stop() -> void {
|
auto APU::DMC::stop() -> void {
|
||||||
length_counter = 0;
|
length_counter = 0;
|
||||||
dma_delay_counter = 0;
|
dma_delay_counter = 0;
|
||||||
cpu.set_rdy_line(1);
|
cpu.rdyLine(1);
|
||||||
cpu.set_rdy_addr(false);
|
cpu.rdyAddr(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::DMC::clock() -> uint8 {
|
auto APU::DMC::clock() -> uint8 {
|
||||||
|
@ -19,10 +19,10 @@ auto APU::DMC::clock() -> uint8 {
|
||||||
dma_delay_counter--;
|
dma_delay_counter--;
|
||||||
|
|
||||||
if(dma_delay_counter == 1) {
|
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) {
|
} else if(dma_delay_counter == 0) {
|
||||||
cpu.set_rdy_line(1);
|
cpu.rdyLine(1);
|
||||||
cpu.set_rdy_addr(false);
|
cpu.rdyAddr(false);
|
||||||
|
|
||||||
dma_buffer = cpu.mdr();
|
dma_buffer = cpu.mdr();
|
||||||
have_dma_buffer = true;
|
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) {
|
if(length_counter > 0 && have_dma_buffer == false && dma_delay_counter == 0) {
|
||||||
cpu.set_rdy_line(0);
|
cpu.rdyLine(0);
|
||||||
dma_delay_counter = 4;
|
dma_delay_counter = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct BandaiFCG : Board {
|
||||||
auto main() -> void {
|
auto main() -> void {
|
||||||
if(irq_counter_enable) {
|
if(irq_counter_enable) {
|
||||||
if(--irq_counter == 0xffff) {
|
if(--irq_counter == 0xffff) {
|
||||||
cpu.set_irq_line(1);
|
cpu.irqLine(1);
|
||||||
irq_counter_enable = false;
|
irq_counter_enable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ struct BandaiFCG : Board {
|
||||||
mirror = data & 0x03;
|
mirror = data & 0x03;
|
||||||
break;
|
break;
|
||||||
case 0x0a:
|
case 0x0a:
|
||||||
cpu.set_irq_line(0);
|
cpu.irqLine(0);
|
||||||
irq_counter_enable = data & 0x01;
|
irq_counter_enable = data & 0x01;
|
||||||
irq_counter = irq_latch;
|
irq_counter = irq_latch;
|
||||||
break;
|
break;
|
||||||
|
@ -58,7 +58,7 @@ struct BandaiFCG : Board {
|
||||||
irq_latch = (irq_latch & 0x00ff) | (data << 8);
|
irq_latch = (irq_latch & 0x00ff) | (data << 8);
|
||||||
break;
|
break;
|
||||||
case 0x0d:
|
case 0x0d:
|
||||||
//TODO: serial EEPROM support
|
//todo: serial EEPROM support
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct Sunsoft5B : Board {
|
||||||
auto main() -> void {
|
auto main() -> void {
|
||||||
if(irq_counter_enable) {
|
if(irq_counter_enable) {
|
||||||
if(--irq_counter == 0xffff) {
|
if(--irq_counter == 0xffff) {
|
||||||
cpu.set_irq_line(irq_enable);
|
cpu.irqLine(irq_enable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ struct Sunsoft5B : Board {
|
||||||
case 13:
|
case 13:
|
||||||
irq_enable = data & 0x80;
|
irq_enable = data & 0x80;
|
||||||
irq_counter_enable = data & 0x01;
|
irq_counter_enable = data & 0x01;
|
||||||
if(irq_enable == 0) cpu.set_irq_line(0);
|
if(irq_enable == 0) cpu.irqLine(0);
|
||||||
break;
|
break;
|
||||||
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
|
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
|
||||||
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;
|
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct MMC3 : Chip {
|
||||||
|
|
||||||
auto main() -> void {
|
auto main() -> void {
|
||||||
if(irq_delay) irq_delay--;
|
if(irq_delay) irq_delay--;
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct MMC5 : Chip {
|
||||||
//scanline() resets this; if no scanlines detected, enter video blanking period
|
//scanline() resets this; if no scanlines detected, enter video blanking period
|
||||||
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank 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();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct MMC6 : Chip {
|
||||||
|
|
||||||
auto main() -> void {
|
auto main() -> void {
|
||||||
if(irq_delay) irq_delay--;
|
if(irq_delay) irq_delay--;
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct VRC3 : Chip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct VRC4 : Chip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ struct VRC6 : Chip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
|
|
||||||
pulse1.clock();
|
pulse1.clock();
|
||||||
pulse2.clock();
|
pulse2.clock();
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct VRC7 : Chip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.irqLine(irq_line);
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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"
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Famicom {
|
namespace Famicom {
|
||||||
|
|
||||||
|
#include "memory.cpp"
|
||||||
#include "timing.cpp"
|
#include "timing.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
|
@ -11,19 +12,24 @@ auto CPU::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::main() -> void {
|
auto CPU::main() -> void {
|
||||||
if(status.interrupt_pending) return interrupt();
|
if(io.interruptPending) return interrupt();
|
||||||
exec();
|
instruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::add_clocks(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
apu.clock -= clocks;
|
apu.clock -= clocks;
|
||||||
if(apu.clock < 0 && !scheduler.synchronizing()) co_switch(apu.thread);
|
if(apu.clock < 0) co_switch(apu.thread);
|
||||||
|
|
||||||
ppu.clock -= clocks;
|
ppu.clock -= clocks;
|
||||||
if(ppu.clock < 0 && !scheduler.synchronizing()) co_switch(ppu.thread);
|
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||||
|
|
||||||
cartridge.clock -= clocks;
|
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 {
|
auto CPU::power() -> void {
|
||||||
|
@ -43,59 +49,8 @@ auto CPU::reset() -> void {
|
||||||
regs.pc = bus.read(0xfffc) << 0;
|
regs.pc = bus.read(0xfffc) << 0;
|
||||||
regs.pc |= bus.read(0xfffd) << 8;
|
regs.pc |= bus.read(0xfffd) << 8;
|
||||||
|
|
||||||
status.interrupt_pending = false;
|
memory::fill(&io, sizeof(IO));
|
||||||
status.nmi_pending = false;
|
io.rdyLine = 1;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,56 @@
|
||||||
struct CPU : Processor::R6502, Thread {
|
struct CPU : Processor::R6502, Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto add_clocks(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> 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 readIO(uint16 addr) -> uint8;
|
||||||
auto ram_write(uint16 addr, uint8 data) -> void;
|
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
auto read(uint16 addr) -> uint8;
|
auto readDebugger(uint16 addr) -> uint8 override;
|
||||||
auto write(uint16 addr, uint8 data) -> void;
|
|
||||||
|
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
//timing.cpp
|
//timing.cpp
|
||||||
auto op_read(uint16 addr) -> uint8;
|
auto read(uint16 addr) -> uint8 override;
|
||||||
auto op_write(uint16 addr, uint8 data) -> void;
|
auto write(uint16 addr, uint8 data) -> void override;
|
||||||
auto last_cycle() -> void;
|
auto lastCycle() -> void override;
|
||||||
auto nmi(uint16& vector) -> void;
|
auto nmi(uint16& vector) -> void override;
|
||||||
|
|
||||||
auto oam_dma() -> void;
|
auto oamdma() -> void;
|
||||||
|
|
||||||
auto set_nmi_line(bool) -> void;
|
auto nmiLine(bool) -> void;
|
||||||
auto set_irq_line(bool) -> void;
|
auto irqLine(bool) -> void;
|
||||||
auto set_irq_apu_line(bool) -> void;
|
auto apuLine(bool) -> void;
|
||||||
|
|
||||||
auto set_rdy_line(bool) -> void;
|
auto rdyLine(bool) -> void;
|
||||||
auto set_rdy_addr(bool valid, uint16 value = 0) -> void;
|
auto rdyAddr(bool valid, uint16 value = 0) -> void;
|
||||||
|
|
||||||
//protected:
|
//protected:
|
||||||
|
vector<Thread*> peripherals;
|
||||||
|
|
||||||
uint8 ram[0x0800];
|
uint8 ram[0x0800];
|
||||||
|
|
||||||
struct Status {
|
struct IO {
|
||||||
bool interrupt_pending;
|
bool interruptPending;
|
||||||
bool nmi_pending;
|
bool nmiPending;
|
||||||
bool nmi_line;
|
bool nmiLine;
|
||||||
bool irq_line;
|
bool irqLine;
|
||||||
bool irq_apu_line;
|
bool apuLine;
|
||||||
|
|
||||||
bool rdy_line;
|
bool rdyLine;
|
||||||
bool rdy_addr_valid;
|
bool rdyAddrValid;
|
||||||
uint16 rdy_addr_value;
|
uint16 rdyAddrValue;
|
||||||
|
|
||||||
bool oam_dma_pending;
|
bool oamdmaPending;
|
||||||
uint8 oam_dma_page;
|
uint8 oamdmaPage;
|
||||||
|
} io;
|
||||||
bool controller_latch;
|
|
||||||
uint controller_port0;
|
|
||||||
uint controller_port1;
|
|
||||||
} status;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -4,20 +4,16 @@ auto CPU::serialize(serializer& s) -> void {
|
||||||
|
|
||||||
s.array(ram);
|
s.array(ram);
|
||||||
|
|
||||||
s.integer(status.interrupt_pending);
|
s.integer(io.interruptPending);
|
||||||
s.integer(status.nmi_pending);
|
s.integer(io.nmiPending);
|
||||||
s.integer(status.nmi_line);
|
s.integer(io.nmiLine);
|
||||||
s.integer(status.irq_line);
|
s.integer(io.irqLine);
|
||||||
s.integer(status.irq_apu_line);
|
s.integer(io.apuLine);
|
||||||
|
|
||||||
s.integer(status.rdy_line);
|
s.integer(io.rdyLine);
|
||||||
s.integer(status.rdy_addr_valid);
|
s.integer(io.rdyAddrValid);
|
||||||
s.integer(status.rdy_addr_value);
|
s.integer(io.rdyAddrValue);
|
||||||
|
|
||||||
s.integer(status.oam_dma_pending);
|
s.integer(io.oamdmaPending);
|
||||||
s.integer(status.oam_dma_page);
|
s.integer(io.oamdmaPage);
|
||||||
|
|
||||||
s.integer(status.controller_latch);
|
|
||||||
s.integer(status.controller_port0);
|
|
||||||
s.integer(status.controller_port1);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,64 @@
|
||||||
auto CPU::op_read(uint16 addr) -> uint8 {
|
auto CPU::read(uint16 addr) -> uint8 {
|
||||||
if(status.oam_dma_pending) {
|
if(io.oamdmaPending) {
|
||||||
status.oam_dma_pending = false;
|
io.oamdmaPending = false;
|
||||||
op_read(addr);
|
read(addr);
|
||||||
oam_dma();
|
oamdma();
|
||||||
}
|
}
|
||||||
|
|
||||||
while(status.rdy_line == 0) {
|
while(io.rdyLine == 0) {
|
||||||
regs.mdr = bus.read(status.rdy_addr_valid ? status.rdy_addr_value : addr);
|
regs.mdr = bus.read(io.rdyAddrValid ? io.rdyAddrValue : addr);
|
||||||
add_clocks(12);
|
step(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.mdr = bus.read(addr);
|
regs.mdr = bus.read(addr);
|
||||||
add_clocks(12);
|
step(12);
|
||||||
return regs.mdr;
|
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);
|
bus.write(addr, regs.mdr = data);
|
||||||
add_clocks(12);
|
step(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::last_cycle() -> void {
|
auto CPU::lastCycle() -> void {
|
||||||
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
|
io.interruptPending = ((io.irqLine | io.apuLine) & ~regs.p.i) | io.nmiPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::nmi(uint16& vector) -> void {
|
auto CPU::nmi(uint16& vector) -> void {
|
||||||
if(status.nmi_pending) {
|
if(io.nmiPending) {
|
||||||
status.nmi_pending = false;
|
io.nmiPending = false;
|
||||||
vector = 0xfffa;
|
vector = 0xfffa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::oam_dma() -> void {
|
auto CPU::oamdma() -> void {
|
||||||
for(uint n : range(256)) {
|
for(uint n : range(256)) {
|
||||||
uint8 data = op_read((status.oam_dma_page << 8) + n);
|
uint8 data = read(io.oamdmaPage << 8 | n);
|
||||||
op_write(0x2004, data);
|
write(0x2004, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::set_nmi_line(bool line) -> void {
|
auto CPU::nmiLine(bool line) -> void {
|
||||||
//edge-sensitive (0->1)
|
//edge-sensitive (0->1)
|
||||||
if(!status.nmi_line && line) status.nmi_pending = true;
|
if(!io.nmiLine && line) io.nmiPending = true;
|
||||||
status.nmi_line = line;
|
io.nmiLine = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::set_irq_line(bool line) -> void {
|
auto CPU::irqLine(bool line) -> void {
|
||||||
//level-sensitive
|
//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
|
//level-sensitive
|
||||||
status.irq_apu_line = line;
|
io.apuLine = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::set_rdy_line(bool line) -> void {
|
auto CPU::rdyLine(bool line) -> void {
|
||||||
status.rdy_line = line;
|
io.rdyLine = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::set_rdy_addr(bool valid, uint16 value) -> void {
|
auto CPU::rdyAddr(bool valid, uint16 value) -> void {
|
||||||
status.rdy_addr_valid = valid;
|
io.rdyAddrValid = valid;
|
||||||
status.rdy_addr_value = value;
|
io.rdyAddrValue = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,15 +37,29 @@ namespace Famicom {
|
||||||
int64 clock = 0;
|
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/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/memory/memory.hpp>
|
||||||
#include <fc/cartridge/cartridge.hpp>
|
#include <fc/cartridge/cartridge.hpp>
|
||||||
#include <fc/cpu/cpu.hpp>
|
#include <fc/cpu/cpu.hpp>
|
||||||
#include <fc/apu/apu.hpp>
|
#include <fc/apu/apu.hpp>
|
||||||
#include <fc/ppu/ppu.hpp>
|
#include <fc/ppu/ppu.hpp>
|
||||||
#include <fc/cheat/cheat.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>
|
#include <fc/interface/interface.hpp>
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -21,7 +21,11 @@ Interface::Interface() {
|
||||||
|
|
||||||
media.append({ID::Famicom, "Famicom", "fc", true});
|
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({0, 0, "Up" });
|
||||||
device.inputs.append({1, 0, "Down" });
|
device.inputs.append({1, 0, "Down" });
|
||||||
device.inputs.append({2, 0, "Left" });
|
device.inputs.append({2, 0, "Left" });
|
||||||
|
@ -190,6 +194,10 @@ auto Interface::unload() -> void {
|
||||||
system.unload();
|
system.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Interface::connect(uint port, uint device) -> void {
|
||||||
|
Famicom::peripherals.connect(port, device);
|
||||||
|
}
|
||||||
|
|
||||||
auto Interface::power() -> void {
|
auto Interface::power() -> void {
|
||||||
system.power();
|
system.power();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,10 @@ struct ID {
|
||||||
CharacterRAM,
|
CharacterRAM,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : uint {
|
enum : uint { //bitmasks
|
||||||
Port1 = 1,
|
ControllerPort1 = 1,
|
||||||
Port2 = 2,
|
ControllerPort2 = 2,
|
||||||
|
ExpansionPort = 4,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto save(uint id, const stream& stream) -> void;
|
auto save(uint id, const stream& stream) -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
|
|
||||||
|
auto connect(uint port, uint device) -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
|
@ -61,6 +63,10 @@ private:
|
||||||
struct Settings {
|
struct Settings {
|
||||||
bool colorEmulation = true;
|
bool colorEmulation = true;
|
||||||
bool scanlineEmulation = true;
|
bool scanlineEmulation = true;
|
||||||
|
|
||||||
|
uint controllerPort1 = 0;
|
||||||
|
uint controllerPort2 = 0;
|
||||||
|
uint expansionPort = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface* interface;
|
extern Interface* interface;
|
||||||
|
|
|
@ -13,9 +13,9 @@ Bus bus;
|
||||||
|
|
||||||
auto Bus::read(uint16 addr) -> uint8 {
|
auto Bus::read(uint16 addr) -> uint8 {
|
||||||
uint8 data = cartridge.prg_read(addr);
|
uint8 data = cartridge.prg_read(addr);
|
||||||
if(addr <= 0x1fff) data = cpu.ram_read(addr);
|
if(addr <= 0x1fff) data = cpu.readRAM(addr);
|
||||||
else if(addr <= 0x3fff) data = ppu.read(addr);
|
else if(addr <= 0x3fff) data = ppu.readIO(addr);
|
||||||
else if(addr <= 0x4017) data = cpu.read(addr);
|
else if(addr <= 0x4017) data = cpu.readIO(addr);
|
||||||
|
|
||||||
if(cheat.enable()) {
|
if(cheat.enable()) {
|
||||||
if(auto result = cheat.find(addr, data)) return result();
|
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 {
|
auto Bus::write(uint16 addr, uint8 data) -> void {
|
||||||
cartridge.prg_write(addr, data);
|
cartridge.prg_write(addr, data);
|
||||||
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
|
if(addr <= 0x1fff) return cpu.writeRAM(addr, data);
|
||||||
if(addr <= 0x3fff) return ppu.write(addr, data);
|
if(addr <= 0x3fff) return ppu.writeIO(addr, data);
|
||||||
if(addr <= 0x4017) return cpu.write(addr, data);
|
if(addr <= 0x4017) return cpu.writeIO(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,16 @@ auto PPU::main() -> void {
|
||||||
auto PPU::tick() -> void {
|
auto PPU::tick() -> void {
|
||||||
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
|
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 == 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.sprite_zero_hit = 0, status.sprite_overflow = 0;
|
||||||
|
|
||||||
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 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 == 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;
|
clock += 4;
|
||||||
if(clock >= 0) co_switch(cpu.thread);
|
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||||
|
|
||||||
status.lx++;
|
status.lx++;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ auto PPU::reset() -> void {
|
||||||
for(auto& n : oam ) n = 0;
|
for(auto& n : oam ) n = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::read(uint16 addr) -> uint8 {
|
auto PPU::readIO(uint16 addr) -> uint8 {
|
||||||
uint8 result = 0x00;
|
uint8 result = 0x00;
|
||||||
|
|
||||||
switch(addr & 7) {
|
switch(addr & 7) {
|
||||||
|
@ -108,7 +108,7 @@ auto PPU::read(uint16 addr) -> uint8 {
|
||||||
result |= status.mdr & 0x1f;
|
result |= status.mdr & 0x1f;
|
||||||
status.address_latch = 0;
|
status.address_latch = 0;
|
||||||
status.nmi_hold = 0;
|
status.nmi_hold = 0;
|
||||||
cpu.set_nmi_line(status.nmi_flag = 0);
|
cpu.nmiLine(status.nmi_flag = 0);
|
||||||
break;
|
break;
|
||||||
case 4: //OAMDATA
|
case 4: //OAMDATA
|
||||||
result = oam[status.oam_addr];
|
result = oam[status.oam_addr];
|
||||||
|
@ -134,7 +134,7 @@ auto PPU::read(uint16 addr) -> uint8 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::write(uint16 addr, uint8 data) -> void {
|
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
||||||
status.mdr = data;
|
status.mdr = data;
|
||||||
|
|
||||||
switch(addr & 7) {
|
switch(addr & 7) {
|
||||||
|
@ -146,7 +146,7 @@ auto PPU::write(uint16 addr, uint8 data) -> void {
|
||||||
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
|
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
|
||||||
status.vram_increment = (data & 0x04) ? 32 : 1;
|
status.vram_increment = (data & 0x04) ? 32 : 1;
|
||||||
status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
|
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;
|
return;
|
||||||
case 1: //PPUMASK
|
case 1: //PPUMASK
|
||||||
status.emphasis = data >> 5;
|
status.emphasis = data >> 5;
|
||||||
|
|
|
@ -10,8 +10,8 @@ struct PPU : Thread {
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
|
|
||||||
auto read(uint16 addr) -> uint8;
|
auto readIO(uint16 addr) -> uint8;
|
||||||
auto write(uint16 addr, uint8 data) -> void;
|
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
auto ciram_read(uint16 addr) -> uint8;
|
auto ciram_read(uint16 addr) -> uint8;
|
||||||
auto ciram_write(uint16 addr, uint8 data) -> void;
|
auto ciram_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
|
@ -24,8 +24,8 @@ auto Scheduler::exit(Event event_) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||||
if(thread == ppu.thread) {
|
if(thread == cpu.thread) {
|
||||||
while(enter(Mode::SynchronizePPU) != Event::Synchronize);
|
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||||
} else {
|
} else {
|
||||||
resume = thread;
|
resume = thread;
|
||||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||||
|
@ -33,8 +33,8 @@ auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Scheduler::synchronize() -> void {
|
auto Scheduler::synchronize() -> void {
|
||||||
if(co_active() == ppu.thread && mode == Mode::SynchronizePPU) return exit(Event::Synchronize);
|
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||||
if(co_active() != ppu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Scheduler::synchronizing() const -> bool {
|
auto Scheduler::synchronizing() const -> bool {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
struct Scheduler {
|
struct Scheduler {
|
||||||
enum class Mode : uint {
|
enum class Mode : uint {
|
||||||
Run,
|
Run,
|
||||||
SynchronizePPU,
|
SynchronizeCPU,
|
||||||
SynchronizeAll,
|
SynchronizeAll,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
|
@ -37,7 +37,6 @@ auto System::serialize(serializer& s) -> void {
|
||||||
|
|
||||||
auto System::serializeAll(serializer& s) -> void {
|
auto System::serializeAll(serializer& s) -> void {
|
||||||
system.serialize(s);
|
system.serialize(s);
|
||||||
input.serialize(s);
|
|
||||||
cartridge.serialize(s);
|
cartridge.serialize(s);
|
||||||
cpu.serialize(s);
|
cpu.serialize(s);
|
||||||
apu.serialize(s);
|
apu.serialize(s);
|
||||||
|
|
|
@ -2,21 +2,23 @@
|
||||||
|
|
||||||
namespace Famicom {
|
namespace Famicom {
|
||||||
|
|
||||||
|
#include "peripherals.cpp"
|
||||||
#include "video.cpp"
|
#include "video.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
System system;
|
System system;
|
||||||
|
|
||||||
auto System::loaded() const -> bool { return _loaded; }
|
|
||||||
|
|
||||||
auto System::run() -> void {
|
auto System::run() -> void {
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(ppu.thread);
|
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu.thread);
|
||||||
scheduler.synchronize(apu.thread);
|
scheduler.synchronize(apu.thread);
|
||||||
|
scheduler.synchronize(ppu.thread);
|
||||||
scheduler.synchronize(cartridge.thread);
|
scheduler.synchronize(cartridge.thread);
|
||||||
|
for(auto peripheral : cpu.peripherals) {
|
||||||
|
scheduler.synchronize(peripheral->thread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::load() -> void {
|
auto System::load() -> void {
|
||||||
|
@ -29,6 +31,7 @@ auto System::load() -> void {
|
||||||
|
|
||||||
auto System::unload() -> void {
|
auto System::unload() -> void {
|
||||||
if(!loaded()) return;
|
if(!loaded()) return;
|
||||||
|
peripherals.unload();
|
||||||
cartridge.unload();
|
cartridge.unload();
|
||||||
_loaded = false;
|
_loaded = false;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +41,6 @@ auto System::power() -> void {
|
||||||
cpu.power();
|
cpu.power();
|
||||||
apu.power();
|
apu.power();
|
||||||
ppu.power();
|
ppu.power();
|
||||||
input.reset();
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,14 +57,12 @@ auto System::reset() -> void {
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
apu.reset();
|
apu.reset();
|
||||||
ppu.reset();
|
ppu.reset();
|
||||||
input.reset();
|
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
|
peripherals.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::init() -> void {
|
auto System::init() -> void {
|
||||||
assert(interface != nullptr);
|
assert(interface != nullptr);
|
||||||
input.connect(0, Input::Device::Joypad);
|
|
||||||
input.connect(1, Input::Device::None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::term() -> void {
|
auto System::term() -> void {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
#include "peripherals.hpp"
|
||||||
|
|
||||||
struct System {
|
struct System {
|
||||||
auto loaded() const -> bool;
|
auto loaded() const -> bool { return _loaded; }
|
||||||
|
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
auto runToSave() -> void;
|
auto runToSave() -> void;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -1,25 +1,24 @@
|
||||||
auto R6502::disassemble() -> string {
|
auto R6502::disassemble() -> string {
|
||||||
string output = {hex(regs.pc, 4L), " "};
|
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 abs = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L)}; };
|
||||||
auto abx = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L), ",x"}; };
|
auto abx = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(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 aby = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L), ",y"}; };
|
||||||
auto iab = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 2), 2L), hex(debugger_read(regs.pc + 1), 2L), ")"}; };
|
auto iab = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 2), 2L), hex(readDebugger(regs.pc + 1), 2L), ")"}; };
|
||||||
auto imm = [&]() -> string { return {"#$", hex(debugger_read(regs.pc + 1), 2L)}; };
|
auto imm = [&]() -> string { return {"#$", hex(readDebugger(regs.pc + 1), 2L)}; };
|
||||||
auto imp = [&]() -> string { return {""}; };
|
auto imp = [&]() -> string { return {""}; };
|
||||||
auto izx = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 1), 2L), ",x)"}; };
|
auto izx = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 1), 2L), ",x)"}; };
|
||||||
auto izy = [&]() -> string { return {"($", hex(debugger_read(regs.pc + 1), 2L), "),y"}; };
|
auto izy = [&]() -> string { return {"($", hex(readDebugger(regs.pc + 1), 2L), "),y"}; };
|
||||||
auto rel = [&]() -> string { return {"$", hex((regs.pc + 2) + (int8)debugger_read(regs.pc + 1), 4L)}; };
|
auto rel = [&]() -> string { return {"$", hex((regs.pc + 2) + (int8)readDebugger(regs.pc + 1), 4L)}; };
|
||||||
auto zpg = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L)}; };
|
auto zpg = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L)}; };
|
||||||
auto zpx = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L), ",x"}; };
|
auto zpx = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L), ",x"}; };
|
||||||
auto zpy = [&]() -> string { return {"$", hex(debugger_read(regs.pc + 1), 2L), ",y"}; };
|
auto zpy = [&]() -> string { return {"$", hex(readDebugger(regs.pc + 1), 2L), ",y"}; };
|
||||||
|
|
||||||
#define op(byte, prefix, mode) \
|
#define op(byte, prefix, mode) \
|
||||||
case byte: output.append(#prefix, " ", mode()); \
|
case byte: output.append(#prefix, " ", mode()); \
|
||||||
break
|
break
|
||||||
|
|
||||||
uint8 opcode = debugger_read(regs.pc);
|
switch(auto opcode = readDebugger(regs.pc)) {
|
||||||
switch(opcode) {
|
|
||||||
op(0x00, brk, imm);
|
op(0x00, brk, imm);
|
||||||
op(0x01, ora, izx);
|
op(0x01, ora, izx);
|
||||||
op(0x05, ora, zpg);
|
op(0x05, ora, zpg);
|
||||||
|
|
|
@ -1,452 +1,296 @@
|
||||||
//opcode functions
|
auto R6502::op_branch(bool condition) {
|
||||||
//================
|
if(!condition) {
|
||||||
|
L rd = readPC();
|
||||||
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();
|
|
||||||
} else {
|
} else {
|
||||||
rd = op_readpci();
|
rd = readPC();
|
||||||
aa = regs.pc + (int8)rd;
|
aa = regs.pc + (int8)rd;
|
||||||
op_page(regs.pc, aa);
|
ioPage(regs.pc, aa);
|
||||||
L op_readpc();
|
L io();
|
||||||
regs.pc = aa;
|
regs.pc = aa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_clear_flag(uint bit) {
|
auto R6502::op_clear_flag(uint bit) {
|
||||||
L op_readpc();
|
L io();
|
||||||
regs.p &= ~(1 << bit);
|
regs.p &= ~(1 << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_decrement(uint8& r) {
|
auto R6502::op_decrement(uint8& r) {
|
||||||
L op_readpc();
|
L io();
|
||||||
r--;
|
r--;
|
||||||
regs.p.n = (r & 0x80);
|
regs.p.n = (r & 0x80);
|
||||||
regs.p.z = (r == 0);
|
regs.p.z = (r == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_increment(uint8& r) {
|
auto R6502::op_increment(uint8& r) {
|
||||||
L op_readpc();
|
L io();
|
||||||
r++;
|
r++;
|
||||||
regs.p.n = (r & 0x80);
|
regs.p.n = (r & 0x80);
|
||||||
regs.p.z = (r == 0);
|
regs.p.z = (r == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_pull(uint8& r) {
|
auto R6502::op_pull(uint8& r) {
|
||||||
op_readpc();
|
io();
|
||||||
op_readpc();
|
io();
|
||||||
L r = op_readsp();
|
L r = readSP();
|
||||||
regs.p.n = (r & 0x80);
|
regs.p.n = (r & 0x80);
|
||||||
regs.p.z = (r == 0);
|
regs.p.z = (r == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_push(uint8& r) {
|
auto R6502::op_push(uint8& r) {
|
||||||
op_readpc();
|
io();
|
||||||
L op_writesp(r);
|
L writeSP(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_absolute(fp op) {
|
auto R6502::op_read_absolute(fp op) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
L rd = op_read(abs.w);
|
L rd = read(abs.w);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_absolute_x(fp op) {
|
auto R6502::op_read_absolute_x(fp op) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page(abs.w, abs.w + regs.x);
|
ioPage(abs.w, abs.w + regs.x);
|
||||||
L rd = op_read(abs.w + regs.x);
|
L rd = read(abs.w + regs.x);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_absolute_y(fp op) {
|
auto R6502::op_read_absolute_y(fp op) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page(abs.w, abs.w + regs.y);
|
ioPage(abs.w, abs.w + regs.y);
|
||||||
L rd = op_read(abs.w + regs.y);
|
L rd = read(abs.w + regs.y);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_immediate(fp op) {
|
auto R6502::op_read_immediate(fp op) {
|
||||||
L rd = op_readpci();
|
L rd = readPC();
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_indirect_zero_page_x(fp op) {
|
auto R6502::op_read_indirect_zero_page_x(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
abs.l = op_readzp(zp++ + regs.x);
|
abs.l = readZP(zp++ + regs.x);
|
||||||
abs.h = op_readzp(zp++ + regs.x);
|
abs.h = readZP(zp++ + regs.x);
|
||||||
L rd = op_read(abs.w);
|
L rd = read(abs.w);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_indirect_zero_page_y(fp op) {
|
auto R6502::op_read_indirect_zero_page_y(fp op) {
|
||||||
rd = op_readpci();
|
rd = readPC();
|
||||||
abs.l = op_readzp(rd++);
|
abs.l = readZP(rd++);
|
||||||
abs.h = op_readzp(rd++);
|
abs.h = readZP(rd++);
|
||||||
op_page(abs.w, abs.w + regs.y);
|
ioPage(abs.w, abs.w + regs.y);
|
||||||
L rd = op_read(abs.w + regs.y);
|
L rd = read(abs.w + regs.y);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_zero_page(fp op) {
|
auto R6502::op_read_zero_page(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
L rd = op_readzp(zp);
|
L rd = readZP(zp);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_zero_page_x(fp op) {
|
auto R6502::op_read_zero_page_x(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
L rd = op_readzp(zp + regs.x);
|
L rd = readZP(zp + regs.x);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_read_zero_page_y(fp op) {
|
auto R6502::op_read_zero_page_y(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
L rd = op_readzp(zp + regs.y);
|
L rd = readZP(zp + regs.y);
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_rmw_absolute(fp op) {
|
auto R6502::op_rmw_absolute(fp op) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
rd = op_read(abs.w);
|
rd = read(abs.w);
|
||||||
op_write(abs.w, rd);
|
write(abs.w, rd);
|
||||||
call(op);
|
call(op);
|
||||||
L op_write(abs.w, rd);
|
L write(abs.w, rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_rmw_absolute_x(fp op) {
|
auto R6502::op_rmw_absolute_x(fp op) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page_always(abs.w, abs.w + regs.x);
|
ioPageAlways(abs.w, abs.w + regs.x);
|
||||||
rd = op_read(abs.w + regs.x);
|
rd = read(abs.w + regs.x);
|
||||||
op_write(abs.w + regs.x, rd);
|
write(abs.w + regs.x, rd);
|
||||||
call(op);
|
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) {
|
auto R6502::op_rmw_zero_page(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
rd = op_readzp(zp);
|
rd = readZP(zp);
|
||||||
op_writezp(zp, rd);
|
writeZP(zp, rd);
|
||||||
call(op);
|
call(op);
|
||||||
L op_writezp(zp, rd);
|
L writeZP(zp, rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_rmw_zero_page_x(fp op) {
|
auto R6502::op_rmw_zero_page_x(fp op) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
rd = op_readzp(zp + regs.x);
|
rd = readZP(zp + regs.x);
|
||||||
op_writezp(zp + regs.x, rd);
|
writeZP(zp + regs.x, rd);
|
||||||
call(op);
|
call(op);
|
||||||
L op_writezp(zp + regs.x, rd);
|
L writeZP(zp + regs.x, rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_set_flag(uint bit) {
|
auto R6502::op_set_flag(uint bit) {
|
||||||
L op_readpc();
|
L io();
|
||||||
regs.p |= 1 << bit;
|
regs.p |= 1 << bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_shift(fp op) {
|
auto R6502::op_shift(fp op) {
|
||||||
L op_readpc();
|
L io();
|
||||||
call(op);
|
call(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_absolute(uint8& r) {
|
auto R6502::op_store_absolute(uint8& r) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
L op_write(abs.w, r);
|
L write(abs.w, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_absolute_x(uint8& r) {
|
auto R6502::op_store_absolute_x(uint8& r) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page_always(abs.w, abs.w + regs.x);
|
ioPageAlways(abs.w, abs.w + regs.x);
|
||||||
L op_write(abs.w + regs.x, r);
|
L write(abs.w + regs.x, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_absolute_y(uint8& r) {
|
auto R6502::op_store_absolute_y(uint8& r) {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page_always(abs.w, abs.w + regs.y);
|
ioPageAlways(abs.w, abs.w + regs.y);
|
||||||
L op_write(abs.w + regs.y, r);
|
L write(abs.w + regs.y, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_indirect_zero_page_x(uint8& r) {
|
auto R6502::op_store_indirect_zero_page_x(uint8& r) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
abs.l = op_readzp(zp++ + regs.x);
|
abs.l = readZP(zp++ + regs.x);
|
||||||
abs.h = op_readzp(zp++ + regs.x);
|
abs.h = readZP(zp++ + regs.x);
|
||||||
L op_write(abs.w, r);
|
L write(abs.w, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_indirect_zero_page_y(uint8& r) {
|
auto R6502::op_store_indirect_zero_page_y(uint8& r) {
|
||||||
rd = op_readpci();
|
rd = readPC();
|
||||||
abs.l = op_readzp(rd++);
|
abs.l = readZP(rd++);
|
||||||
abs.h = op_readzp(rd++);
|
abs.h = readZP(rd++);
|
||||||
op_page_always(abs.w, abs.w + regs.y);
|
ioPageAlways(abs.w, abs.w + regs.y);
|
||||||
L op_write(abs.w + regs.y, r);
|
L write(abs.w + regs.y, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_zero_page(uint8& r) {
|
auto R6502::op_store_zero_page(uint8& r) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
L op_writezp(zp, r);
|
L writeZP(zp, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_zero_page_x(uint8& r) {
|
auto R6502::op_store_zero_page_x(uint8& r) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
L op_writezp(zp + regs.x, r);
|
L writeZP(zp + regs.x, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_store_zero_page_y(uint8& r) {
|
auto R6502::op_store_zero_page_y(uint8& r) {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
L op_writezp(zp + regs.y, r);
|
L writeZP(zp + regs.y, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opi_transfer(uint8& s, uint8& d, bool flag) {
|
auto R6502::op_transfer(uint8& s, uint8& d, bool flag) {
|
||||||
L op_readpc();
|
L io();
|
||||||
d = s;
|
d = s;
|
||||||
if(flag == false) return;
|
if(!flag) return;
|
||||||
regs.p.n = (d & 0x80);
|
regs.p.n = (d & 0x80);
|
||||||
regs.p.z = (d == 0);
|
regs.p.z = (d == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//opcodes
|
//
|
||||||
//=======
|
|
||||||
|
|
||||||
auto R6502::op_brk() {
|
auto R6502::op_brk() {
|
||||||
op_readpci();
|
readPC();
|
||||||
op_writesp(regs.pc >> 8);
|
writeSP(regs.pc >> 8);
|
||||||
op_writesp(regs.pc >> 0);
|
writeSP(regs.pc >> 0);
|
||||||
op_writesp(regs.p | 0x30);
|
writeSP(regs.p | 0x30);
|
||||||
abs.l = op_read(0xfffe);
|
abs.l = read(0xfffe);
|
||||||
regs.p.i = 1;
|
regs.p.i = 1;
|
||||||
regs.p.d = 0;
|
regs.p.d = 0;
|
||||||
L abs.h = op_read(0xffff);
|
L abs.h = read(0xffff);
|
||||||
regs.pc = abs.w;
|
regs.pc = abs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_jmp_absolute() {
|
auto R6502::op_jmp_absolute() {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
L abs.h = op_readpci();
|
L abs.h = readPC();
|
||||||
regs.pc = abs.w;
|
regs.pc = abs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_jmp_indirect_absolute() {
|
auto R6502::op_jmp_indirect_absolute() {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
iabs.l = op_read(abs.w); abs.l++;
|
iabs.l = read(abs.w); abs.l++;
|
||||||
L iabs.h = op_read(abs.w); abs.l++;
|
L iabs.h = read(abs.w); abs.l++;
|
||||||
regs.pc = iabs.w;
|
regs.pc = iabs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_jsr_absolute() {
|
auto R6502::op_jsr_absolute() {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_readpc();
|
io();
|
||||||
regs.pc--;
|
regs.pc--;
|
||||||
op_writesp(regs.pc >> 8);
|
writeSP(regs.pc >> 8);
|
||||||
L op_writesp(regs.pc >> 0);
|
L writeSP(regs.pc >> 0);
|
||||||
regs.pc = abs.w;
|
regs.pc = abs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_nop() {
|
auto R6502::op_nop() {
|
||||||
L op_readpc();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_php() {
|
auto R6502::op_php() {
|
||||||
op_readpc();
|
io();
|
||||||
L op_writesp(regs.p | 0x30);
|
L writeSP(regs.p | 0x30);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_plp() {
|
auto R6502::op_plp() {
|
||||||
op_readpc();
|
io();
|
||||||
op_readpc();
|
io();
|
||||||
L regs.p = op_readsp();
|
L regs.p = readSP();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_rti() {
|
auto R6502::op_rti() {
|
||||||
op_readpc();
|
io();
|
||||||
op_readpc();
|
io();
|
||||||
regs.p = op_readsp();
|
regs.p = readSP();
|
||||||
abs.l = op_readsp();
|
abs.l = readSP();
|
||||||
L abs.h = op_readsp();
|
L abs.h = readSP();
|
||||||
regs.pc = abs.w;
|
regs.pc = abs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_rts() {
|
auto R6502::op_rts() {
|
||||||
op_readpc();
|
io();
|
||||||
op_readpc();
|
io();
|
||||||
abs.l = op_readsp();
|
abs.l = readSP();
|
||||||
abs.h = op_readsp();
|
abs.h = readSP();
|
||||||
L op_readpc();
|
L io();
|
||||||
regs.pc = ++abs.w;
|
regs.pc = ++abs.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
//illegal opcodes
|
//illegal opcodes
|
||||||
//===============
|
//===============
|
||||||
|
|
||||||
auto R6502::opill_arr_immediate() {
|
auto R6502::op_arr_immediate() {
|
||||||
L rd = op_readpci();
|
L rd = readPC();
|
||||||
regs.a &= rd;
|
regs.a &= rd;
|
||||||
regs.a = (regs.p.c << 7) | (regs.a >> 1);
|
regs.a = (regs.p.c << 7) | (regs.a >> 1);
|
||||||
regs.p.n = (regs.a & 0x80);
|
regs.p.n = (regs.a & 0x80);
|
||||||
|
@ -455,34 +299,34 @@ L rd = op_readpci();
|
||||||
regs.p.v = regs.p.c ^ ((regs.a >> 5) & 1);
|
regs.p.v = regs.p.c ^ ((regs.a >> 5) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_absolute() {
|
auto R6502::op_nop_absolute() {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
L op_readpc();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_absolute_x() {
|
auto R6502::op_nop_absolute_x() {
|
||||||
abs.l = op_readpci();
|
abs.l = readPC();
|
||||||
abs.h = op_readpci();
|
abs.h = readPC();
|
||||||
op_page(abs.w, abs.w + regs.x);
|
ioPage(abs.w, abs.w + regs.x);
|
||||||
L op_readpc();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_immediate() {
|
auto R6502::op_nop_immediate() {
|
||||||
L rd = op_readpc();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_implied() {
|
auto R6502::op_nop_implied() {
|
||||||
L op_readpc();
|
L io();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_zero_page() {
|
auto R6502::op_nop_zero_page() {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
L op_readzp(zp);
|
L readZP(zp);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::opill_nop_zero_page_x() {
|
auto R6502::op_nop_zero_page_x() {
|
||||||
zp = op_readpci();
|
zp = readPC();
|
||||||
op_readzp(zp);
|
readZP(zp);
|
||||||
L op_readzp(zp + regs.x);
|
L readZP(zp + regs.x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,36 @@
|
||||||
auto R6502::op_readpc() -> uint8 {
|
auto R6502::io() -> uint8 {
|
||||||
return op_read(regs.pc);
|
return read(regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_readpci() -> uint8 {
|
auto R6502::readPC() -> uint8 {
|
||||||
return op_read(regs.pc++);
|
return read(regs.pc++);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_readsp() -> uint8 {
|
auto R6502::readSP() -> uint8 {
|
||||||
return op_read(0x0100 | ++regs.s);
|
return read(0x0100 | ++regs.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_readzp(uint8 addr) -> uint8 {
|
auto R6502::readZP(uint8 addr) -> uint8 {
|
||||||
return op_read(addr);
|
return read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto R6502::op_writesp(uint8 data) -> void {
|
auto R6502::writeSP(uint8 data) -> void {
|
||||||
op_write(0x0100 | regs.s--, data);
|
write(0x0100 | regs.s--, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_writezp(uint8 addr, uint8 data) -> void {
|
auto R6502::writeZP(uint8 addr, uint8 data) -> void {
|
||||||
op_write(addr, data);
|
write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto R6502::op_page(uint16 x, uint16 y) -> void {
|
auto R6502::ioPage(uint16 x, uint16 y) -> void {
|
||||||
if((x & 0xff00) != (y & 0xff00)) op_read((x & 0xff00) | (y & 0x00ff));
|
if(x >> 8 == y >> 8) return;
|
||||||
|
read((x & 0xff00) | (y & 0x00ff));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::op_page_always(uint16 x, uint16 y) -> void {
|
auto R6502::ioPageAlways(uint16 x, uint16 y) -> void {
|
||||||
op_read((x & 0xff00) | (y & 0x00ff));
|
read((x & 0xff00) | (y & 0x00ff));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
namespace Processor {
|
namespace Processor {
|
||||||
|
|
||||||
#define I
|
#define L lastCycle();
|
||||||
#define L last_cycle();
|
|
||||||
#define call(op) (this->*op)()
|
#define call(op) (this->*op)()
|
||||||
|
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
|
#include "algorithms.cpp"
|
||||||
#include "instructions.cpp"
|
#include "instructions.cpp"
|
||||||
|
#include "switch.cpp"
|
||||||
#include "disassembler.cpp"
|
#include "disassembler.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
|
|
||||||
|
@ -31,210 +32,20 @@ auto R6502::reset() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto R6502::interrupt() -> void {
|
auto R6502::interrupt() -> void {
|
||||||
op_readpc();
|
io();
|
||||||
op_readpc();
|
io();
|
||||||
op_writesp(regs.pc >> 8);
|
writeSP(regs.pc >> 8);
|
||||||
op_writesp(regs.pc >> 0);
|
writeSP(regs.pc >> 0);
|
||||||
op_writesp(regs.p | 0x20);
|
writeSP(regs.p | 0x20);
|
||||||
uint16 vector = 0xfffe; //IRQ
|
uint16 vector = 0xfffe; //IRQ
|
||||||
nmi(vector);
|
nmi(vector);
|
||||||
abs.l = op_read(vector++);
|
abs.l = read(vector++);
|
||||||
regs.p.i = 1;
|
regs.p.i = 1;
|
||||||
regs.p.d = 0;
|
regs.p.d = 0;
|
||||||
L abs.h = op_read(vector++);
|
L abs.h = read(vector++);
|
||||||
regs.pc = abs.w;
|
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 L
|
||||||
#undef call
|
#undef call
|
||||||
|
|
||||||
|
|
|
@ -7,88 +7,87 @@
|
||||||
namespace Processor {
|
namespace Processor {
|
||||||
|
|
||||||
struct R6502 {
|
struct R6502 {
|
||||||
virtual auto op_read(uint16 addr) -> uint8 = 0;
|
virtual auto read(uint16 addr) -> uint8 = 0;
|
||||||
virtual auto op_write(uint16 addr, uint8 data) -> void = 0;
|
virtual auto write(uint16 addr, uint8 data) -> void = 0;
|
||||||
virtual auto last_cycle() -> void = 0;
|
virtual auto lastCycle() -> void = 0;
|
||||||
virtual auto nmi(uint16& vector) -> 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 mdr() const -> uint8;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
auto interrupt() -> void;
|
auto interrupt() -> void;
|
||||||
auto exec() -> void;
|
auto instruction() -> void;
|
||||||
|
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
auto op_readpc() -> uint8;
|
auto io() -> uint8;
|
||||||
auto op_readpci() -> uint8;
|
auto readPC() -> uint8;
|
||||||
auto op_readsp() -> uint8;
|
auto readSP() -> uint8;
|
||||||
auto op_readzp(uint8 addr) -> uint8;
|
auto readZP(uint8 addr) -> uint8;
|
||||||
|
|
||||||
auto op_writesp(uint8 data) -> void;
|
auto writeSP(uint8 data) -> void;
|
||||||
auto op_writezp(uint8 addr, uint8 data) -> void;
|
auto writeZP(uint8 addr, uint8 data) -> void;
|
||||||
|
|
||||||
auto op_page(uint16 x, uint16 y) -> void;
|
auto ioPage(uint16 x, uint16 y) -> void;
|
||||||
auto op_page_always(uint16 x, uint16 y) -> void;
|
auto ioPageAlways(uint16 x, uint16 y) -> void;
|
||||||
|
|
||||||
//instructions.cpp
|
//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;
|
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 op_branch(bool condition);
|
||||||
auto opi_clear_flag(uint bit);
|
auto op_clear_flag(uint bit);
|
||||||
auto opi_decrement(uint8& r);
|
auto op_decrement(uint8& r);
|
||||||
auto opi_increment(uint8& r);
|
auto op_increment(uint8& r);
|
||||||
auto opi_pull(uint8& r);
|
auto op_pull(uint8& r);
|
||||||
auto opi_push(uint8& r);
|
auto op_push(uint8& r);
|
||||||
auto opi_read_absolute(fp);
|
auto op_read_absolute(fp);
|
||||||
auto opi_read_absolute_x(fp);
|
auto op_read_absolute_x(fp);
|
||||||
auto opi_read_absolute_y(fp);
|
auto op_read_absolute_y(fp);
|
||||||
auto opi_read_immediate(fp);
|
auto op_read_immediate(fp);
|
||||||
auto opi_read_indirect_zero_page_x(fp);
|
auto op_read_indirect_zero_page_x(fp);
|
||||||
auto opi_read_indirect_zero_page_y(fp);
|
auto op_read_indirect_zero_page_y(fp);
|
||||||
auto opi_read_zero_page(fp);
|
auto op_read_zero_page(fp);
|
||||||
auto opi_read_zero_page_x(fp);
|
auto op_read_zero_page_x(fp);
|
||||||
auto opi_read_zero_page_y(fp);
|
auto op_read_zero_page_y(fp);
|
||||||
auto opi_rmw_absolute(fp);
|
auto op_rmw_absolute(fp);
|
||||||
auto opi_rmw_absolute_x(fp);
|
auto op_rmw_absolute_x(fp);
|
||||||
auto opi_rmw_zero_page(fp);
|
auto op_rmw_zero_page(fp);
|
||||||
auto opi_rmw_zero_page_x(fp);
|
auto op_rmw_zero_page_x(fp);
|
||||||
auto opi_set_flag(uint bit);
|
auto op_set_flag(uint bit);
|
||||||
auto opi_shift(fp);
|
auto op_shift(fp);
|
||||||
auto opi_store_absolute(uint8& r);
|
auto op_store_absolute(uint8& r);
|
||||||
auto opi_store_absolute_x(uint8& r);
|
auto op_store_absolute_x(uint8& r);
|
||||||
auto opi_store_absolute_y(uint8& r);
|
auto op_store_absolute_y(uint8& r);
|
||||||
auto opi_store_indirect_zero_page_x(uint8& r);
|
auto op_store_indirect_zero_page_x(uint8& r);
|
||||||
auto opi_store_indirect_zero_page_y(uint8& r);
|
auto op_store_indirect_zero_page_y(uint8& r);
|
||||||
auto opi_store_zero_page(uint8& r);
|
auto op_store_zero_page(uint8& r);
|
||||||
auto opi_store_zero_page_x(uint8& r);
|
auto op_store_zero_page_x(uint8& r);
|
||||||
auto opi_store_zero_page_y(uint8& r);
|
auto op_store_zero_page_y(uint8& r);
|
||||||
auto opi_transfer(uint8& s, uint8& d, bool flag);
|
auto op_transfer(uint8& s, uint8& d, bool flag);
|
||||||
|
|
||||||
auto op_brk();
|
auto op_brk();
|
||||||
auto op_jmp_absolute();
|
auto op_jmp_absolute();
|
||||||
|
@ -100,13 +99,13 @@ struct R6502 {
|
||||||
auto op_rti();
|
auto op_rti();
|
||||||
auto op_rts();
|
auto op_rts();
|
||||||
|
|
||||||
auto opill_arr_immediate();
|
auto op_arr_immediate();
|
||||||
auto opill_nop_absolute();
|
auto op_nop_absolute();
|
||||||
auto opill_nop_absolute_x();
|
auto op_nop_absolute_x();
|
||||||
auto opill_nop_immediate();
|
auto op_nop_immediate();
|
||||||
auto opill_nop_implied();
|
auto op_nop_implied();
|
||||||
auto opill_nop_zero_page();
|
auto op_nop_zero_page();
|
||||||
auto opill_nop_zero_page_x();
|
auto op_nop_zero_page_x();
|
||||||
|
|
||||||
//disassembler.cpp
|
//disassembler.cpp
|
||||||
auto disassemble() -> string;
|
auto disassemble() -> string;
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue