//
//  6522Tests.swift
//  Clock Signal
//
//  Created by Thomas Harte on 18/06/2016.
//  Copyright 2016 Thomas Harte. All rights reserved.
//

import XCTest
import Foundation

class MOS6522Tests: XCTestCase {

	private var m6522: MOS6522Bridge!

	override func setUp() {
		m6522 = MOS6522Bridge()
	}

	// MARK: Timer tests

	func testTimerCount() {
		// set timer 1 to a value of m652200a
		m6522.setValue(10, forRegister: 4)
		m6522.setValue(0, forRegister: 5)

		// complete the setting cycle
		m6522.run(forHalfCycles: 2)

		// run for 5 cycles
		m6522.run(forHalfCycles: 10)

		// check that the timer has gone down by 5
		XCTAssert(m6522.value(forRegister: 4) == 5, "Low order byte should be 5; was \(m6522.value(forRegister: 4))")
		XCTAssert(m6522.value(forRegister: 5) == 0, "High order byte should be 0; was \(m6522.value(forRegister: 5))")
	}

	func testTimerLatches() {
		// set timer 2 to $1020
		m6522.setValue(0x10, forRegister: 8)
		m6522.setValue(0x20, forRegister: 9)

		// change the low-byte latch
		m6522.setValue(0x40, forRegister: 8)

		// complete the cycle
		m6522.run(forHalfCycles: 2)

		// chek that the new latched value hasn't been copied
		XCTAssert(m6522.value(forRegister: 8) == 0x10, "Low order byte should be 0x10; was \(m6522.value(forRegister: 8))")
		XCTAssert(m6522.value(forRegister: 9) == 0x20, "High order byte should be 0x20; was \(m6522.value(forRegister: 9))")

		// write the low-byte latch
		m6522.setValue(0x50, forRegister: 9)

		// complete the cycle
		m6522.run(forHalfCycles: 2)

		// chek that the latched value has been copied
		XCTAssert(m6522.value(forRegister: 8) == 0x40, "Low order byte should be 0x50; was \(m6522.value(forRegister: 8))")
		XCTAssert(m6522.value(forRegister: 9) == 0x50, "High order byte should be 0x40; was \(m6522.value(forRegister: 9))")
	}

	func testTimerReload() {
		// set timer 1 to a value of m6522010, enable repeating mode
		m6522.setValue(16, forRegister: 4)
		m6522.setValue(0, forRegister: 5)
		m6522.setValue(0x40, forRegister: 11)
		m6522.setValue(0x40 | 0x80, forRegister: 14)

		// complete the cycle to set initial values
		m6522.run(forHalfCycles: 2)

		// run for 16 cycles
		m6522.run(forHalfCycles: 32)

		// check that the timer has gone down to 0 but not yet triggered an interrupt
		XCTAssert(m6522.value(forRegister: 4) == 0, "Low order byte should be 0; was \(m6522.value(forRegister: 4))")
		XCTAssert(m6522.value(forRegister: 5) == 0, "High order byte should be 0; was \(m6522.value(forRegister: 5))")
		XCTAssert(!m6522.irqLine, "IRQ should not yet be active")

		// check that two half-cycles later the timer is $ffff but IRQ still hasn't triggered
		m6522.run(forHalfCycles: 2)
		XCTAssert(m6522.value(forRegister: 4) == 0xff, "Low order byte should be 0xff; was \(m6522.value(forRegister: 4))")
		XCTAssert(m6522.value(forRegister: 5) == 0xff, "High order byte should be 0xff; was \(m6522.value(forRegister: 5))")
		XCTAssert(!m6522.irqLine, "IRQ should not yet be active")

		// check that one half-cycle later the timer is still $ffff and IRQ has triggered...
		m6522.run(forHalfCycles: 1)
		XCTAssert(m6522.irqLine, "IRQ should be active")
		XCTAssert(m6522.value(forRegister: 4) == 0xff, "Low order byte should be 0xff; was \(m6522.value(forRegister: 4))")
		XCTAssert(m6522.value(forRegister: 5) == 0xff, "High order byte should be 0xff; was \(m6522.value(forRegister: 5))")

		// ... but that reading the timer cleared the interrupt
		XCTAssert(!m6522.irqLine, "IRQ should be active")

		// check that one half-cycles later the timer has reloaded
		m6522.run(forHalfCycles: 1)
		XCTAssert(m6522.value(forRegister: 4) == 0x10, "Low order byte should be 0x10; was \(m6522.value(forRegister: 4))")
		XCTAssert(m6522.value(forRegister: 5) == 0x00, "High order byte should be 0x00; was \(m6522.value(forRegister: 5))")
	}


	// MARK: PB7 timer 1 tests
	// These follow the same logic and check for the same results as the VICE VIC-20 via_pb7 tests.

