// // 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), ] ) } }