mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-18 23:23:56 +00:00
Merge pull request #35 from TomHarte/BRKDiversion
Adds a small amount of test coverage to the functioning of 6502 interrupts
This commit is contained in:
commit
7f10616be9
@ -69,7 +69,7 @@ void MOS6560::set_output_mode(OutputMode output_mode)
|
|||||||
chrominances = ntsc_chrominances;
|
chrominances = ntsc_chrominances;
|
||||||
display_type = Outputs::CRT::NTSC60;
|
display_type = Outputs::CRT::NTSC60;
|
||||||
_timing.cycles_per_line = 65;
|
_timing.cycles_per_line = 65;
|
||||||
_timing.line_counter_increment_offset = 65 - 36; // TODO: 36 is from memory; need to look up.
|
_timing.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
|
||||||
_timing.lines_per_progressive_field = 261;
|
_timing.lines_per_progressive_field = 261;
|
||||||
_timing.supports_interlacing = true;
|
_timing.supports_interlacing = true;
|
||||||
break;
|
break;
|
||||||
|
@ -28,7 +28,6 @@ Machine::Machine() :
|
|||||||
_is_pal_region(false)
|
_is_pal_region(false)
|
||||||
{
|
{
|
||||||
memset(_collisions, 0xff, sizeof(_collisions));
|
memset(_collisions, 0xff, sizeof(_collisions));
|
||||||
set_reset_line(true);
|
|
||||||
setup_reported_collisions();
|
setup_reported_collisions();
|
||||||
|
|
||||||
for(int vbextend = 0; vbextend < 2; vbextend++)
|
for(int vbextend = 0; vbextend < 2; vbextend++)
|
||||||
@ -414,8 +413,6 @@ void Machine::output_pixels(unsigned int count)
|
|||||||
|
|
||||||
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
||||||
{
|
{
|
||||||
set_reset_line(false);
|
|
||||||
|
|
||||||
uint8_t returnValue = 0xff;
|
uint8_t returnValue = 0xff;
|
||||||
unsigned int cycles_run_for = 3;
|
unsigned int cycles_run_for = 3;
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ Machine::Machine() :
|
|||||||
memset(_roms[c], 0xff, 16384);
|
memset(_roms[c], 0xff, 16384);
|
||||||
|
|
||||||
_tape.set_delegate(this);
|
_tape.set_delegate(this);
|
||||||
set_reset_line(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::setup_output(float aspect_ratio)
|
void Machine::setup_output(float aspect_ratio)
|
||||||
@ -86,7 +85,6 @@ void Machine::close_output()
|
|||||||
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
||||||
{
|
{
|
||||||
unsigned int cycles = 1;
|
unsigned int cycles = 1;
|
||||||
set_reset_line(false);
|
|
||||||
|
|
||||||
if(address < 0x8000)
|
if(address < 0x8000)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,6 @@ Machine::Machine() :
|
|||||||
_userPortVIA.set_delegate(this);
|
_userPortVIA.set_delegate(this);
|
||||||
_keyboardVIA.set_delegate(this);
|
_keyboardVIA.set_delegate(this);
|
||||||
_tape.set_delegate(this);
|
_tape.set_delegate(this);
|
||||||
set_reset_line(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::~Machine()
|
Machine::~Machine()
|
||||||
@ -28,8 +27,6 @@ Machine::~Machine()
|
|||||||
|
|
||||||
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
||||||
{
|
{
|
||||||
set_reset_line(false);
|
|
||||||
|
|
||||||
// test for PC at F92F
|
// test for PC at F92F
|
||||||
if(_use_fast_tape_hack && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
|
if(_use_fast_tape_hack && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B73C7191D036BD90074D992 /* Vic20Document.swift */; };
|
4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B73C7191D036BD90074D992 /* Vic20Document.swift */; };
|
||||||
4B73C71D1D036C030074D992 /* Vic20Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B73C71B1D036C030074D992 /* Vic20Document.xib */; };
|
4B73C71D1D036C030074D992 /* Vic20Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B73C71B1D036C030074D992 /* Vic20Document.xib */; };
|
||||||
4B886FF21D03B517004291C3 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B886FF01D03B517004291C3 /* Vic20.cpp */; };
|
4B886FF21D03B517004291C3 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B886FF01D03B517004291C3 /* Vic20.cpp */; };
|
||||||
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
|
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
|
||||||
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
|
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
|
||||||
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
|
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
|
||||||
4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BB297E31B587D8300A49093 /* TestMachine.mm */; };
|
4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BB297E31B587D8300A49093 /* TestMachine.mm */; };
|
||||||
@ -323,6 +323,7 @@
|
|||||||
4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; };
|
4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; };
|
||||||
4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
||||||
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; };
|
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; };
|
||||||
|
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; };
|
||||||
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
|
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@ -397,7 +398,7 @@
|
|||||||
4B73C71C1D036C030074D992 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Vic20Document.xib"; sourceTree = SOURCE_ROOT; };
|
4B73C71C1D036C030074D992 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Vic20Document.xib"; sourceTree = SOURCE_ROOT; };
|
||||||
4B886FF01D03B517004291C3 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vic20.cpp; path = "Vic-20/Vic20.cpp"; sourceTree = "<group>"; };
|
4B886FF01D03B517004291C3 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vic20.cpp; path = "Vic-20/Vic20.cpp"; sourceTree = "<group>"; };
|
||||||
4B886FF11D03B517004291C3 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Vic20.hpp; path = "Vic-20/Vic20.hpp"; sourceTree = "<group>"; };
|
4B886FF11D03B517004291C3 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Vic20.hpp; path = "Vic-20/Vic20.hpp"; sourceTree = "<group>"; };
|
||||||
4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = "<group>"; };
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = "<group>"; };
|
||||||
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
4BB297E01B587D8300A49093 /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = 6502_functional_test.bin; sourceTree = "<group>"; };
|
4BB297E01B587D8300A49093 /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = 6502_functional_test.bin; sourceTree = "<group>"; };
|
||||||
4BB297E11B587D8300A49093 /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = AllSuiteA.bin; sourceTree = "<group>"; };
|
4BB297E11B587D8300A49093 /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = AllSuiteA.bin; sourceTree = "<group>"; };
|
||||||
@ -708,6 +709,7 @@
|
|||||||
4BC9DF441D044FCA00F44158 /* ROMImages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ROMImages; path = ../../../../ROMImages; sourceTree = "<group>"; };
|
4BC9DF441D044FCA00F44158 /* ROMImages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ROMImages; path = ../../../../ROMImages; sourceTree = "<group>"; };
|
||||||
4BC9DF4D1D04691600F44158 /* 6560.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6560.cpp; sourceTree = "<group>"; };
|
4BC9DF4D1D04691600F44158 /* 6560.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6560.cpp; sourceTree = "<group>"; };
|
||||||
4BC9DF4E1D04691600F44158 /* 6560.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6560.hpp; sourceTree = "<group>"; };
|
4BC9DF4E1D04691600F44158 /* 6560.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6560.hpp; sourceTree = "<group>"; };
|
||||||
|
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502InterruptTests.swift; sourceTree = "<group>"; };
|
||||||
4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.hpp; sourceTree = "<group>"; };
|
4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.hpp; sourceTree = "<group>"; };
|
||||||
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
||||||
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; };
|
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; };
|
||||||
@ -1253,9 +1255,10 @@
|
|||||||
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
||||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||||
4B92EAC91B7C112B00246143 /* TimingTests.swift */,
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||||
|
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||||
);
|
);
|
||||||
path = "Clock SignalTests";
|
path = "Clock SignalTests";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1814,7 +1817,8 @@
|
|||||||
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */,
|
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */,
|
||||||
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */,
|
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */,
|
||||||
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
|
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
|
||||||
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */,
|
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */,
|
||||||
|
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
|
||||||
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
||||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
|
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
|
||||||
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
|
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
|
||||||
|
73
OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift
Normal file
73
OSBindings/Mac/Clock SignalTests/6502InterruptTests.swift
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// 6502InterruptTests.swift
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 28/06/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class MOS6502InterruptTests: XCTestCase {
|
||||||
|
|
||||||
|
var machine: CSTestMachine! = nil
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
// create a machine full of NOPs
|
||||||
|
machine = CSTestMachine()
|
||||||
|
for c in 0...65535 {
|
||||||
|
machine.setValue(0xea, forAddress: UInt16(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the IRQ vector to be 0x1234
|
||||||
|
machine.setValue(0x34, forAddress: 0xfffe)
|
||||||
|
machine.setValue(0x12, forAddress: 0xffff)
|
||||||
|
|
||||||
|
// add a CLI
|
||||||
|
machine.setValue(0x58, forAddress: 0x4000)
|
||||||
|
|
||||||
|
// pick things off at 0x4000
|
||||||
|
machine.setValue(0x4000, forRegister: CSTestMachineRegister.ProgramCounter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIRQLine() {
|
||||||
|
// run for six cycles; check that no interrupt has occurred
|
||||||
|
machine.runForNumberOfCycles(6)
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x4003, "No interrupt should have occurred with line low")
|
||||||
|
|
||||||
|
// enable the interrupt line, check that it was too late
|
||||||
|
machine.irqLine = true
|
||||||
|
machine.runForNumberOfCycles(2)
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
|
||||||
|
|
||||||
|
// run for a further 7 cycles, confirm that the IRQ vector was jumped to
|
||||||
|
machine.runForNumberOfCycles(7)
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIFlagSet() {
|
||||||
|
// enable the interrupt line, run for eleven cycles to get past the CLIP and the following NOP and into the interrupt routine
|
||||||
|
machine.irqLine = true
|
||||||
|
machine.runForNumberOfCycles(11)
|
||||||
|
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||||
|
XCTAssert(machine.valueForRegister(.Flags) & 0x04 == 0x04, "Interrupt status flag should be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCLISEIFlagClear() {
|
||||||
|
// set up an SEI as the second instruction, enable the IRQ line
|
||||||
|
machine.setValue(0x78, forAddress: 0x4001)
|
||||||
|
machine.irqLine = true
|
||||||
|
|
||||||
|
// run for four cycles; the CLI and SEI should have been performed
|
||||||
|
machine.runForNumberOfCycles(4)
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x4002, "CLI/SEI pair should have been performed in their entirety")
|
||||||
|
|
||||||
|
// run for seven more cycles
|
||||||
|
machine.runForNumberOfCycles(7)
|
||||||
|
|
||||||
|
// interrupt should have taken place despite SEI
|
||||||
|
XCTAssert(machine.valueForRegister(.ProgramCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class TimingTests: XCTestCase, CSTestMachineJamHandler {
|
class MOS6502TimingTests: XCTestCase, CSTestMachineJamHandler {
|
||||||
|
|
||||||
private var endTime: UInt32 = 0
|
private var endTime: UInt32 = 0
|
||||||
|
|
@ -46,12 +46,8 @@ class KlausDormannTests: XCTestCase {
|
|||||||
|
|
||||||
if newPC == oldPC {
|
if newPC == oldPC {
|
||||||
let error = errorForTrapAddress(oldPC)
|
let error = errorForTrapAddress(oldPC)
|
||||||
|
XCTAssert(error == nil, "Failed with error \(error)")
|
||||||
if let error = error {
|
return
|
||||||
NSException(name: "Failed test", reason: error, userInfo: nil).raise()
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,5 +41,7 @@ extern const uint8_t CSTestMachineJamOpcode;
|
|||||||
@property (nonatomic, readonly) BOOL isJammed;
|
@property (nonatomic, readonly) BOOL isJammed;
|
||||||
@property (nonatomic, readonly) uint32_t timestamp;
|
@property (nonatomic, readonly) uint32_t timestamp;
|
||||||
@property (nonatomic, weak) id <CSTestMachineJamHandler> jamHandler;
|
@property (nonatomic, weak) id <CSTestMachineJamHandler> jamHandler;
|
||||||
|
@property (nonatomic, assign) BOOL irqLine;
|
||||||
|
@property (nonatomic, assign) BOOL nmiLine;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -99,4 +99,14 @@ class MachineJamHandler: public CPU6502::AllRAMProcessor::JamHandler {
|
|||||||
return _processor.get_timestamp();
|
return _processor.get_timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setIrqLine:(BOOL)irqLine {
|
||||||
|
_irqLine = irqLine;
|
||||||
|
_processor.set_irq_line(irqLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setNmiLine:(BOOL)nmiLine {
|
||||||
|
_nmiLine = nmiLine;
|
||||||
|
_processor.set_nmi_line(nmiLine);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -92,9 +92,14 @@ template <class T> class Processor {
|
|||||||
enum MicroOp {
|
enum MicroOp {
|
||||||
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
|
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
|
||||||
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
|
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
|
||||||
CycleSetIReadBRKLow, CycleReadBRKHigh,
|
OperationSetI,
|
||||||
|
|
||||||
|
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
|
||||||
|
CycleReadVectorLow, CycleReadVectorHigh,
|
||||||
|
|
||||||
CycleReadFromS, CycleReadFromPC,
|
CycleReadFromS, CycleReadFromPC,
|
||||||
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
|
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
|
||||||
|
CycleNoWritePush,
|
||||||
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
|
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
|
||||||
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
|
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
|
||||||
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
|
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
|
||||||
@ -121,7 +126,6 @@ template <class T> class Processor {
|
|||||||
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
|
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
OperationSetFlagsFromA,
|
OperationSetFlagsFromA,
|
||||||
CycleReadRSTLow, CycleReadRSTHigh, CycleReadNMILow, CycleReadNMIHigh,
|
|
||||||
CycleScheduleJam
|
CycleScheduleJam
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -271,7 +275,7 @@ template <class T> class Processor {
|
|||||||
|
|
||||||
static const MicroOp operations[256][10] = {
|
static const MicroOp operations[256][10] = {
|
||||||
|
|
||||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, CycleSetIReadBRKLow, CycleReadBRKHigh),
|
/* 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),
|
||||||
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
||||||
@ -455,11 +459,19 @@ template <class T> class Processor {
|
|||||||
|
|
||||||
int _cycles_left_to_run;
|
int _cycles_left_to_run;
|
||||||
|
|
||||||
bool _ready_line_is_enabled;
|
enum InterruptRequestFlags: uint8_t {
|
||||||
bool _reset_line_is_enabled;
|
Reset = 0x80,
|
||||||
bool _irq_line_is_enabled, _irq_request_history[2];
|
IRQ = 0x40,
|
||||||
bool _nmi_line_is_enabled;
|
NMI = 0x20,
|
||||||
|
|
||||||
|
PowerOn = 0x10,
|
||||||
|
};
|
||||||
|
uint8_t _interrupt_requests;
|
||||||
|
|
||||||
bool _ready_is_active;
|
bool _ready_is_active;
|
||||||
|
bool _ready_line_is_enabled;
|
||||||
|
|
||||||
|
bool _irq_line_is_enabled, _irq_request_history;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Gets the program representing an RST response.
|
Gets the program representing an RST response.
|
||||||
@ -470,11 +482,12 @@ template <class T> class Processor {
|
|||||||
static const MicroOp reset[] = {
|
static const MicroOp reset[] = {
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
CyclePullOperand,
|
CycleNoWritePush,
|
||||||
CyclePullOperand,
|
CycleNoWritePush,
|
||||||
CyclePullOperand,
|
OperationRSTPickVector,
|
||||||
CycleReadRSTLow,
|
CycleNoWritePush,
|
||||||
CycleReadRSTHigh,
|
CycleReadVectorLow,
|
||||||
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
};
|
};
|
||||||
return reset;
|
return reset;
|
||||||
@ -487,12 +500,16 @@ template <class T> class Processor {
|
|||||||
*/
|
*/
|
||||||
inline const MicroOp *get_irq_program() {
|
inline const MicroOp *get_irq_program() {
|
||||||
static const MicroOp reset[] = {
|
static const MicroOp reset[] = {
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleFetchOperand,
|
||||||
CyclePushPCH,
|
CyclePushPCH,
|
||||||
CyclePushPCL,
|
CyclePushPCL,
|
||||||
|
OperationBRKPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
CycleSetIReadBRKLow,
|
OperationSetI,
|
||||||
CycleReadBRKHigh,
|
CycleReadVectorLow,
|
||||||
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
};
|
};
|
||||||
return reset;
|
return reset;
|
||||||
@ -505,12 +522,15 @@ template <class T> class Processor {
|
|||||||
*/
|
*/
|
||||||
inline const MicroOp *get_nmi_program() {
|
inline const MicroOp *get_nmi_program() {
|
||||||
static const MicroOp reset[] = {
|
static const MicroOp reset[] = {
|
||||||
|
CycleFetchOperand,
|
||||||
|
CycleFetchOperand,
|
||||||
CyclePushPCH,
|
CyclePushPCH,
|
||||||
CyclePushPCL,
|
CyclePushPCL,
|
||||||
|
OperationNMIPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
CycleReadNMILow,
|
CycleReadVectorLow,
|
||||||
CycleReadNMIHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
};
|
};
|
||||||
return reset;
|
return reset;
|
||||||
@ -528,8 +548,8 @@ template <class T> class Processor {
|
|||||||
_scheduledPrograms{nullptr, nullptr, nullptr, nullptr},
|
_scheduledPrograms{nullptr, nullptr, nullptr, nullptr},
|
||||||
_interruptFlag(Flag::Interrupt),
|
_interruptFlag(Flag::Interrupt),
|
||||||
_s(0),
|
_s(0),
|
||||||
_nextBusOperation(BusOperation::None)
|
_nextBusOperation(BusOperation::None),
|
||||||
|
_interrupt_requests(InterruptRequestFlags::PowerOn)
|
||||||
{
|
{
|
||||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
// 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
|
// mask the other flags so we need to do that, at least
|
||||||
@ -576,13 +596,16 @@ template <class T> class Processor {
|
|||||||
#define checkSchedule(op) \
|
#define checkSchedule(op) \
|
||||||
if(!_scheduledPrograms[scheduleProgramsReadPointer]) {\
|
if(!_scheduledPrograms[scheduleProgramsReadPointer]) {\
|
||||||
scheduleProgramsReadPointer = _scheduleProgramsWritePointer = scheduleProgramProgramCounter = 0;\
|
scheduleProgramsReadPointer = _scheduleProgramsWritePointer = scheduleProgramProgramCounter = 0;\
|
||||||
if(_reset_line_is_enabled) {\
|
if(_interrupt_requests) {\
|
||||||
schedule_program(get_reset_program());\
|
if(_interrupt_requests & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
|
||||||
} else if(_nmi_line_is_enabled) {\
|
_interrupt_requests &= ~InterruptRequestFlags::PowerOn;\
|
||||||
_nmi_line_is_enabled = false;\
|
schedule_program(get_reset_program());\
|
||||||
schedule_program(get_nmi_program());\
|
} else if(_interrupt_requests & InterruptRequestFlags::NMI) {\
|
||||||
} else if(_irq_request_history[0]) {\
|
_interrupt_requests &= ~InterruptRequestFlags::NMI;\
|
||||||
schedule_program(get_irq_program());\
|
schedule_program(get_nmi_program());\
|
||||||
|
} else if(_interrupt_requests & InterruptRequestFlags::IRQ) {\
|
||||||
|
schedule_program(get_irq_program());\
|
||||||
|
} \
|
||||||
} else {\
|
} else {\
|
||||||
schedule_program(fetch_decode_execute);\
|
schedule_program(fetch_decode_execute);\
|
||||||
}\
|
}\
|
||||||
@ -602,8 +625,8 @@ template <class T> class Processor {
|
|||||||
while (!_ready_is_active && number_of_cycles > 0) {
|
while (!_ready_is_active && number_of_cycles > 0) {
|
||||||
|
|
||||||
if(nextBusOperation != BusOperation::None) {
|
if(nextBusOperation != BusOperation::None) {
|
||||||
_irq_request_history[0] = _irq_request_history[1];
|
_interrupt_requests = (_interrupt_requests & ~InterruptRequestFlags::IRQ) | (_irq_request_history ? InterruptRequestFlags::IRQ : 0);
|
||||||
_irq_request_history[1] = _irq_line_is_enabled && !_interruptFlag;
|
_irq_request_history = _irq_line_is_enabled && !_interruptFlag;
|
||||||
number_of_cycles -= static_cast<T *>(this)->perform_bus_operation(nextBusOperation, busAddress, busValue);
|
number_of_cycles -= static_cast<T *>(this)->perform_bus_operation(nextBusOperation, busAddress, busValue);
|
||||||
nextBusOperation = BusOperation::None;
|
nextBusOperation = BusOperation::None;
|
||||||
}
|
}
|
||||||
@ -665,18 +688,28 @@ template <class T> class Processor {
|
|||||||
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 CycleNoWritePush:
|
||||||
|
{
|
||||||
|
uint16_t targetAddress = _s | 0x100; _s--;
|
||||||
|
read_mem(_operand, targetAddress);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
#undef push
|
#undef push
|
||||||
|
|
||||||
case CycleReadFromS: throwaway_read(_s | 0x100); break;
|
case CycleReadFromS: throwaway_read(_s | 0x100); break;
|
||||||
case CycleReadFromPC: throwaway_read(_pc.full); break;
|
case CycleReadFromPC: throwaway_read(_pc.full); break;
|
||||||
|
|
||||||
case CycleReadNMILow: read_mem(_pc.bytes.low, 0xfffa); break;
|
case OperationBRKPickVector:
|
||||||
case CycleReadNMIHigh: read_mem(_pc.bytes.high, 0xfffb); break;
|
// NMI can usurp BRK-vector operations
|
||||||
case CycleReadRSTLow: read_mem(_pc.bytes.low, 0xfffc); break;
|
nextAddress.full = (_interrupt_requests & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||||
case CycleReadRSTHigh: read_mem(_pc.bytes.high, 0xfffd); break;
|
_interrupt_requests &= ~InterruptRequestFlags::NMI; // TODO: this probably doesn't happen now?
|
||||||
case CycleSetIReadBRKLow: _interruptFlag = Flag::Interrupt; read_mem(_pc.bytes.low, 0xfffe); break;
|
break;
|
||||||
case CycleReadBRKHigh: read_mem(_pc.bytes.high, 0xffff); break;
|
case OperationNMIPickVector: nextAddress.full = 0xfffa; break;
|
||||||
|
case OperationRSTPickVector: nextAddress.full = 0xfffc; break;
|
||||||
|
case CycleReadVectorLow: read_mem(_pc.bytes.low, nextAddress.full); break;
|
||||||
|
case CycleReadVectorHigh: read_mem(_pc.bytes.high, nextAddress.full+1); break;
|
||||||
|
case OperationSetI: _interruptFlag = Flag::Interrupt; break;
|
||||||
|
|
||||||
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;
|
||||||
@ -1171,7 +1204,7 @@ template <class T> class Processor {
|
|||||||
*/
|
*/
|
||||||
inline void set_reset_line(bool active)
|
inline void set_reset_line(bool active)
|
||||||
{
|
{
|
||||||
_reset_line_is_enabled = active;
|
_interrupt_requests = (_interrupt_requests & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1181,7 +1214,16 @@ template <class T> class Processor {
|
|||||||
*/
|
*/
|
||||||
inline bool get_reset_line()
|
inline bool get_reset_line()
|
||||||
{
|
{
|
||||||
return _reset_line_is_enabled;
|
return !!(_interrupt_requests & InterruptRequestFlags::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
|
||||||
|
reset at the first opportunity. Use @c set_power_on to disable that behaviour.
|
||||||
|
*/
|
||||||
|
inline void set_power_on(bool active)
|
||||||
|
{
|
||||||
|
_interrupt_requests = (_interrupt_requests & ~InterruptRequestFlags::PowerOn) | (active ? InterruptRequestFlags::PowerOn : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1202,7 +1244,7 @@ template <class T> class Processor {
|
|||||||
inline void set_nmi_line(bool active)
|
inline void set_nmi_line(bool active)
|
||||||
{
|
{
|
||||||
// NMI is edge triggered, not level
|
// NMI is edge triggered, not level
|
||||||
_nmi_line_is_enabled |= active;
|
_interrupt_requests |= (active ? InterruptRequestFlags::NMI : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -12,7 +12,10 @@
|
|||||||
|
|
||||||
using namespace CPU6502;
|
using namespace CPU6502;
|
||||||
|
|
||||||
AllRAMProcessor::AllRAMProcessor() : _timestamp(0) {}
|
AllRAMProcessor::AllRAMProcessor() : _timestamp(0)
|
||||||
|
{
|
||||||
|
set_power_on(false);
|
||||||
|
}
|
||||||
|
|
||||||
int AllRAMProcessor::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
int AllRAMProcessor::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user