	// Perfoms:
	//
	//	(1) establish initial ACR and port B output value, and grab port B input value.
	//	(2) start timer 1, grab port B input value.
	//	(3) set final ACR, grab port B input value.
	//	(4) allow timer 1 to expire, grab port B input value.
	private func runTest(startACR: UInt8, endACR: UInt8, portBOutput: UInt8) -> [UInt8] {
		var result: [UInt8] = []

		// Clear all register values.
		for n: UInt in 0...15 {
			m6522.setValue(0, forRegister: n)
		}
		m6522.run(forHalfCycles: 2)

		// Set data direction and current port B value.
		m6522.setValue(0xff, forRegister: 2)
		m6522.run(forHalfCycles: 2)
		m6522.setValue(portBOutput, forRegister: 0)
		m6522.run(forHalfCycles: 2)

		// Set initial ACR and grab the current port B value.
		m6522.setValue(startACR, forRegister: 0xb)
		m6522.run(forHalfCycles: 2)
		result.append(m6522.value(forRegister: 0))
		m6522.run(forHalfCycles: 2)

		// Start timer 1 and grab the value.
		m6522.setValue(1, forRegister: 0x5)
		m6522.run(forHalfCycles: 2)
		result.append(m6522.value(forRegister: 0))
		m6522.run(forHalfCycles: 2)

		// Set the final ACR value and grab value.
		m6522.setValue(endACR, forRegister: 0xb)
		m6522.run(forHalfCycles: 2)
		result.append(m6522.value(forRegister: 0))
		m6522.run(forHalfCycles: 2)

		// Make sure timer 1 has expired.
		m6522.run(forHalfCycles: 512)

		// Grab the final value.
		result.append(m6522.value(forRegister: 0))

		return result
	}

