1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Improves MEMPTR testing and some results.

This commit is contained in:
Thomas Harte 2020-02-24 23:32:18 -05:00
parent 7959d243f6
commit 3097c4ccae
4 changed files with 277 additions and 44 deletions

View File

@ -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() {

View File

@ -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)

View File

@ -101,9 +101,10 @@ template < class T,
scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_];
break;
case MicroOp::Increment16: (*static_cast<uint16_t *>(operation->source))++; break;
case MicroOp::Increment8NoFlags: ++ *static_cast<uint8_t *>(operation->source); break;
case MicroOp::Increment16: ++ *static_cast<uint16_t *>(operation->source); break;
case MicroOp::IncrementPC: pc_.full += pc_increment_; break;
case MicroOp::Decrement16: (*static_cast<uint16_t *>(operation->source))--; break;
case MicroOp::Decrement16: -- *static_cast<uint16_t *>(operation->source); break;
case MicroOp::Move8: *static_cast<uint8_t *>(operation->destination) = *static_cast<uint8_t *>(operation->source); break;
case MicroOp::Move16: *static_cast<uint16_t *>(operation->destination) = *static_cast<uint16_t *>(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<uint8_t *>(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<uint8_t *>(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;

View File

@ -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<MicroOp *> instructions;
std::vector<MicroOp> all_operations;
std::vector<MicroOp> fetch_decode_execute;
MicroOp *fetch_decode_execute_data;
MicroOp *fetch_decode_execute_data = nullptr;
uint8_t r_step;
bool is_indexed;