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:
@@ -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_) {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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),
|
||||||
|
@@ -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),
|
||||||
|
@@ -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_));
|
||||||
|
@@ -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_),
|
||||||
|
@@ -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 */,
|
||||||
|
@@ -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))
|
||||||
}
|
}
|
||||||
|
@@ -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] = [
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
Binary file not shown.
@@ -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!)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 = ""
|
||||||
|
@@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user