1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-12 09:25:19 +00:00

Merge pull request #519 from TomHarte/65C02

Makes an initial pass at 65C02 emulation
This commit is contained in:
Thomas Harte
2018-08-10 23:21:45 -04:00
committed by GitHub
23 changed files with 515 additions and 214 deletions

View File

@@ -298,7 +298,7 @@ template <bool is_iie> class ConcreteMachine:
public: public:
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
video_bus_handler_(ram_, aux_ram_), video_bus_handler_(ram_, aux_ram_),
audio_toggle_(audio_queue_), audio_toggle_(audio_queue_),
speaker_(audio_toggle_) { speaker_(audio_toggle_) {

View File

@@ -32,7 +32,7 @@ template<class T> class Cartridge:
public: public:
Cartridge(const std::vector<uint8_t> &rom) : Cartridge(const std::vector<uint8_t> &rom) :
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
rom_(rom), rom_(rom),
bus_extender_(rom_.data(), rom.size()) { bus_extender_(rom_.data(), rom.size()) {
// The above works because bus_extender_ is declared after rom_ in the instance storage list; // The above works because bus_extender_ is declared after rom_ in the instance storage list;

View File

@@ -18,7 +18,7 @@ using namespace Commodore::C1540;
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
Storage::Disk::Controller(1000000), Storage::Disk::Controller(1000000),
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
drive_(new Storage::Disk::Drive(1000000, 300, 2)), drive_(new Storage::Disk::Drive(1000000, 300, 2)),
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)), serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
serial_port_(new SerialPort), serial_port_(new SerialPort),

View File

@@ -293,7 +293,7 @@ class ConcreteMachine:
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
user_port_via_port_handler_(new UserPortVIA), user_port_via_port_handler_(new UserPortVIA),
keyboard_via_port_handler_(new KeyboardVIA), keyboard_via_port_handler_(new KeyboardVIA),
serial_port_(new SerialPort), serial_port_(new SerialPort),

View File

@@ -50,7 +50,7 @@ class ConcreteMachine:
public Activity::Source { public Activity::Source {
public: public:
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
sound_generator_(audio_queue_), sound_generator_(audio_queue_),
speaker_(sound_generator_) { speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_)); memset(key_states_, 0, sizeof(key_states_));

View File

@@ -207,7 +207,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
public: public:
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(*this), m6502_(CPU::MOS6502::Personality::P6502, *this),
ay8910_(audio_queue_), ay8910_(audio_queue_),
speaker_(ay8910_), speaker_(ay8910_),
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_), via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),

View File

@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
4B018B89211930DE002A3937 /* 65C02_extended_opcodes_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */; };
4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */; }; 4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */; };
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
@@ -690,6 +691,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_extended_opcodes_test.bin; path = "Klaus Dormann/65C02_extended_opcodes_test.bin"; sourceTree = "<group>"; };
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = "<group>"; }; 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = "<group>"; };
4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; }; 4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; };
4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = "<group>"; }; 4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = "<group>"; };
@@ -1545,14 +1547,15 @@
4B1414631B588A1100E04248 /* Test Binaries */ = { 4B1414631B588A1100E04248 /* Test Binaries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
4B9252CD1E74D28200B76AF1 /* Atari ROMs */, 4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */, 4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */, 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */, 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
4BE9A6B21EDE294200CBCB47 /* Zexall */,
4BBF49B41ED2881600AB3669 /* FUSE */, 4BBF49B41ED2881600AB3669 /* FUSE */,
4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */, 4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */,
4BE9A6B21EDE294200CBCB47 /* Zexall */,
); );
name = "Test Binaries"; name = "Test Binaries";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3330,6 +3333,7 @@
4BB2998A1B587D8400A49093 /* lseix in Resources */, 4BB2998A1B587D8400A49093 /* lseix in Resources */,
4BB2994E1B587D8400A49093 /* dexn in Resources */, 4BB2994E1B587D8400A49093 /* dexn in Resources */,
4BB299971B587D8400A49093 /* nopa in Resources */, 4BB299971B587D8400A49093 /* nopa in Resources */,
4B018B89211930DE002A3937 /* 65C02_extended_opcodes_test.bin in Resources */,
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */, 4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */,
4BB299521B587D8400A49093 /* eoray in Resources */, 4BB299521B587D8400A49093 /* eoray in Resources */,
4BB299411B587D8400A49093 /* cpyb in Resources */, 4BB299411B587D8400A49093 /* cpyb in Resources */,

View File

