mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-11 04:28:58 +00:00
Starts on two-bus-cycle instructions, correcting validators.
This commit is contained in:
parent
57a7e0834f
commit
818655a9b6
@ -16,7 +16,7 @@ static constexpr uint16_t initial_pc = 0;
|
|||||||
|
|
||||||
struct CapturingZ80: public CPU::Z80::BusHandler {
|
struct CapturingZ80: public CPU::Z80::BusHandler {
|
||||||
|
|
||||||
CapturingZ80(const std::initializer_list<uint8_t> &code) : z80_(*this) {
|
template <typename Collection> CapturingZ80(const Collection &code) : z80_(*this) {
|
||||||
// Take a copy of the code.
|
// Take a copy of the code.
|
||||||
std::copy(code.begin(), code.end(), ram_);
|
std::copy(code.begin(), code.end(), ram_);
|
||||||
|
|
||||||
@ -97,8 +97,7 @@ struct CapturingZ80: public CPU::Z80::BusHandler {
|
|||||||
observed by ZX Spectrum users in the software-side documentation of
|
observed by ZX Spectrum users in the software-side documentation of
|
||||||
contended memory timings.
|
contended memory timings.
|
||||||
*/
|
*/
|
||||||
@implementation Z80ContentionTests {
|
@implementation Z80ContentionTests
|
||||||
}
|
|
||||||
|
|
||||||
struct ContentionCheck {
|
struct ContentionCheck {
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
@ -112,31 +111,28 @@ struct ContentionCheck {
|
|||||||
- (void)validate48Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
- (void)validate48Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
||||||
// 48[/128]k contention logic: triggered on address alone, _unless_
|
// 48[/128]k contention logic: triggered on address alone, _unless_
|
||||||
// MREQ is also active.
|
// MREQ is also active.
|
||||||
//
|
|
||||||
// I think the source I'm using also implicitly assumes that refresh
|
|
||||||
// addresses are outside of the contended area, and doesn't check them.
|
|
||||||
// So unlike the actual ULA I'm also ignoring any address while refresh
|
|
||||||
// is asserted.
|
|
||||||
int count = -1;
|
|
||||||
uint16_t address = 0;
|
|
||||||
auto contention = contentions.begin();
|
auto contention = contentions.begin();
|
||||||
|
|
||||||
const auto bus_records = z80.cycle_records();
|
const auto bus_records = z80.cycle_records();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
uint16_t address = bus_records.front().address;
|
||||||
for(const auto &record: bus_records) {
|
for(const auto &record: bus_records) {
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
|
const bool is_final = &record == &bus_records.back();
|
||||||
if(
|
if(
|
||||||
!count || // i.e. is at start.
|
&record != &bus_records.front() && ( // i.e. not at front.
|
||||||
(&record == &bus_records.back()) || // i.e. is at end.
|
is_final ||
|
||||||
!record.mreq // i.e. beginning of a new contention.
|
!record.mreq // i.e. beginning of a new contention.
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if(count) {
|
XCTAssertNotEqual(contention, contentions.end());
|
||||||
XCTAssertNotEqual(contention, contentions.end());
|
|
||||||
XCTAssertEqual(contention->address, address);
|
|
||||||
XCTAssertEqual(contention->length, count);
|
|
||||||
++contention;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
XCTAssertEqual(contention->address, address);
|
||||||
|
XCTAssertEqual(contention->length, count - !is_final); // Subtract 1 if not at end, because in that case this cycle
|
||||||
|
// is one after the previous group ended.
|
||||||
|
|
||||||
|
++contention;
|
||||||
count = 1;
|
count = 1;
|
||||||
address = record.address;
|
address = record.address;
|
||||||
}
|
}
|
||||||
@ -151,27 +147,28 @@ struct ContentionCheck {
|
|||||||
*/
|
*/
|
||||||
- (void)validatePlus3Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
- (void)validatePlus3Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
||||||
// +3 contention logic: triggered by the leading edge of MREQ, sans refresh.
|
// +3 contention logic: triggered by the leading edge of MREQ, sans refresh.
|
||||||
int count = -1;
|
|
||||||
uint16_t address = 0;
|
|
||||||
auto contention = contentions.begin();
|
auto contention = contentions.begin();
|
||||||
|
|
||||||
const auto bus_records = z80.bus_records();
|
const auto bus_records = z80.bus_records();
|
||||||
|
|
||||||
for(size_t c = 0; c < bus_records.size(); c += 2) {
|
int count = 0;
|
||||||
const bool is_leading_edge = !bus_records[c].mreq && bus_records[c+1].mreq && !bus_records[c].refresh;
|
uint16_t address = bus_records.front().address;
|
||||||
|
|
||||||
|
for(size_t c = 0; c < bus_records.size(); c += 2) {
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
|
const bool is_leading_edge = !bus_records[c].mreq && bus_records[c+1].mreq && !bus_records[c].refresh;
|
||||||
|
const bool is_final = c == bus_records.size() - 2;
|
||||||
if(
|
if(
|
||||||
!count || // i.e. is at start.
|
c && ( // i.e. not at front.
|
||||||
(c == bus_records.size() - 2) || // i.e. is at end.
|
is_final ||
|
||||||
is_leading_edge // i.e. beginning of a new contention.
|
is_leading_edge // i.e. beginning of a new contention.
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if(count) {
|
XCTAssertNotEqual(contention, contentions.end());
|
||||||
XCTAssertNotEqual(contention, contentions.end());
|
|
||||||
XCTAssertEqual(contention->address, address);
|
XCTAssertEqual(contention->address, address);
|
||||||
XCTAssertEqual(contention->length, count);
|
XCTAssertEqual(contention->length, count - !is_final); // See validate48Contention for exposition.
|
||||||
++contention;
|
++contention;
|
||||||
}
|
|
||||||
|
|
||||||
count = 1;
|
count = 1;
|
||||||
address = bus_records[c].address;
|
address = bus_records[c].address;
|
||||||
@ -183,7 +180,7 @@ struct ContentionCheck {
|
|||||||
|
|
||||||
// MARK: - Opcode tests.
|
// MARK: - Opcode tests.
|
||||||
|
|
||||||
- (void)testSimpleSingleBytes {
|
- (void)testSimpleOneBytes {
|
||||||
for(uint8_t opcode : {
|
for(uint8_t opcode : {
|
||||||
0x00, // NOP
|
0x00, // NOP
|
||||||
|
|
||||||
@ -225,7 +222,8 @@ struct ContentionCheck {
|
|||||||
0x0f, // RRCA
|
0x0f, // RRCA
|
||||||
0xe9, // JP (HL)
|
0xe9, // JP (HL)
|
||||||
}) {
|
}) {
|
||||||
CapturingZ80 z80({opcode});
|
const std::initializer_list<uint8_t> opcodes = {opcode};
|
||||||
|
CapturingZ80 z80(opcodes);
|
||||||
z80.run_for(4);
|
z80.run_for(4);
|
||||||
|
|
||||||
[self validate48Contention:{{initial_pc, 4}} z80:z80];
|
[self validate48Contention:{{initial_pc, 4}} z80:z80];
|
||||||
@ -233,4 +231,21 @@ struct ContentionCheck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testSimpleTwoBytes {
|
||||||
|
// This group should apparently also include 'NOPD'. Neither I nor any other
|
||||||
|
// page I could find seems to have heard of 'NOPD'.
|
||||||
|
|
||||||
|
for(const auto &sequence : std::vector<std::vector<uint8_t>>{
|
||||||
|
// sro d (i.e. RLC, RRC, RL, RR, SLA, SRA, SRL and SLL)
|
||||||
|
{0xcb, 0x00}, {0xcb, 0x01}, {0xcb, 0x02}, {0xcb, 0x03},
|
||||||
|
{0xcb, 0x04}, {0xcb, 0x05}, {0xcb, 0x07},
|
||||||
|
}) {
|
||||||
|
CapturingZ80 z80(sequence);
|
||||||
|
z80.run_for(8);
|
||||||
|
|
||||||
|
[self validate48Contention:{{initial_pc, 4}, {initial_pc+1, 4}} z80:z80];
|
||||||
|
[self validatePlus3Contention:{{initial_pc, 4}, {initial_pc+1, 4}} z80:z80];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
Loading…
Reference in New Issue
Block a user