1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00
CLK/OSBindings/Mac/Clock SignalTests/6522Tests.swift
2023-05-12 14:14:45 -04:00

365 lines
16 KiB
Swift

//
// 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.
*/
}
}