@@ -15,7 +15,7 @@ class MOS6502InterruptTests: XCTestCase {
super.setUp() super.setUp()
// create a machine full of NOPs // create a machine full of NOPs
machine = CSTestMachine6502() machine = CSTestMachine6502(is65C02: false)
for c in 0...65535 { for c in 0...65535 {
machine.setValue(0xea, forAddress: UInt16(c)) machine.setValue(0xea, forAddress: UInt16(c))
} }

View File

@@ -12,7 +12,7 @@ import XCTest
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler { class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
private var endTime: UInt32 = 0 private var endTime: UInt32 = 0
private let machine = CSTestMachine6502() private let machine = CSTestMachine6502(is65C02: false)
func testImplied() { func testImplied() {
let code: [UInt8] = [ let code: [UInt8] = [

View File

@@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
func testAllSuiteA() { func testAllSuiteA() {
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") { if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) { if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502() let machine = CSTestMachine6502(is65C02: false)
machine.setData(allSuiteA, atAddress: 0x4000) machine.setData(allSuiteA, atAddress: 0x4000)
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end

View File

@@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
func testBCD() { func testBCD() {
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) { if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502() let machine = CSTestMachine6502(is65C02: false)
machine.trapHandler = self machine.trapHandler = self
machine.setData(bcdTest, atAddress: 0x2900) machine.setData(bcdTest, atAddress: 0x2900)

View File

@@ -32,8 +32,8 @@ class VanillaSerialPort: public Commodore::Serial::Port {
_serialBus.reset(new ::Commodore::Serial::Bus); _serialBus.reset(new ::Commodore::Serial::Bus);
_serialPort.reset(new VanillaSerialPort); _serialPort.reset(new VanillaSerialPort);
_c1540.reset(new Commodore::C1540::Machine(Commodore::C1540::Machine::C1540)); auto rom_fetcher = CSROMFetcher();
_c1540->set_rom_fetcher(CSROMFetcher()); _c1540.reset(new Commodore::C1540::Machine(Commodore::C1540::Personality::C1540, rom_fetcher));
_c1540->set_serial_bus(_serialBus); _c1540->set_serial_bus(_serialBus);
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus); Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
} }

View File

@@ -23,7 +23,11 @@ extern const uint8_t CSTestMachine6502JamOpcode;
@interface CSTestMachine6502 : CSTestMachine @interface CSTestMachine6502 : CSTestMachine
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress; - (nonnull instancetype)init NS_UNAVAILABLE;
- (nonnull instancetype)initIs65C02:(BOOL)is65C02;
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
- (void)runForNumberOfCycles:(int)cycles; - (void)runForNumberOfCycles:(int)cycles;
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address; - (void)setValue:(uint8_t)value forAddress:(uint16_t)address;

View File

@@ -8,7 +8,7 @@
#import "TestMachine6502.h" #import "TestMachine6502.h"
#include <stdint.h> #include <stdint.h>
#include "6502AllRAM.hpp" #include "../../../../Processors/6502/AllRAM/6502AllRAM.hpp"
#import "TestMachine+ForSubclassEyesOnly.h" #import "TestMachine+ForSubclassEyesOnly.h"
const uint8_t CSTestMachine6502JamOpcode = CPU::MOS6502::JamOpcode; const uint8_t CSTestMachine6502JamOpcode = CPU::MOS6502::JamOpcode;
@@ -35,11 +35,12 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
#pragma mark - Lifecycle #pragma mark - Lifecycle
- (instancetype)init { - (instancetype)initIs65C02:(BOOL)is65C02 {
self = [super init]; self = [super init];
if(self) { if(self) {
_processor = CPU::MOS6502::AllRAMProcessor::Processor(); _processor = CPU::MOS6502::AllRAMProcessor::Processor(
is65C02 ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502);
} }
return self; return self;

View File

@@ -11,10 +11,37 @@ import XCTest
class KlausDormannTests: XCTestCase { class KlausDormannTests: XCTestCase {
func testKlausDormann() { fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 {
if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") {
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502(is65C02: is65C02)
machine.setData(functionalTest, atAddress: 0)
machine.setValue(0x400, for: .programCounter)
while true {
let oldPC = machine.value(for: .lastOperationAddress)
machine.runForNumber(ofCycles: 1000)
let newPC = machine.value(for: .lastOperationAddress)
if newPC == oldPC {
machine.runForNumber(ofCycles: 7)
let retestPC = machine.value(for: .lastOperationAddress)
if retestPC == oldPC {
return newPC
}
}
}
}
}
return 0
}
/// Runs Klaus Dorman's 6502 tests.
func test6502() {
func errorForTrapAddress(_ address: UInt16) -> String? { func errorForTrapAddress(_ address: UInt16) -> String? {
let hexAddress = String(format:"%04x", address)
switch address { switch address {
case 0x3399: return nil // success! case 0x3399: return nil // success!
@@ -28,29 +55,63 @@ class KlausDormannTests: XCTestCase {
case 0x26d2: return "ASL zpg,x produced incorrect flags" case 0x26d2: return "ASL zpg,x produced incorrect flags"
case 0x36c6: return "Unexpected RESET" case 0x36c6: return "Unexpected RESET"
default: return "Unknown error at \(hexAddress)" case 0: return "Didn't find tests"
default: return "Unknown error at \(String(format:"%04x", address))"
} }
} }
if let filename = Bundle(for: type(of: self)).path(forResource: "6502_functional_test", ofType: "bin") { let destination = runTest(resource: "6502_functional_test", is65C02: false)
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) { let error = errorForTrapAddress(destination)
let machine = CSTestMachine6502()
machine.setData(functionalTest, atAddress: 0)
machine.setValue(0x400, for: .programCounter)
while true {
let oldPC = machine.value(for: .lastOperationAddress)
machine.runForNumber(ofCycles: 1000)
let newPC = machine.value(for: .lastOperationAddress)
if newPC == oldPC {
let error = errorForTrapAddress(oldPC)
XCTAssert(error == nil, "Failed with error \(error!)") XCTAssert(error == nil, "Failed with error \(error!)")
return }
}
} /// Runs Klaus Dorman's 65C02 tests.
func test65C02() {
func errorForTrapAddress(_ address: UInt16) -> String? {
switch address {
case 0x24f1: return nil // success!
case 0x0423: return "PHX: value of X not on stack page"
case 0x0428: return "PHX: stack pointer not decremented"
case 0x042d: return "PLY: didn't acquire value 0xaa from stack"
case 0x0432: return "PLY: didn't acquire value 0x55 from stack"
case 0x0437: return "PLY: stack pointer not incremented"
case 0x043c: return "PLY: stack pointer not incremented"
case 0x066a: return "BRA: branch not taken"
case 0x0730: return "BBS: branch not taken"
case 0x0733: return "BBR: branch taken"
case 0x2884: return "JMP (abs) exhibited 6502 page-crossing bug"
case 0x16ca: return "JMP (abs, x) failed"
case 0x2785: return "BRK didn't clear the decimal mode flag"
case 0x177b: return "INC A didn't function"
case 0x1834: return "LDA (zp) acted as JAM"
case 0x183a: return "STA (zp) acted as JAM"
case 0x1849: return "LDA/STA (zp) left flags in incorrect state"
case 0x1983: return "STZ didn't store zero"
case 0x1b03: return "BIT didn't set flags correctly"
case 0x1c6c: return "BIT immediate didn't set flags correctly"
case 0x1d88: return "TRB set Z flag incorrectly"
case 0x1e7c: return "RMB set flags incorrectly"
case 0x2245: return "CMP (zero) didn't work"
case 0x2506: return "Decimal ADC set flags incorrectly"
case 0: return "Didn't find tests"
default: return "Unknown error at \(String(format:"%04x", address))"
} }
} }
let destination = runTest(resource: "65C02_extended_opcodes_test", is65C02: true)
let error = errorForTrapAddress(destination)
XCTAssert(error == nil, "Failed with error \(error!)")
} }
} }

View File

@@ -200,7 +200,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) { if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) { if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
machine = CSTestMachine6502() machine = CSTestMachine6502(is65C02: false)
machine.trapHandler = self machine.trapHandler = self
// machine.logActivity = true // machine.logActivity = true
output = "" output = ""

View File

@@ -33,6 +33,15 @@ enum Register {
S S
}; };
/*
The list of 6502 variants supported by this implementation.
*/
enum Personality {
P6502, // the original 6502, replete with various undocumented instructions
P65C02, // the 65C02; an extended 6502 with a few extra instructions and addressing modes for existing instructions
P65SC02, // like the 65C02, but lacking bit instructions
};
/* /*
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
the corresponding set. the corresponding set.
@@ -110,6 +119,8 @@ class BusHandler {
*/ */
class ProcessorBase: public ProcessorStorage { class ProcessorBase: public ProcessorStorage {
public: public:
ProcessorBase(Personality personality) : ProcessorStorage(personality) {}
/*! /*!
Gets the value of a register. Gets the value of a register.
@@ -193,7 +204,7 @@ template <typename T, bool uses_ready_line> class Processor: public ProcessorBas
/*! /*!
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
*/ */
Processor(T &bus_handler) : bus_handler_(bus_handler) {} Processor(Personality personality, T &bus_handler) : ProcessorBase(personality), personality_(personality), bus_handler_(bus_handler) {}
/*! /*!
Runs the 6502 for a supplied number of cycles. Runs the 6502 for a supplied number of cycles.
@@ -210,6 +221,7 @@ template <typename T, bool uses_ready_line> class Processor: public ProcessorBas
void set_ready_line(bool active); void set_ready_line(bool active);
private: private:
Personality personality_;
T &bus_handler_; T &bus_handler_;
}; };

View File

@@ -17,8 +17,8 @@ namespace {
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
public: public:
ConcreteAllRAMProcessor() : ConcreteAllRAMProcessor(Personality personality) :
mos6502_(*this) { mos6502_(personality, *this) {
mos6502_.set_power_on(false); mos6502_.set_power_on(false);
} }
@@ -68,6 +68,6 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
} }
AllRAMProcessor *AllRAMProcessor::Processor() { AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
return new ConcreteAllRAMProcessor; return new ConcreteAllRAMProcessor(personality);
} }

View File

@@ -19,7 +19,7 @@ class AllRAMProcessor:
public ::CPU::AllRAMProcessor { public ::CPU::AllRAMProcessor {
public: public:
static AllRAMProcessor *Processor(); static AllRAMProcessor *Processor(Personality personality);
virtual ~AllRAMProcessor() {} virtual ~AllRAMProcessor() {}
virtual void run_for(const Cycles cycles) = 0; virtual void run_for(const Cycles cycles) = 0;

View File

@@ -13,7 +13,7 @@
*/ */
template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::run_for(const Cycles cycles) { template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::run_for(const Cycles cycles) {
static const MicroOp doBranch[] = { static const MicroOp do_branch[] = {
CycleReadFromPC, CycleReadFromPC,
CycleAddSignedOperandToPC, CycleAddSignedOperandToPC,
OperationMoveToNextProgram OperationMoveToNextProgram
@@ -93,11 +93,25 @@ if(number_of_cycles <= Cycles(0)) break;
} break; } break;
case CycleFetchOperand: case CycleFetchOperand:
// This is supposed to produce the 65C02's 1-cycle NOPs; they're
// treated as a special case because they break the rule that
// governs everything else on the 6502: that two bytes will always
// be fetched.
if(
personality_ == P6502 ||
(operation_&7) != 3 ||
operation_ == 0xcb ||
operation_ == 0xdb
) {
read_mem(operand_, pc_.full); read_mem(operand_, pc_.full);
}
break; break;
case OperationDecodeOperation: case OperationDecodeOperation:
scheduled_program_counter_ = operations[operation_]; if(operation_ == 0x89) {
printf("");
}
scheduled_program_counter_ = operations_[operation_];
continue; continue;
case OperationMoveToNextProgram: case OperationMoveToNextProgram:
@@ -115,6 +129,8 @@ if(number_of_cycles <= Cycles(0)) break;
case CyclePushPCL: push(pc_.bytes.low); break; case CyclePushPCL: push(pc_.bytes.low); break;
case CyclePushOperand: push(operand_); break; case CyclePushOperand: push(operand_); break;
case CyclePushA: push(a_); break; case CyclePushA: push(a_); break;
case CyclePushX: push(x_); break;
case CyclePushY: push(y_); break;
case CycleNoWritePush: { case CycleNoWritePush: {
uint16_t targetAddress = s_ | 0x100; s_--; uint16_t targetAddress = s_ | 0x100; s_--;
read_mem(operand_, targetAddress); read_mem(operand_, targetAddress);
@@ -135,20 +151,29 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue; case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break; case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break; case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
case OperationSetI: inverse_interrupt_flag_ = 0; continue; case OperationSetI:
inverse_interrupt_flag_ = 0;
if(personality_ != P6502) decimal_flag_ = false;
continue;
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break; case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break; case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break;
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break; case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break; case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
case OperationSetFlagsFromOperand: set_flags(operand_); continue; case OperationSetFlagsFromOperand: set_flags(operand_); continue;
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue; case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
case OperationSetOperandFromFlags: operand_ = get_flags(); continue; case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue; case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break; case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
case CycleReadPCHFromAddress: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break; case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadAndIncrementPC: { case CycleReadAndIncrementPC: {
uint16_t oldPC = pc_.full; uint16_t oldPC = pc_.full;
@@ -160,7 +185,7 @@ if(number_of_cycles <= Cycles(0)) break;
case CycleScheduleJam: { case CycleScheduleJam: {
is_jammed_ = true; is_jammed_ = true;
scheduled_program_counter_ = operations[CPU::MOS6502::JamOpcode]; scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
} continue; } continue;
// MARK: - Bitwise // MARK: - Bitwise
@@ -179,6 +204,7 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationSTA: operand_ = a_; continue; case OperationSTA: operand_ = a_; continue;
case OperationSTX: operand_ = x_; continue; case OperationSTX: operand_ = x_; continue;
case OperationSTY: operand_ = y_; continue; case OperationSTY: operand_ = y_; continue;
case OperationSTZ: operand_ = 0; continue;
case OperationSAX: operand_ = a_ & x_; continue; case OperationSAX: operand_ = a_ & x_; continue;
case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue; case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue;
case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue; case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue;
@@ -208,13 +234,33 @@ if(number_of_cycles <= Cycles(0)) break;
carry_flag_ = ((~temp16) >> 8)&1; carry_flag_ = ((~temp16) >> 8)&1;
} continue; } continue;
// MARK: - BIT // MARK: - BIT, TSB, TRB
case OperationBIT: case OperationBIT:
zero_result_ = operand_ & a_; zero_result_ = operand_ & a_;
negative_result_ = operand_; negative_result_ = operand_;
overflow_flag_ = operand_&Flag::Overflow; overflow_flag_ = operand_&Flag::Overflow;
continue; continue;
case OperationBITNoNV:
zero_result_ = operand_ & a_;
continue;
case OperationTRB:
zero_result_ = operand_ & a_;
operand_ &= ~a_;
continue;
case OperationTSB:
zero_result_ = operand_ & a_;
operand_ |= a_;
continue;
// MARK: - RMB and SMB
case OperationRMB:
operand_ &= ~(1 << (operation_ >> 4));
continue;
case OperationSMB:
operand_ |= 1 << ((operation_ >> 4)&7);
continue;
// MARK: - ADC/SBC (and INS) // MARK: - ADC/SBC (and INS)
@@ -239,6 +285,12 @@ if(number_of_cycles <= Cycles(0)) break;
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
a_ = static_cast<uint8_t>(temp16); a_ = static_cast<uint8_t>(temp16);
if(personality_ != P6502) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
}
continue; continue;
} else { } else {
operand_ = ~operand_; operand_ = ~operand_;
@@ -259,6 +311,12 @@ if(number_of_cycles <= Cycles(0)) break;
carry_flag_ = (result >> 8) ? 1 : 0; carry_flag_ = (result >> 8) ? 1 : 0;
a_ = static_cast<uint8_t>(result); a_ = static_cast<uint8_t>(result);
zero_result_ = static_cast<uint8_t>(decimalResult); zero_result_ = static_cast<uint8_t>(decimalResult);
if(personality_ != P6502) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
}
} else { } else {
const uint16_t result = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_); const uint16_t result = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
@@ -345,6 +403,8 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue; case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue; case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue; case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue; case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue; case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
@@ -405,6 +465,9 @@ if(number_of_cycles <= Cycles(0)) break;
operand_ += x_; operand_ += x_;
read_mem(address_.bytes.low, operand_); read_mem(address_.bytes.low, operand_);
break; break;
case CycleFetchAddressLowFromOperand:
read_mem(address_.bytes.low, operand_);
break;
case CycleIncrementOperandFetchAddressHigh: case CycleIncrementOperandFetchAddressHigh:
operand_++; operand_++;
read_mem(address_.bytes.high, operand_); read_mem(address_.bytes.high, operand_);
@@ -454,7 +517,7 @@ if(number_of_cycles <= Cycles(0)) break;
// MARK: - Branching // MARK: - Branching
#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = doBranch #define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = do_branch
case OperationBPL: BRA(!(negative_result_&0x80)); continue; case OperationBPL: BRA(!(negative_result_&0x80)); continue;
case OperationBMI: BRA(negative_result_&0x80); continue; case OperationBMI: BRA(negative_result_&0x80); continue;
@@ -464,6 +527,7 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationBCS: BRA(carry_flag_); continue; case OperationBCS: BRA(carry_flag_); continue;
case OperationBNE: BRA(zero_result_); continue; case OperationBNE: BRA(zero_result_); continue;
case OperationBEQ: BRA(!zero_result_); continue; case OperationBEQ: BRA(!zero_result_); continue;
case OperationBRA: BRA(true); continue;
case CycleAddSignedOperandToPC: case CycleAddSignedOperandToPC:
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_); nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
@@ -476,6 +540,39 @@ if(number_of_cycles <= Cycles(0)) break;
} }
continue; continue;
case CycleFetchFromHalfUpdatedPC: {
uint16_t halfUpdatedPc = static_cast<uint16_t>(((pc_.bytes.low + (int8_t)operand_) & 0xff) | (pc_.bytes.high << 8));
throwaway_read(halfUpdatedPc);
} break;
case OperationAddSignedOperandToPC16:
pc_.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
continue;
case OperationBBRBBS: {
// To reach here, the 6502 has (i) read the operation; (ii) read the first operand;
// and (iii) read from the corresponding zero page.
const uint8_t mask = static_cast<uint8_t>(1 << ((operation_ >> 4)&7));
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
static const MicroOp do_branch[] = {
CycleFetchOperand, // Fetch offset.
OperationIncrementPC,
CycleFetchFromHalfUpdatedPC,
OperationAddSignedOperandToPC16,
OperationMoveToNextProgram
};
scheduled_program_counter_ = do_branch;
} else {
static const MicroOp do_not_branch[] = {
CycleFetchOperand,
OperationIncrementPC,
CycleFetchFromHalfUpdatedPC,
OperationMoveToNextProgram
};
scheduled_program_counter_ = do_not_branch;
}
} break;
#undef BRA #undef BRA
// MARK: - Transfers // MARK: - Transfers

View File

@@ -8,6 +8,8 @@
#include "../6502.hpp" #include "../6502.hpp"
#include <cstring>
using namespace CPU::MOS6502; using namespace CPU::MOS6502;
#define Program(...) {__VA_ARGS__, OperationMoveToNextProgram} #define Program(...) {__VA_ARGS__, OperationMoveToNextProgram}
@@ -20,13 +22,14 @@ using namespace CPU::MOS6502;
#define Zero OperationLoadAddressZeroPage #define Zero OperationLoadAddressZeroPage
#define ZeroX CycleLoadAddessZeroX #define ZeroX CycleLoadAddessZeroX
#define ZeroY CycleLoadAddessZeroY #define ZeroY CycleLoadAddessZeroY
#define ZeroIndirect OperationLoadAddressZeroPage, CycleFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh
#define IndexedIndirect CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh #define IndexedIndirect CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh
#define IndirectIndexedr CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLow, OperationCorrectAddressHigh #define IndirectIndexedr CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLow, OperationCorrectAddressHigh
#define IndirectIndexed CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLowRead, OperationCorrectAddressHigh #define IndirectIndexed CycleIncrementPCFetchAddressLowFromOperand, CycleIncrementOperandFetchAddressHigh, CycleAddYToAddressLowRead, OperationCorrectAddressHigh
#define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__ #define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__
#define Write(...) __VA_ARGS__, CycleWriteOperandToAddress #define Write(...) __VA_ARGS__, CycleWriteOperandToAddress
#define ReadModifyWrite(...) CycleFetchOperandFromAddress, CycleWriteOperandToAddress, __VA_ARGS__, CycleWriteOperandToAddress #define ReadModifyWrite(...) CycleFetchOperandFromAddress, (personality == P6502) ? CycleWriteOperandToAddress : CycleFetchOperandFromAddress, __VA_ARGS__, CycleWriteOperandToAddress
#define AbsoluteRead(op) Program(Absolute, Read(op)) #define AbsoluteRead(op) Program(Absolute, Read(op))
#define AbsoluteXRead(op) Program(AbsoluteXr, Read(op)) #define AbsoluteXRead(op) Program(AbsoluteXr, Read(op))
@@ -34,6 +37,7 @@ using namespace CPU::MOS6502;
#define ZeroRead(...) Program(Zero, Read(__VA_ARGS__)) #define ZeroRead(...) Program(Zero, Read(__VA_ARGS__))
#define ZeroXRead(op) Program(ZeroX, Read(op)) #define ZeroXRead(op) Program(ZeroX, Read(op))
#define ZeroYRead(op) Program(ZeroY, Read(op)) #define ZeroYRead(op) Program(ZeroY, Read(op))
#define ZeroIndirectRead(op) Program(ZeroIndirect, Read(op))
#define IndexedIndirectRead(op) Program(IndexedIndirect, Read(op)) #define IndexedIndirectRead(op) Program(IndexedIndirect, Read(op))
#define IndirectIndexedRead(op) Program(IndirectIndexedr, Read(op)) #define IndirectIndexedRead(op) Program(IndirectIndexedr, Read(op))
@@ -43,6 +47,7 @@ using namespace CPU::MOS6502;
#define ZeroWrite(op) Program(Zero, Write(op)) #define ZeroWrite(op) Program(Zero, Write(op))
#define ZeroXWrite(op) Program(ZeroX, Write(op)) #define ZeroXWrite(op) Program(ZeroX, Write(op))
#define ZeroYWrite(op) Program(ZeroY, Write(op)) #define ZeroYWrite(op) Program(ZeroY, Write(op))
#define ZeroIndirectWrite(op) Program(ZeroIndirect, Write(op))
#define IndexedIndirectWrite(op) Program(IndexedIndirect, Write(op)) #define IndexedIndirectWrite(op) Program(IndexedIndirect, Write(op))
#define IndirectIndexedWrite(op) Program(IndirectIndexed, Write(op)) #define IndirectIndexedWrite(op) Program(IndirectIndexed, Write(op))
@@ -67,7 +72,14 @@ using namespace CPU::MOS6502;
#define JAM {CycleFetchOperand, CycleScheduleJam} #define JAM {CycleFetchOperand, CycleScheduleJam}
const ProcessorStorage::MicroOp ProcessorStorage::operations[256][10] = { ProcessorStorage::ProcessorStorage(Personality personality) {
// only the interrupt flag is defined upon reset but get_flags isn't going to
// mask the other flags so we need to do that, at least
carry_flag_ &= Flag::Carry;
decimal_flag_ &= Flag::Decimal;
overflow_flag_ &= Flag::Overflow;
const InstructionList operations_6502[256] = {
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh), /* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh),
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA), /* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO), /* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
@@ -128,7 +140,7 @@ const ProcessorStorage::MicroOp ProcessorStorage::operations[256][10] = {
/* 0x66 ROR zpg */ ZeroReadModifyWrite(OperationROR), /* 0x67 RRA zpg */ ZeroReadModifyWrite(OperationRRA, OperationADC), /* 0x66 ROR zpg */ ZeroReadModifyWrite(OperationROR), /* 0x67 RRA zpg */ ZeroReadModifyWrite(OperationRRA, OperationADC),
/* 0x68 PLA */ Program(CycleReadFromS, CyclePullA, OperationSetFlagsFromA), /* 0x69 ADC # */ Immediate(OperationADC), /* 0x68 PLA */ Program(CycleReadFromS, CyclePullA, OperationSetFlagsFromA), /* 0x69 ADC # */ Immediate(OperationADC),
/* 0x6a ROR A */ Implied(OperationROR), /* 0x6b ARR # */ Immediate(OperationARR), /* 0x6a ROR A */ Implied(OperationROR), /* 0x6b ARR # */ Immediate(OperationARR),
/* 0x6c JMP (abs) */ Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress), /* 0x6c JMP (abs) */ Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc),
/* 0x6d ADC abs */ AbsoluteRead(OperationADC), /* 0x6d ADC abs */ AbsoluteRead(OperationADC),
/* 0x6e ROR abs */ AbsoluteReadModifyWrite(OperationROR), /* 0x6f RRA abs */ AbsoluteReadModifyWrite(OperationRRA, OperationADC), /* 0x6e ROR abs */ AbsoluteReadModifyWrite(OperationROR), /* 0x6f RRA abs */ AbsoluteReadModifyWrite(OperationRRA, OperationADC),
/* 0x70 BVS */ Program(OperationBVS), /* 0x71 ADC ind, y */ IndirectIndexedRead(OperationADC), /* 0x70 BVS */ Program(OperationBVS), /* 0x71 ADC ind, y */ IndirectIndexedRead(OperationADC),
@@ -203,7 +215,105 @@ const ProcessorStorage::MicroOp ProcessorStorage::operations[256][10] = {
/* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS), /* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS),
/* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC), /* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC),
/* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS), /* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS),
}; };
// Install the basic 6502 table.
memcpy(operations_, operations_6502, sizeof(operations_));
// Patch the table according to the chip's personality.
#define Install(location, instructions) {\
const InstructionList code = instructions; \
memcpy(&operations_[location], code, sizeof(InstructionList)); \
}
if(personality != P6502) {
// Add P[L/H][X/Y].
Install(0x5a, Program(CyclePushY));
Install(0xda, Program(CyclePushX));
Install(0x7a, Program(CycleReadFromS, CyclePullY, OperationSetFlagsFromY));
Install(0xfa, Program(CycleReadFromS, CyclePullX, OperationSetFlagsFromX));
// Add BRA.
Install(0x80, Program(OperationBRA));
// Add BBS and BBR. These take five cycles. My guessed breakdown is:
// 1. read opcode
// 2. read operand
// 3. read zero page
// 4. read second operand
// 5. read from PC without top byte fixed yet
// ... with the caveat that (3) and (4) could be the other way around.
for(int location = 0x0f; location <= 0xff; location += 0x10) {
Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS));
}
// Add NOPs.
// The 1-byte, 1-cycle (!) NOPs.
for(int c = 0x03; c <= 0xf3; c += 0x10) {
Install(c, ImpliedNop());
}
for(int c = 0x0b; c <= 0xbb; c += 0x10) {
Install(c, ImpliedNop());
}
for(int c = 0xeb; c <= 0xfb; c += 0x10) {
Install(c, ImpliedNop());
}
// The 2-byte, 2-cycle NOPs that the 6502 doesn't have.
for(int c = 0x02; c <= 0x62; c += 0x10) {
Install(c, ImmediateNop());
}
// Correct JMP (abs) and install JMP (abs, x).
Install(0x6c, Program(CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed));
Install(0x7c, Program(
CycleReadAddressHLoadAddressL, // (3) read second byte of (addr)
CycleAddXToAddressLowRead, // (4) calculate addr+x, read from (addr+x) with high byte not yet calculated
OperationCorrectAddressHigh, CycleReadPCLFromAddress, // (5) read from real (addr+x)
CycleReadPCHFromAddressInc // (6) read from addr+x+1
));
// Add INA and DEA.
Install(0x1a, Program(OperationINA));
Install(0x3a, Program(OperationDEA));
// Add (zp) operations.
Install(0x12, ZeroIndirectRead(OperationORA));
Install(0x32, ZeroIndirectRead(OperationAND));
Install(0x52, ZeroIndirectRead(OperationEOR));
Install(0x72, ZeroIndirectRead(OperationADC));
Install(0x92, ZeroIndirectWrite(OperationSTA));
Install(0xb2, ZeroIndirectRead(OperationLDA));
Install(0xd2, ZeroIndirectRead(OperationCMP));
Install(0xf2, ZeroIndirectRead(OperationSBC));
// Add STZ.
Install(0x9c, AbsoluteWrite(OperationSTZ));
Install(0x9e, AbsoluteXWrite(OperationSTZ));
Install(0x64, ZeroWrite(OperationSTZ));
Install(0x74, ZeroXWrite(OperationSTZ));
// Add the extra BITs.
Install(0x34, ZeroXRead(OperationBIT));
Install(0x3c, AbsoluteXRead(OperationBIT));
Install(0x89, Immediate(OperationBITNoNV));
// Add TRB and TSB.
Install(0x04, ZeroReadModifyWrite(OperationTSB));
Install(0x0c, AbsoluteReadModifyWrite(OperationTSB));
Install(0x14, ZeroReadModifyWrite(OperationTRB));
Install(0x1c, AbsoluteReadModifyWrite(OperationTRB));
// Add RMB and SMB.
for(int c = 0x07; c <= 0x77; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationRMB));
}
for(int c = 0x87; c <= 0xf7; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationSMB));
}
}
#undef Install
}
#undef Program #undef Program
#undef Absolute #undef Absolute
@@ -243,11 +353,3 @@ const ProcessorStorage::MicroOp ProcessorStorage::operations[256][10] = {
#undef IndirectIndexedReadModify #undef IndirectIndexedReadModify
#undef Immediate #undef Immediate
#undef Implied #undef Implied
ProcessorStorage::ProcessorStorage() {
// only the interrupt flag is defined upon reset but get_flags isn't going to
// mask the other flags so we need to do that, at least
carry_flag_ &= Flag::Carry;
decimal_flag_ &= Flag::Decimal;
overflow_flag_ &= Flag::Overflow;
}

View File

@@ -15,7 +15,7 @@
*/ */
class ProcessorStorage { class ProcessorStorage {
protected: protected:
ProcessorStorage(); ProcessorStorage(Personality);
/* /*
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
@@ -25,44 +25,64 @@ class ProcessorStorage {
enum MicroOp { enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH, CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand, CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI, CyclePushX, CyclePushY, OperationSetI,
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector, OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh, CycleReadVectorLow, CycleReadVectorHigh,
CycleReadFromS, CycleReadFromPC, CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA, CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CyclePullX, CyclePullY,
CycleNoWritePush, CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL, CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute, CycleReadAddressHLoadAddressL,
CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed, CycleReadPCHFromAddressInc,
CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow, OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead, CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC, OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA, CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand, CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
CycleFetchAddressLowFromOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR, OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA, OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA, OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSAX, OperationSHA, OperationSTX, OperationSTY, OperationSTZ,
OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP, OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationASL, OperationCPX, OperationCPY, OperationBIT, OperationBITNoNV,
OperationASL, OperationRMB, OperationSMB,
OperationASO, OperationROL, OperationRLA, OperationLSR, OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA, OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD, OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED, OperationINC, OperationSEC, OperationSEI, OperationSED,
OperationDEC, OperationINX, OperationDEX, OperationINY, OperationTRB, OperationTSB,
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
OperationBVS, OperationBCC, OperationBCS, OperationBNE, OperationINC, OperationDEC, OperationINX, OperationDEX,
OperationBEQ, OperationTXA, OperationTYA, OperationTXS, OperationINY, OperationDEY, OperationINA, OperationDEA,
OperationTAY, OperationTAX, OperationTSX, OperationARR,
OperationSBX, OperationLXA, OperationANE, OperationANC, OperationBPL, OperationBMI, OperationBVC, OperationBVS,
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet, OperationBCC, OperationBCS, OperationBNE, OperationBEQ,
OperationBRA, OperationBBRBBS,
OperationTXA, OperationTYA, OperationTXS, OperationTAY,
OperationTAX, OperationTSX,
OperationARR, OperationSBX, OperationLXA, OperationANE,
OperationANC, OperationLAS,
CycleFetchFromHalfUpdatedPC, CycleAddSignedOperandToPC, OperationAddSignedOperandToPC16,
OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags, OperationSetOperandFromFlags,
OperationSetFlagsFromA, OperationSetFlagsFromA, OperationSetFlagsFromX, OperationSetFlagsFromY,
CycleScheduleJam CycleScheduleJam
}; };
static const MicroOp operations[256][10]; using InstructionList = MicroOp[10];
InstructionList operations_[256];
const MicroOp *scheduled_program_counter_ = nullptr; const MicroOp *scheduled_program_counter_ = nullptr;