//
//  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: CSTestMachine6502! = nil
	override func setUp() {
		super.setUp()

		// Create a machine full of NOPs.
		machine = CSTestMachine6502(processor: .processor6502)
		for c in 0...65535 {
			machine.setValue(0xea, forAddress: UInt32(c))
		}

		// Set the IRQ vector to 0x1234.
		machine.setValue(0x34, forAddress: 0xfffe)
		machine.setValue(0x12, forAddress: 0xffff)

		// Add a CLI.
		machine.setValue(0x58, forAddress: 0x4000)

		// Begin at 0x4000.
		machine.setValue(0x4000, for: .programCounter)
	}

	func testIRQLine() {
		// Run for six cycles; check that no interrupt has occurred.
		machine.runForNumber(ofCycles: 6)
		XCTAssertEqual(machine.value(for: .programCounter), 0x4003, "No interrupt should have occurred with line low")

		// enable the interrupt line, check that it was too late
		machine.irqLine = true
		machine.runForNumber(ofCycles: 2)
		XCTAssertEqual(machine.value(for: .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.runForNumber(ofCycles: 6)
		XCTAssertNotEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should not yet have begun")
		machine.runForNumber(ofCycles: 1)
		XCTAssertEqual(machine.value(for: .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.runForNumber(ofCycles: 11)

		XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
		XCTAssertEqual(machine.value(for: .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.runForNumber(ofCycles: 4)
		XCTAssertEqual(machine.value(for: .programCounter), 0x4002, "CLI/SEI pair should have been performed in their entirety")

		// run for seven more cycles
		machine.runForNumber(ofCycles: 7)

		// interrupt should have taken place despite SEI
		XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
	}

}
