2017-06-15 23:06:59 +00:00
|
|
|
//
|
|
|
|
// Z80MachineCycleTests.swift
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 15/06/2017.
|
|
|
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import XCTest
|
|
|
|
|
|
|
|
class Z80MachineCycleTests: XCTestCase {
|
|
|
|
|
2017-06-16 00:59:59 +00:00
|
|
|
private struct MachineCycle {
|
2017-06-15 23:06:59 +00:00
|
|
|
var operation: CSTestMachineZ80BusOperationCaptureOperation
|
|
|
|
var length: Int32
|
|
|
|
}
|
|
|
|
|
2017-06-16 00:59:59 +00:00
|
|
|
private func test(program : [UInt8], busCycles : [MachineCycle]) {
|
2017-06-15 23:06:59 +00:00
|
|
|
// Create a machine and install the supplied program at address 0, setting the PC to run from there
|
|
|
|
let machine = CSTestMachineZ80()
|
|
|
|
machine.setValue(0x0000, for: .programCounter)
|
|
|
|
machine.setData(Data(bytes: program), atAddress: 0x0000)
|
|
|
|
|
|
|
|
// Figure out the total number of cycles implied by the bus cycles
|
|
|
|
var totalCycles: Int32 = 0
|
|
|
|
for cycle in busCycles {
|
|
|
|
totalCycles += Int32(cycle.length)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the machine, capturing bus activity
|
|
|
|
machine.captureBusActivity = true
|
|
|
|
machine.runForNumber(ofCycles: totalCycles)
|
|
|
|
|
|
|
|
// Check the results
|
|
|
|
totalCycles = 0
|
|
|
|
var index = 0
|
|
|
|
for cycle in machine.busOperationCaptures {
|
|
|
|
let length = cycle.timeStamp - totalCycles
|
|
|
|
totalCycles += length
|
|
|
|
|
2017-06-16 02:33:46 +00:00
|
|
|
if index >= busCycles.count {
|
|
|
|
// this can't be reached without one of the asserts failing;
|
|
|
|
// it's to prevent an unintended exeception via out-of-bounds
|
|
|
|
// array access
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
XCTAssertEqual(length, busCycles[index].length)
|
|
|
|
XCTAssertEqual(cycle.operation, busCycles[index].operation)
|
|
|
|
}
|
2017-06-15 23:06:59 +00:00
|
|
|
|
|
|
|
index += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 00:59:59 +00:00
|
|
|
// LD r, r
|
2017-06-15 23:06:59 +00:00
|
|
|
func testLDrs() {
|
|
|
|
test(
|
|
|
|
program: [0x40],
|
|
|
|
busCycles: [
|
2017-06-16 00:59:59 +00:00
|
|
|
MachineCycle(operation: .readOpcode, length: 4)
|
2017-06-15 23:06:59 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2017-06-16 02:19:49 +00:00
|
|
|
// LD r, n
|
2017-06-16 00:59:59 +00:00
|
|
|
func testLDrn() {
|
|
|
|
test(
|
|
|
|
program: [0x3e, 0x00],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD r, (HL)
|
|
|
|
func testLDrHL() {
|
|
|
|
test(
|
|
|
|
program: [0x46],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (HL), r
|
|
|
|
func testLDHLr() {
|
|
|
|
test(
|
|
|
|
program: [0x70],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD r, (IX+d)
|
|
|
|
func testLDrIXd() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0x7e, 0x10],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (IX+d), r
|
|
|
|
func testLDIXdr() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0x70, 0x10],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (HL), n
|
|
|
|
func testLDHLn() {
|
|
|
|
test(
|
|
|
|
program: [0x36, 0x10],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD A, (DE)
|
|
|
|
func testLDADE() {
|
|
|
|
test(
|
|
|
|
program: [0x1a],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (DE), A
|
|
|
|
func testLDDEA() {
|
|
|
|
test(
|
|
|
|
program: [0x12],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD A, (nn)
|
2017-06-16 02:19:49 +00:00
|
|
|
func testLDAinn() {
|
2017-06-16 00:59:59 +00:00
|
|
|
test(
|
|
|
|
program: [0x3a, 0x23, 0x45],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (nn), A
|
2017-06-16 02:19:49 +00:00
|
|
|
func testLDinnA() {
|
2017-06-16 00:59:59 +00:00
|
|
|
test(
|
|
|
|
program: [0x32, 0x23, 0x45],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD A, I
|
|
|
|
func testLDAI() {
|
|
|
|
test(
|
|
|
|
program: [0xed, 0x57],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD I, A
|
|
|
|
func testLDIA() {
|
|
|
|
test(
|
|
|
|
program: [0xed, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2017-06-16 02:19:49 +00:00
|
|
|
|
|
|
|
// LD dd, nn
|
|
|
|
func testLDddnn() {
|
|
|
|
test(
|
|
|
|
program: [0x01, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD IX, nn
|
|
|
|
func testLDIXnn() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0x21, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD HL, (nn)
|
|
|
|
func testLDHLinn() {
|
|
|
|
test(
|
|
|
|
program: [0x2a, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (nn), HL
|
|
|
|
func testLDinnHL() {
|
|
|
|
test(
|
|
|
|
program: [0x22, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD dd, (nn)
|
|
|
|
func testLDddinn() {
|
|
|
|
test(
|
|
|
|
program: [0xed, 0x4b, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (nn), dd
|
|
|
|
func testLDinndd() {
|
|
|
|
test(
|
|
|
|
program: [0xed, 0x43, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD IX, (nn)
|
|
|
|
func testLDIXinn() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0x2a, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD (nn), IX
|
|
|
|
func testLDinnIX() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0x22, 0x12, 0x47],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD SP, HL
|
|
|
|
func testLDSPHL() {
|
|
|
|
test(
|
|
|
|
program: [0xf9],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 6),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LD SP, IX
|
|
|
|
func testLDSPIX() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0xf9],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 6),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PUSH qq
|
|
|
|
func testPUSHqq() {
|
|
|
|
test(
|
|
|
|
program: [0xc5],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PUSH IX
|
|
|
|
func testPUSHIX() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0xe5],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// POP qq
|
|
|
|
func testPOPqq() {
|
|
|
|
test(
|
|
|
|
program: [0xe1],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// POP IX
|
|
|
|
func testPOPIX() {
|
|
|
|
test(
|
|
|
|
program: [0xdd, 0xe1],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EX DE, HL
|
|
|
|
func testEXDEHL() {
|
|
|
|
test(
|
|
|
|
program: [0xeb],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EX AF, AF'
|
|
|
|
func testEXAFAFDd() {
|
|
|
|
test(
|
|
|
|
program: [0x08],
|
|
|
|
busCycles: [
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
2017-06-15 23:06:59 +00:00
|
|
|
}
|