mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-25 15:28:58 +00:00
365 lines
16 KiB
Swift
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.
|
|
*/
|
|
}
|
|
}
|