diff --git a/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift b/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift index 87e4249a7..c3760f0b2 100644 --- a/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift +++ b/OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift @@ -87,6 +87,8 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler { func testFull() { runTest("z80full") + // Current status: 002 of 152 tests failed. + // Specifically: BIT N,(HL) and BIT N,[R,(HL)], i.e. MEMPTR by proxy. } func testMemptr() { diff --git a/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift b/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift index 8a815c6d5..5e0d37ad2 100644 --- a/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift +++ b/OSBindings/Mac/Clock SignalTests/Z80MemptrTests.swift @@ -8,10 +8,10 @@ import XCTest -class Z80MemptrTests: XCTestCase { - private let machine = CSTestMachineZ80() +class Z80MemptrTester: XCTestCase { + let machine = CSTestMachineZ80() - private func test(program : [UInt8], length : Int32, initialValue : UInt16) -> UInt16 { + private func test(program : [UInt8], initialValue : UInt16) -> UInt16 { // Create a machine and install the supplied program at address 0, setting the PC to run from there machine.setValue(0x0000, for: .programCounter) machine.setData(Data(_: program), atAddress: 0x0000) @@ -19,15 +19,114 @@ class Z80MemptrTests: XCTestCase { // Set the initial value of memptr, run for the requested number of cycles, // return the new value machine.setValue(initialValue, for: .memPtr) - machine.runForNumber(ofCycles: length) + machine.runForInstruction() return machine.value(for: .memPtr) } - fileprivate func insert16(program: inout [UInt8], address: Int, offset: size_t) { + private func testPage(_ prefix: UInt8, exclusions: [Int]) { + for opcode in 0 ..< 256 { + if exclusions.contains(opcode) { + continue + } + + var program: [UInt8] = [ + (prefix != 0) ? prefix : UInt8(opcode), UInt8(opcode), 0, 0 + ] + + let argumentPosition = (prefix != 0) ? 2 : 1 + for _ in 0 ..< 10 { + let random = arc4random_uniform(65536) + program[argumentPosition + 0] = UInt8(random & 0x00ff) + program[argumentPosition + 1] = UInt8(random >> 8) + + let expectedResult = UInt16(arc4random_uniform(65536)) + + let result = test(program: program, initialValue: expectedResult) + XCTAssertEqual(result, expectedResult, "Failed for opcode \(String(opcode, radix:16))") + + // One failure will do. + if result != expectedResult { + break + } + } + } + } + + private func insert16(program: inout [UInt8], address: Int, offset: size_t) { program[offset] = UInt8(address & 0x00ff) program[offset + 1] = UInt8(address >> 8) } + /// Tests that everything not listed in the documentation has no effect upon MEMPTR. + func testStandardPageOthers() { + testPage(0, exclusions: [ + 0x02, // LD (BC), A + 0x09, // ADD HL, BC + 0x0a, // LD A, (BC) + 0x10, // DJNZ + 0x12, // LD (DE), A + 0x18, // JR + 0x19, // ADD HL, DE + 0x1a, // LD A, (DE) + 0x20, // JR NZ + 0x22, // LD (nn), HL + 0x28, // JR Z + 0x29, // ADD HL, HL + 0x30, // JR NC + 0x32, // LD (nn), A + 0x38, // JR C + 0x39, // ADD HL, SP + 0x3a, // LD A, (nn) + + 0xcb, // CB page + 0xdd, // DD page + 0xed, // ED page + 0xfd, // FD page + ]) + } + + func testEDPageOthers() { + testPage(0xed, exclusions: [ + 0x42, // SBC HL, BC + 0x43, // LD (nn), HL + 0x45, // RETN (??) + 0x4a, // ADC HL, BC + 0x4d, // RETI + 0x52, // SBC HL, DE + 0x53, // LD (nn), DE + 0x55, // RETN (??) + 0x5a, // ADC HL, DE + 0x5d, // RETN (??) + 0x62, // SBC HL, HL + 0x63, // LD (nn), HL + 0x65, // RETN (??) + 0x67, // RRD + 0x6a, // ADC HL, HL + 0x6d, // RETN (??) + 0x6f, // RLD + 0x72, // SBC HL, SP + 0x73, // LD (nn), SP + 0x75, // RETN (??) + 0x78, // IN A, (C) + 0x79, // OUT (C), A + 0x7a, // ADC HL, SP + 0x7d, // RETN (??) + 0xa1, // CPI + 0xa2, // INI + 0xa3, // OUTI + 0xa9, // CPD + 0xaa, // IND + 0xab, // OUTD + 0xb1, // CPIR + 0xb3, // OUIR + 0xb9, // CPDR + 0xbb // OTDR + ]) +// testPage(0xcb, exclusions: []) +// testPage(0xdd, exclusions: []) +// testPage(0xfd, exclusions: []) + } + /* Re: comments below: All the CPU chips tested give the same results except KP1858BM1 and T34BM1 slices noted as "BM1". @@ -44,7 +143,7 @@ class Z80MemptrTests: XCTestCase { program[2] = UInt8(addr >> 8) let expectedResult = UInt16((addr + 1) & 0xffff) - let result = test(program: program, length: 13, initialValue: 0xffff) + let result = test(program: program, initialValue: 0xffff) XCTAssertEqual(result, expectedResult) } } @@ -73,9 +172,9 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16(((addr + 1) & 0xff) + (a << 8)) - let bcResult = test(program: bcProgram, length: 7, initialValue: 0xffff) - let deResult = test(program: deProgram, length: 7, initialValue: 0xffff) - let nnResult = test(program: nnProgram, length: 13, initialValue: 0xffff) + let bcResult = test(program: bcProgram, initialValue: 0xffff) + let deResult = test(program: deProgram, initialValue: 0xffff) + let nnResult = test(program: nnProgram, initialValue: 0xffff) XCTAssertEqual(bcResult, expectedResult) XCTAssertEqual(deResult, expectedResult) @@ -99,8 +198,8 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16((addr + 1) & 0xffff) - let bcResult = test(program: bcProgram, length: 7, initialValue: 0xffff) - let deResult = test(program: deProgram, length: 7, initialValue: 0xffff) + let bcResult = test(program: bcProgram, initialValue: 0xffff) + let deResult = test(program: deProgram, initialValue: 0xffff) XCTAssertEqual(bcResult, expectedResult) XCTAssertEqual(deResult, expectedResult) @@ -135,11 +234,11 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16((addr + 1) & 0xffff) - XCTAssertEqual(test(program: ldnnhlBaseProgram, length: 16, initialValue: expectedResult ^ 1), expectedResult) - XCTAssertEqual(test(program: ldnnbcEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult) - XCTAssertEqual(test(program: ldnndeEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult) - XCTAssertEqual(test(program: ldnnhlEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult) - XCTAssertEqual(test(program: ldnnspEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult) + XCTAssertEqual(test(program: ldnnhlBaseProgram, initialValue: expectedResult ^ 1), expectedResult) + XCTAssertEqual(test(program: ldnnbcEDProgram, initialValue: expectedResult ^ 1), expectedResult) + XCTAssertEqual(test(program: ldnndeEDProgram, initialValue: expectedResult ^ 1), expectedResult) + XCTAssertEqual(test(program: ldnnhlEDProgram, initialValue: expectedResult ^ 1), expectedResult) + XCTAssertEqual(test(program: ldnnspEDProgram, initialValue: expectedResult ^ 1), expectedResult) } } @@ -190,15 +289,15 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16((addr + 1) & 0xffff) - XCTAssertEqual(test(program: hlBaseProgram, length: 16, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: hlBaseProgram, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: bcEDProgram, length: 20, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: deEDProgram, length: 20, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: hlEDProgram, length: 20, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: spEDProgram, length: 20, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: bcEDProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: deEDProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: hlEDProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: spEDProgram, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: ixProgram, length: 20, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: iyProgram, length: 20, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: ixProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: iyProgram, initialValue: 0xffff), expectedResult) } } @@ -225,9 +324,9 @@ class Z80MemptrTests: XCTestCase { iyProgram[2] = UInt8(addr & 0x00ff) iyProgram[3] = UInt8(addr >> 8) - XCTAssertEqual(test(program: hlProgram, length: 19, initialValue: 0xffff), UInt16(addr)) - XCTAssertEqual(test(program: ixProgram, length: 23, initialValue: 0xffff), UInt16(addr)) - XCTAssertEqual(test(program: iyProgram, length: 23, initialValue: 0xffff), UInt16(addr)) + XCTAssertEqual(test(program: hlProgram, initialValue: 0xffff), UInt16(addr)) + XCTAssertEqual(test(program: ixProgram, initialValue: 0xffff), UInt16(addr)) + XCTAssertEqual(test(program: iyProgram, initialValue: 0xffff), UInt16(addr)) } } @@ -248,9 +347,9 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16((addr + 1) & 0xffff) machine.setValue(UInt16(addr), for: .HL) - XCTAssertEqual(test(program: addProgram, length: 11, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: adcProgram, length: 15, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: sbcProgram, length: 15, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: addProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: adcProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: sbcProgram, initialValue: 0xffff), expectedResult) } } @@ -268,20 +367,132 @@ class Z80MemptrTests: XCTestCase { let expectedResult = UInt16((addr + 1) & 0xffff) machine.setValue(UInt16(addr), for: .HL) - XCTAssertEqual(test(program: rldProgram, length: 18, initialValue: 0xffff), expectedResult) - XCTAssertEqual(test(program: rrdProgram, length: 18, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: rldProgram, initialValue: 0xffff), expectedResult) + XCTAssertEqual(test(program: rrdProgram, initialValue: 0xffff), expectedResult) } } /* TODO: JR/DJNZ/RET/RETI/RST (jumping to addr) MEMPTR = addr + + (implemented in principle) */ + func testJR() { + var jrProgram: [UInt8] = [ + 0x18, 0x00 + ] + + for offset in 0 ..< 256 { + jrProgram[1] = UInt8(offset) + let result = test(program: jrProgram, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } + + func testJRcc() { + func testJR(instruction: UInt8) { + var program: [UInt8] = [ + instruction, 0x00 + ] + + for offset in 0 ..< 256 { + program[1] = UInt8(offset) + let result = test(program: program, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } + + // JR NZ. + machine.setValue(0x00, for: .AF) + testJR(instruction: 0x20) + + machine.setValue(0xff, for: .AF) + testJR(instruction: 0x20) + + // JR NC + machine.setValue(0x00, for: .AF) + testJR(instruction: 0x30) + + machine.setValue(0xff, for: .AF) + testJR(instruction: 0x30) + + // JR Z + machine.setValue(0x00, for: .AF) + testJR(instruction: 0x28) + + machine.setValue(0x01, for: .AF) + testJR(instruction: 0x28) + + // JR C + machine.setValue(0x00, for: .AF) + testJR(instruction: 0x38) + + machine.setValue(0x01, for: .AF) + testJR(instruction: 0x38) + } + + func testRST() { + var rstProgram: [UInt8] = [ + 0x00 + ] + + for offset in 0 ..< 8 { + rstProgram[0] = UInt8(offset << 3) + 0xc7 + let result = test(program: rstProgram, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } + + func testRET() { + let retProgram: [UInt8] = [ + 0xc9 + ] + + for addr in 0 ..< 65536 { + let stackContents: [UInt8] = [ + UInt8(addr & 0xff), UInt8(addr >> 8) + ] + + machine.setData(Data(stackContents), atAddress: 0xf001) + machine.setValue(0xf000, for: .stackPointer) + let result = test(program: retProgram, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } + func testRETI() { + let retiProgram: [UInt8] = [ + 0xed, 0x4d + ] + + for addr in 0 ..< 65536 { + let stackContents: [UInt8] = [ + UInt8(addr & 0xff), UInt8(addr >> 8) + ] + + machine.setData(Data(stackContents), atAddress: 0xf001) + machine.setValue(0xf000, for: .stackPointer) + let result = test(program: retiProgram, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } /* TODO: JP(except JP rp)/CALL addr (even in case of conditional call/jp, independently on condition satisfied or not) MEMPTR = addr */ + func testCALL() { + var callProgram: [UInt8] = [ + 0xcd, 0x00, 0x00 + ] + + for offset in 0 ..< 65536 { + callProgram[1] = UInt8(offset & 0xff) + callProgram[2] = UInt8(offset >> 8) + let result = test(program: callProgram, initialValue: 0xffff) + XCTAssertEqual(result, machine.value(for: .programCounter)) + } + } /* TODO: IN A,(port) diff --git a/Processors/Z80/Implementation/Z80Implementation.hpp b/Processors/Z80/Implementation/Z80Implementation.hpp index 12d52e834..1e40f2ed4 100644 --- a/Processors/Z80/Implementation/Z80Implementation.hpp +++ b/Processors/Z80/Implementation/Z80Implementation.hpp @@ -101,9 +101,10 @@ template < class T, scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_]; break; - case MicroOp::Increment16: (*static_cast(operation->source))++; break; + case MicroOp::Increment8NoFlags: ++ *static_cast(operation->source); break; + case MicroOp::Increment16: ++ *static_cast(operation->source); break; case MicroOp::IncrementPC: pc_.full += pc_increment_; break; - case MicroOp::Decrement16: (*static_cast(operation->source))--; break; + case MicroOp::Decrement16: -- *static_cast(operation->source); break; case MicroOp::Move8: *static_cast(operation->destination) = *static_cast(operation->source); break; case MicroOp::Move16: *static_cast(operation->destination) = *static_cast(operation->source); break; @@ -180,6 +181,7 @@ template < class T, case MicroOp::DJNZ: bc_.halves.high--; if(!bc_.halves.high) { + memptr_.full = pc_.full; advance_operation(); } break; @@ -480,7 +482,8 @@ template < class T, if(test) { \ pc_.full -= 2; \ } else { \ - advance_operation(); \ + memptr_.full = pc_.full - 1; \ + advance_operation(); \ } #define LDxR_STEP(dir) \ @@ -514,9 +517,10 @@ template < class T, #undef LDxR_STEP -#define CPxR_STEP(dir) \ - hl_.full += dir; \ - bc_.full--; \ +#define CPxR_STEP(dir) \ + hl_.full += dir; \ + memptr_.full += dir; \ + bc_.full--; \ \ uint8_t result = a_ - temp8_; \ const uint8_t halfResult = (a_&0xf) - (temp8_&0xf); \ @@ -541,12 +545,10 @@ template < class T, } break; case MicroOp::CPD: { - memptr_.full--; CPxR_STEP(-1); } break; case MicroOp::CPI: { - memptr_.full++; CPxR_STEP(1); } break; @@ -636,6 +638,7 @@ template < class T, case MicroOp::BIT: { const uint8_t result = *static_cast(operation->source) & (1 << ((operation_ >> 3)&7)); + // Leak MEMPTR into bits 5 and 3 if this is either BIT n,(HL) or BIT n,(IX/IY+d). if(current_instruction_page_->is_indexed || ((operation_&0x07) == 6)) { bit53_result_ = memptr_.halves.high; } else { @@ -797,13 +800,15 @@ template < class T, } break; -// MARK: - Input +// MARK: - Input and Output case MicroOp::SetInFlags: subtract_flag_ = half_carry_result_ = 0; sign_result_ = zero_result_ = bit53_result_ = *static_cast(operation->source); set_parity(sign_result_); - set_did_compute_flags(); + set_did_compute_flags(); // deliberate fallthrough + case MicroOp::SetOutFlags: + memptr_.full = bc_.full + 1; break; diff --git a/Processors/Z80/Implementation/Z80Storage.hpp b/Processors/Z80/Implementation/Z80Storage.hpp index bc1a89cd7..1008a2819 100644 --- a/Processors/Z80/Implementation/Z80Storage.hpp +++ b/Processors/Z80/Implementation/Z80Storage.hpp @@ -20,6 +20,7 @@ class ProcessorStorage { DecodeOperationNoRChange, MoveToNextProgram, + Increment8NoFlags, Increment8, Increment16, Decrement8, @@ -73,31 +74,45 @@ class ProcessorStorage { JumpTo66, HALT, + /// Decrements BC; if BC is 0 then moves to the next instruction. Otherwise allows this instruction to finish. DJNZ, + DAA, CPL, SCF, CCF, + /// Resets the bit in @c source implied by @c operation_ . RES, + /// Tests the bit in @c source implied by @c operation_ . BIT, + /// Sets the bit in @c source implied by @c operation_ . SET, + /// Sets @c memptr_ to the target address implied by @c operation_ . CalculateRSTDestination, + /// Resets subtract and carry, sets sign, zero, five and three according to the value of @c a_ and sets parity to the value of @c IFF2 . SetAFlags, + /// Resets subtract and carry, sets sign, zero, five and three according to the value of @c operation and sets parity the same as sign. SetInFlags, + /// Sets @c memptr_ to @c bc_.full+1 . + SetOutFlags, + /// Sets @c temp8_ to 0. SetZero, + /// A no-op; used in instruction lists to indicate where an index calculation should go if this is an I[X/Y]+d operation. IndexedPlaceHolder, + /// Sets @c memptr_ to (a_ << 8) + ((source_ + 1) & 0xff) SetAddrAMemptr, + /// Resets: IFF1, IFF2, interrupt mode, the PC, I and R; sets all flags, the SP to 0xffff and a_ to 0xff. Reset }; Type type; - void *source; - void *destination; + void *source = nullptr; + void *destination = nullptr; PartialMachineCycle machine_cycle; }; @@ -105,7 +120,7 @@ class ProcessorStorage { std::vector instructions; std::vector all_operations; std::vector fetch_decode_execute; - MicroOp *fetch_decode_execute_data; + MicroOp *fetch_decode_execute_data = nullptr; uint8_t r_step; bool is_indexed;