	func testTimer1PB7() {
		// Original top row. [original Vic-20 screen output in comments on the right]
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00])	// @@@@	(i.e. 0, 0, 0, 0)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00])	// @@@@	(i.e. 0, 0, 0, 0)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00])	// @@b@	(i.e. 0, 0, 1, 0)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00])	// @@b@	(i.e. 0, 0, 1, 0)

		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff])	// cccc	(i.e. 1, 1, 1, 1)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff])	// cccc	(i.e. 1, 1, 1, 1)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f])	// ccca	(i.e. 1, 1, 1, 0)
		XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f])	// ccca	(i.e. 1, 1, 1, 0)

		// Second row.	[same output as first row]
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00])	// @@@@
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00])	// @@@@
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00])	// @@b@
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00])	// @@b@

		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff])	// cccc
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff])	// cccc
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f])	// ccca
		XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f])	// ccca

		// Third row.
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00])	// b@@@	(i.e. 1, 0, 0, 0)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00])	// b@@@	(i.e. 1, 0, 0, 0)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80])	// b@@b	(i.e. 1, 0, 0, 1)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80])	// b@@b	(i.e. 1, 0, 0, 1)

		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff])	// cacc	(i.e. 1, 0, 1, 1)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff])	// cacc	(i.e. 1, 0, 1, 1)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff])	// caac	(i.e. 1, 0, 0, 1)
		XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff])	// caac	(i.e. 1, 0, 0, 1)

		// Final row.	[same output as third row]
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00])	// b@@@
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00])	// b@@@
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80])	// b@@b
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80])	// b@@b

		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff])	// cacc
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff])	// cacc
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff])	// caac
		XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff])	// caac

		// Conclusions:
		//
		//	after inital ACR and port B value:	[original data if not in PB7 output mode, otherwise 1]
		//	after starting timer 1:				[original data if not in PB7 output mode, otherwise 0]
		//	after final ACR value:				[original data if not in PB7 output mode, 1 if has transitioned to PB7 mode, 0 if was already in PB7 mode]
		//	after timer 1 expiry:				[original data if not in PB7 mode, 1 if timer has expired while in PB7 mode]
		//
		//	i.e.
		//		(1)	there is separate storage for the programmer-set PB7 and the timer output;
		//		(2)	the timer output is reset upon a timer write only if PB7 output is enabled;
		//		(3)	expiry toggles the output.
	}


	// MARK: Data direction tests
	func testDataDirection() {
		// set low four bits of register B as output, the top four as input
		m6522.setValue(0xf0, forRegister: 2)

		// ask to output 0x8c
		m6522.setValue(0x8c, forRegister: 0)

		// complete the cycle
		m6522.run(forHalfCycles: 2)

		// set current input as 0xda
		m6522.portBInput = 0xda

		// test that the result of reading register B is therefore 0x8a
		XCTAssert(m6522.value(forRegister: 0) == 0x8a, "Data direction register should mix input and output; got \(m6522.value(forRegister: 0))")
	}

	func testShiftDisabled() {
		/*
			Mode 0 disables the Shift Register. In this mode the microprocessor can
			write or read the SR and the SR will shift on each CB1 positive edge
			shifting in the value on CB2. In this mode the SR Interrupt Flag is
			disabled (held to a logic 0).
		*/
	}

	func testShiftInUnderT2() {
		/*
			In mode 1, the shifting rate is controlled by the low order 8 bits of T2
			(Figure 22). Shift pulses are generated on the CB1 pin to control shifting
			in external devices. The time between transitions of this output clock is a
			function of the system clock period and the contents of the low order T2
			latch (N).

			The shifting operation is triggered by the read or write of the SR if the
			SR flag is set in the IFR. Otherwise the first shift will occur at the next
			time-out of T2 after a read or write of the SR. Data is shifted first into
			the low order bit of SR and is then shifted into the next higher order bit
			of the shift register on the negative-going edge of each clock pulse. The
			input data should change before the positive-going edge of the CB1 clock
			pulse. This data is shifted into shift register during the 02 clock cycle
			following the positive-going edge of the CB1 clock pulse. After 8 CB1 clock
			pulses, the shift register interrupt flag will set and IRQ will go low.
		*/
	}

	func testShiftInUnderPhase2() {
		/*
			In mode 2, the shift rate is a direct function of the system clock
			frequency (Figure 23). CB1 becomes an output which generates shift pulses
			for controlling external devices. Timer 2 operates as an independent
			interval timer and has no effect on SR. The shifting operation is triggered
			by reading or writing the Shift Register. Data is shifted, first into bit 0
			and is then shifted into the next higher order bit of the shift register on
			the trailing edge of each 02 clock pulse. After 8 clock pulses, the shift
			register interrupt flag will be set, and the output clock pulses on CB1
			will stop.
		*/
	}

	func testShiftInUnderCB1() {
		/*
			In mode 3, external pin CB1 becomes an input (Figure 24). This allows an
			external device to load the shift register at its own pace. The shift
			register counter will interrupt the processor each time 8 bits have been
			shifted in. However the shift register counter does not stop the shifting
			operation; it acts simply as a pulse counter. Reading or writing the Shift
			Register resets the Interrupt Flag and initializes the SR counter to count
			another 8 pulses.

			Note that the data is shifted during the first system clock cycle
			following the positive-going edge of the CB1 shift pulse. For this reason,
			data must be held stable during the first full cycle following CB1 going
			high.
		*/
	}

	func testShiftOutUnderT2FreeRunning() {
		/*
			Mode 4 is very similar to mode 5 in which the shifting rate is set by T2.
			However, in mode 4 the SR Counter does not stop the shifting operation
			(Figure 25). Since the Shift Register bit 7 (SR7) is recirculated back into
			bit 0, the 8 bits loaded into the Shift Register will be clocked onto CB2
			repetitively. In this mode the Shift Register Counter is disabled.
		*/
	}

	func testShiftOutUnderT2() {
		/*
			In mode 5, the shift rate is controlled by T2 (as in mode 4). The shifting
			operation is triggered by the read or write of the SR if the SR flag is set
			in the IFR (Figure 26). Otherwise the first shift will occur at the next
			time-out of T2 after a read or write of the SR. However, with each read or
			write of the shift register the SR Counter is reset and 8 bits are shifted
			onto CB2. At the same time, 8 shift pulses are generated on CB1 to control
			shifting in external devices. After the 8 shift pulses, the shifting is
			disabled, the SR Interrupt Flag is set and CB2 remains at the last data
			level.
		*/
	}

	func testShiftOutUnderPhase2() {
		/*
			In mode 6, the shift rate is controlled by the 02 system clock (Figure 27).

			(... and I'm assuming the same behaviour as shift out under control of T2
			otherwise, based on original context)
		*/
		// Set the shift register to a non-zero something.
		m6522.setValue(0xaa, forRegister: 10)

		// Set shift register mode 6.
		m6522.setValue(6 << 2, forRegister: 11)

		// Make sure the shift register's interrupt bit is set.
		m6522.run(forHalfCycles: 16)
		XCTAssertEqual(m6522.value(forRegister: 13) & 0x04, 0x04)

		// Test that output is now inhibited: CB2 should remain unchanged.
		let initialOutput = m6522.value(forControlLine: .two, port: .B)
		for _ in 1...8 {
			m6522.run(forHalfCycles: 2)
			XCTAssertEqual(m6522.value(forControlLine: .two, port: .B), initialOutput)
		}

		// Set a new value to the shift register.
		m6522.setValue(0x16, forRegister: 10)

		// Test that the new value is shifted out.
		var output = 0
		for _ in 1..<8 {
			m6522.run(forHalfCycles: 2)
			output = (output << 1) | (m6522.value(forControlLine: .two, port: .B) ? 1 : 0)
		}
		XCTAssertEqual(output, 0x16)
	}

	func testShiftOutUnderCB1() {
		/*
			In mode 7, shifting is controlled by pulses applied to the CB1 pin by an
			external device (Figure 28). The SR counter sets the SR Interrupt Flag each
			time it counts 8 pulses but it does not disable the shifting function. Each
			time the microprocessor, writes or reads the shift register, the SR
			Interrupt Flag is reset and the SR counter is initialized to begin counting
			the next 8 shift pulses on pin CB1. After 8 shift pulses, the Interrupt
			Flag is set. The microprocessor can then load the shift register with the
			next byte of data.
		*/
	}
}
