mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
1250 lines
29 KiB
Swift
1250 lines
29 KiB
Swift
//
|
|
// Z80MachineCycleTests.swift
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 15/06/2017.
|
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
|
|
class Z80MachineCycleTests: XCTestCase {
|
|
|
|
private struct MachineCycle: CustomDebugStringConvertible {
|
|
var operation: CSTestMachineZ80BusOperationCaptureOperation
|
|
var length: Int32
|
|
|
|
public var debugDescription: String {
|
|
get {
|
|
var opName = ""
|
|
switch operation {
|
|
case .readOpcode: opName = "ro"
|
|
case .read: opName = "r"
|
|
case .write: opName = "w"
|
|
case .portRead: opName = "i"
|
|
case .portWrite: opName = "o"
|
|
case .internalOperation: opName = "iop"
|
|
}
|
|
return "\(opName) \(length)"
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private func test(program : [UInt8], busCycles : [MachineCycle]) {
|
|
// 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
|
|
var matches = true
|
|
for cycle in machine.busOperationCaptures {
|
|
let length = cycle.timeStamp - totalCycles
|
|
totalCycles += length
|
|
|
|
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 {
|
|
XCTAssert(length & Int32(1) == 0, "While performing \(machine.busOperationCaptures) Z80 ended on a half cycle")
|
|
|
|
if length != busCycles[index].length*2 || cycle.operation != busCycles[index].operation {
|
|
matches = false
|
|
break;
|
|
}
|
|
}
|
|
|
|
index += 1
|
|
}
|
|
|
|
XCTAssert(matches, "Z80 performed \(machine.busOperationCaptures); was expected to perform \(busCycles)")
|
|
}
|
|
|
|
// LD r, r
|
|
func testLDrs() {
|
|
test(
|
|
program: [0x40],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4)
|
|
]
|
|
)
|
|
}
|
|
|
|
// LD r, n
|
|
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 (IX+d), n
|
|
func testLDIXdn() {
|
|
test(
|
|
program: [0xdd, 0x36, 0x10, 0x80],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 5),
|
|
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)
|
|
func testLDAinn() {
|
|
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
|
|
func testLDinnA() {
|
|
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),
|
|
]
|
|
)
|
|
}
|
|
|
|
// 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: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// POP IX
|
|
func testPOPIX() {
|
|
test(
|
|
program: [0xdd, 0xe1],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, 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),
|
|
]
|
|
)
|
|
}
|
|
|
|
// EXX
|
|
func testEXX() {
|
|
test(
|
|
program: [0xd9],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// EX (SP), HL
|
|
func testEXSPHL() {
|
|
test(
|
|
program: [0xe3],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .write, length: 5),
|
|
]
|
|
)
|
|
}
|
|
|
|
// EX (SP), IX
|
|
func testEXSPIX() {
|
|
test(
|
|
program: [0xdd, 0xe3],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .write, length: 5),
|
|
]
|
|
)
|
|
}
|
|
|
|
// LDI
|
|
func testLDI() {
|
|
test(
|
|
program: [0xed, 0xa0],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .write, length: 5),
|
|
]
|
|
)
|
|
}
|
|
|
|
// CPI (NB: I've diverted from the documentation by assuming the five-cycle 'write' is an internal operation)
|
|
func testCPI() {
|
|
test(
|
|
program: [0xed, 0xa1],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
]
|
|
)
|
|
}
|
|
|
|
// LDIR
|
|
func testLDIR() {
|
|
test(
|
|
program: [
|
|
0x01, 0x02, 0x00, // LD BC, 2
|
|
0xed, 0xb0, // LDIR
|
|
0x00, 0x00, 0x00 // NOP, NOP, NOP
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .write, length: 5),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .write, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// CPIR (as per CPI; assumed no writes)
|
|
func testCPIR() {
|
|
test(
|
|
program: [
|
|
0x01, 0x02, 0x00, // LD BC, 2
|
|
0xed, 0xb1, // CPIR
|
|
0x00, 0x00, 0x00 // NOP, NOP, NOP
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD A,r
|
|
func testADDAr() {
|
|
test(
|
|
program: [0x80],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD A,n
|
|
func testADDAn() {
|
|
test(
|
|
program: [0xc6, 0x00],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD A,(HL)
|
|
func testADDAHL() {
|
|
test(
|
|
program: [0x86],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD A,(IX+d)
|
|
func testADDAIXd() {
|
|
test(
|
|
program: [0xdd, 0x86],
|
|
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),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INC r, DEC r
|
|
func testINCrDECr() {
|
|
test(
|
|
program: [0x3c, 0x3d],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INC (HL), DEC (HL)
|
|
func testINCHLDECHL() {
|
|
test(
|
|
program: [0x34, 0x34],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// DAA, CPL, CCF, SCF, NOP, DI, EI, HALT
|
|
func testDAACPLCCFSCFNOPDIEIHALT() {
|
|
test(
|
|
program: [0x27, 0x2f, 0x3f, 0x37, 0x00, 0xf3, 0xfb, 0x76],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4), // one more for luck
|
|
]
|
|
)
|
|
}
|
|
|
|
// NEG, IM 0, IM 1, IM 2
|
|
func testNEGIMs() {
|
|
test(
|
|
program: [0xed, 0x44, 0xed, 0x46, 0xed, 0x56, 0xed, 0x5e],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD HL, rr
|
|
func testADDHL() {
|
|
test(
|
|
program: [0x09],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD IX, rr
|
|
func testADDIX() {
|
|
test(
|
|
program: [0xdd, 0x09],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// ADD A, (HL)
|
|
func testADCHL() {
|
|
test(
|
|
program: [0xed, 0x4a],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INC rr
|
|
func testINCss() {
|
|
test(
|
|
program: [0x03],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 6),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INC IX
|
|
func testINCIX() {
|
|
test(
|
|
program: [0xdd, 0x23],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 6),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RLCA
|
|
func testRLCA() {
|
|
test(
|
|
program: [0x07],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RLC r
|
|
func testRLCr() {
|
|
test(
|
|
program: [0xcb, 0x00],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RLC (HL)
|
|
func testRLCHL() {
|
|
test(
|
|
program: [0xcb, 0x06],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RLC (IX+d) (NB: the official table doesn't read the final part of the instruction; I've assumed a five-cycle version of read opcode replaces the internal operation)
|
|
func testRLCIX() {
|
|
test(
|
|
program: [0xdd, 0xcb, 0x00, 0x06],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RLD
|
|
func testRLD() {
|
|
test(
|
|
program: [0xed, 0x6f],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// BIT n,r
|
|
func testBITr() {
|
|
test(
|
|
program: [0xcb, 0x40],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// SET n,r
|
|
func testSETr() {
|
|
test(
|
|
program: [0xcb, 0xc0],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// BIT n,(HL)
|
|
func testBITHL() {
|
|
test(
|
|
program: [0xcb, 0x46],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// SET n,(HL)
|
|
func testSETHL() {
|
|
test(
|
|
program: [0xcb, 0xc6],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// BIT n,(IX+d)
|
|
func testBITIX() {
|
|
test(
|
|
program: [0xdd, 0xcb, 0x00, 0x46],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// SET n,(IX+d)
|
|
func testSETIX() {
|
|
test(
|
|
program: [0xdd, 0xcb, 0x00, 0xc6],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JP nn
|
|
func testJPnn() {
|
|
test(
|
|
program: [0xc3, 0x00, 0x80],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JP cc, nn
|
|
func testJPccnn() {
|
|
test(
|
|
program: [
|
|
0x37, // SCF
|
|
0xd2, 0x00, 0x80, // JP NC, 0x8000
|
|
0xda, 0x00, 0x80, // JP C, 0x8000
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JR e
|
|
func testJRe() {
|
|
test(
|
|
program: [0x18, 0x80],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JR cc, e
|
|
func testJRcce() {
|
|
test(
|
|
program: [
|
|
0x37, // SCF
|
|
0x30, 0x80, // JR NC, 0x8000
|
|
0x38, 0x80, // JR C, 0x8000
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JP (HL)
|
|
func testJPHL() {
|
|
test(
|
|
program: [0xe9],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// JP (IX)
|
|
func testJPIX() {
|
|
test(
|
|
program: [0xdd, 0xe9],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// DJNZ
|
|
func testDJNZ() {
|
|
test(
|
|
program: [
|
|
0x06, 0x02, // LD B, 2
|
|
0x10, 0xfe, // DJNZ -2
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// CALL
|
|
func testCALL() {
|
|
test(
|
|
program: [0xcd, 0x00, 0x80],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// CALL cc
|
|
func testCALLcc() {
|
|
test(
|
|
program: [
|
|
0x37, // SCF
|
|
0xd4, 0x00, 0x80, // CALL NC
|
|
0xdc, 0x00, 0x80 // CALL C
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RET
|
|
func testRET() {
|
|
test(
|
|
program: [0xc9],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RET cc
|
|
func testRETcc() {
|
|
test(
|
|
program: [
|
|
0x37, // SCF
|
|
0xd0, // RET NC
|
|
0xd8, // RET C
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RETI
|
|
func testRETI() {
|
|
test(
|
|
program: [0xed, 0x4d],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// RST
|
|
func testRST() {
|
|
test(
|
|
program: [0xc7],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// IN A, (n)
|
|
func testINAn() {
|
|
test(
|
|
program: [0xdb, 0xfe],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .portRead, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// IN r, (C)
|
|
func testINrC() {
|
|
test(
|
|
program: [0xed, 0x40],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .portRead, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INI
|
|
func testINI() {
|
|
test(
|
|
program: [0xed, 0xa2],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .portRead, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// INIR
|
|
func testINIR() {
|
|
test(
|
|
program: [
|
|
0x01, 0x02, 0x00, // LD BC, 2
|
|
0xed, 0xb2, // INIR
|
|
0x00, 0x00, 0x00 // NOP, NOP, NOP
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .portRead, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .portRead, length: 4),
|
|
MachineCycle(operation: .write, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// OUT (n), A
|
|
func testOUTnA() {
|
|
test(
|
|
program: [0xd3, 0xfe],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .portWrite, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// OUT (C), r
|
|
func testOUTCr() {
|
|
test(
|
|
program: [0xed, 0x41],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .portWrite, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// OUTI
|
|
func testOUTI() {
|
|
test(
|
|
program: [0xed, 0xa3],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .portWrite, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
|
|
// OTIR
|
|
func testOTIR() {
|
|
test(
|
|
program: [
|
|
0x01, 0x02, 0x00, // LD BC, 2
|
|
0xed, 0xb3, // OTIR
|
|
0x00, 0x00, 0x00 // NOP, NOP, NOP
|
|
],
|
|
busCycles: [
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .read, length: 3),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .portWrite, length: 4),
|
|
MachineCycle(operation: .internalOperation, length: 5),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
MachineCycle(operation: .readOpcode, length: 5),
|
|
MachineCycle(operation: .read, length: 3),
|
|
MachineCycle(operation: .portWrite, length: 4),
|
|
|
|
MachineCycle(operation: .readOpcode, length: 4),
|
|
]
|
|
)
|
|
}
|
|
}
|