//
//  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()
		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, for: CSTestMachine6502Register.programCounter)
	}

    func testIRQLine() {
		// run for six cycles; check that no interrupt has occurred
		machine.runForNumber(ofCycles: 6)
		XCTAssert(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)
		XCTAssert(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)
		XCTAssert(machine.value(for: .programCounter) != 0x1234, "Interrupt routine should not yet have begun")
		machine.runForNumber(ofCycles: 1)
		XCTAssert(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)

		XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
		XCTAssert(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)
		XCTAssert(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
		XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
	}
}
