2019-12-14 19:26:33 +00:00
|
|
|
//
|
|
|
|
// 68000ComparativeTests.cpp
|
|
|
|
// Clock SignalTests
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 14/12/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
|
|
|
|
#include "../../../Processors/68000/68000.hpp"
|
2022-05-02 00:40:57 +00:00
|
|
|
#include "../../../InstructionSets/M68k/Executor.hpp"
|
2019-12-14 19:26:33 +00:00
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <memory>
|
2019-12-14 20:09:06 +00:00
|
|
|
#include <functional>
|
2019-12-14 19:26:33 +00:00
|
|
|
|
2022-05-03 15:28:50 +00:00
|
|
|
//#define USE_EXISTING_IMPLEMENTATION
|
2022-05-03 15:09:57 +00:00
|
|
|
|
2019-12-14 19:26:33 +00:00
|
|
|
@interface M68000ComparativeTests : XCTestCase
|
|
|
|
@end
|
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
@implementation M68000ComparativeTests {
|
|
|
|
NSSet<NSString *> *_fileSet;
|
|
|
|
NSSet<NSString *> *_testSet;
|
|
|
|
|
|
|
|
NSMutableSet<NSString *> *_failures;
|
|
|
|
NSMutableArray<NSNumber *> *_failingOpcodes;
|
|
|
|
}
|
|
|
|
|
2022-05-03 15:28:50 +00:00
|
|
|
// New implementation verified against (i.e. has the same failures as the old, likely all BCD related):
|
|
|
|
//
|
|
|
|
// add_sub
|
|
|
|
// addi_subi_cmpi
|
|
|
|
// eor_and_or
|
2022-05-03 18:45:49 +00:00
|
|
|
// addq_subq
|
|
|
|
// addx_subx
|
2022-05-03 19:32:54 +00:00
|
|
|
// bcc
|
2022-05-03 19:49:55 +00:00
|
|
|
// cmp
|
|
|
|
// dbcc_scc
|
|
|
|
// eori_andi_ori
|
2022-05-04 00:45:36 +00:00
|
|
|
// lea
|
2022-05-04 23:44:59 +00:00
|
|
|
// lslr_aslr_roxlr_rolr
|
2022-05-05 00:38:56 +00:00
|
|
|
// move_tofrom_srccr
|
2022-05-05 16:37:47 +00:00
|
|
|
// move
|
|
|
|
// movep
|
2022-05-03 15:28:50 +00:00
|
|
|
|
2022-05-04 12:41:55 +00:00
|
|
|
// Issues to fix:
|
|
|
|
//
|
|
|
|
// LINK A7, in which the post-operation writeback overwrites
|
|
|
|
// the modified value.
|
|
|
|
//
|
|
|
|
// Do I need a dedicated LINKA7 operation?
|
|
|
|
//
|
|
|
|
// This affects link_unlk
|
|
|
|
|
2022-05-03 19:49:55 +00:00
|
|
|
// Skipped for now, for implying a more granular decoder:
|
|
|
|
//
|
2022-05-03 19:40:04 +00:00
|
|
|
// btst_bchg_bclr_bset
|
2022-05-03 19:49:55 +00:00
|
|
|
// chk
|
|
|
|
//
|
|
|
|
// And for uncertainty around the test result status register correctness:
|
|
|
|
//
|
|
|
|
// divu_divs
|
|
|
|
// eor_and_or (which invokes BCD)
|
|
|
|
// exg (also BCD)
|
2022-05-04 00:45:36 +00:00
|
|
|
// chk
|
|
|
|
//
|
|
|
|
// And because possibly my old CHK is pushing the wrong program counter?
|
|
|
|
//
|
|
|
|
// ext.json
|
2022-05-03 19:40:04 +00:00
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
- (void)setUp {
|
|
|
|
// To limit tests run to a subset of files and/or of tests, uncomment and fill in below.
|
2022-05-05 22:51:29 +00:00
|
|
|
_fileSet = [NSSet setWithArray:@[@"movem.json"]];
|
2022-05-06 13:45:06 +00:00
|
|
|
// _testSet = [NSSet setWithArray:@[@"MOVEM 00a8 (0)"]];
|
2020-01-05 03:22:33 +00:00
|
|
|
// _fileSet = [NSSet setWithArray:@[@"jmp_jsr.json"]];
|
2020-01-02 01:00:37 +00:00
|
|
|
// _testSet = [NSSet setWithArray:@[@"CHK 41a8"]];
|
2019-12-16 02:26:47 +00:00
|
|
|
}
|
2019-12-14 19:26:33 +00:00
|
|
|
|
|
|
|
- (void)testAll {
|
2019-12-16 02:26:47 +00:00
|
|
|
// These will accumulate a list of failing tests and associated opcodes.
|
|
|
|
_failures = [[NSMutableSet alloc] init];
|
|
|
|
_failingOpcodes = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
// Get the full list of available test files.
|
2019-12-14 19:26:33 +00:00
|
|
|
NSBundle *const bundle = [NSBundle bundleForClass:[self class]];
|
|
|
|
NSArray<NSURL *> *const tests = [bundle URLsForResourcesWithExtension:@"json" subdirectory:@"68000 Comparative Tests"];
|
2019-12-15 03:58:51 +00:00
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
// Issue each test file.
|
2019-12-14 19:26:33 +00:00
|
|
|
for(NSURL *url in tests) {
|
2019-12-16 02:26:47 +00:00
|
|
|
// Compare against a file set if one has been supplied.
|
2021-03-19 08:19:49 +00:00
|
|
|
if(_fileSet && ![_fileSet containsObject:[url lastPathComponent]]) continue;
|
2019-12-15 03:58:51 +00:00
|
|
|
NSLog(@"Testing %@", url);
|
2019-12-14 19:26:33 +00:00
|
|
|
[self testJSONAtURL:url];
|
|
|
|
}
|
2019-12-16 02:26:47 +00:00
|
|
|
|
|
|
|
// Output a summary of failures.
|
2019-12-18 03:19:23 +00:00
|
|
|
NSLog(@"Total: %@", @(_failures.count));
|
2019-12-16 02:26:47 +00:00
|
|
|
NSLog(@"Failures: %@", _failures);
|
|
|
|
NSLog(@"Failing opcodes:");
|
|
|
|
for(NSNumber *number in _failingOpcodes) {
|
|
|
|
NSLog(@"%04x", number.intValue);
|
|
|
|
}
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testJSONAtURL:(NSURL *)url {
|
2019-12-16 02:26:47 +00:00
|
|
|
// Read the nominated file and parse it as JSON.
|
2019-12-14 19:26:33 +00:00
|
|
|
NSData *const data = [NSData dataWithContentsOfURL:url];
|
|
|
|
NSError *error;
|
|
|
|
NSArray *const jsonContents = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
|
|
|
|
|
|
|
if(!data || error || ![jsonContents isKindOfClass:[NSArray class]]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
// Perform each dictionary in the array as a test.
|
2019-12-14 19:26:33 +00:00
|
|
|
for(NSDictionary *test in jsonContents) {
|
|
|
|
if(![test isKindOfClass:[NSDictionary class]]) continue;
|
2022-05-03 15:09:57 +00:00
|
|
|
#ifdef USE_EXISTING_IMPLEMENTATION
|
|
|
|
[self testOperationClassic:test];
|
|
|
|
#else
|
2022-05-02 00:40:57 +00:00
|
|
|
[self testOperationExecutor:test];
|
2022-05-03 15:09:57 +00:00
|
|
|
#endif
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-02 00:40:57 +00:00
|
|
|
- (void)testOperationClassic:(NSDictionary *)test {
|
2019-12-14 19:26:33 +00:00
|
|
|
// Only entries with a name are valid.
|
|
|
|
NSString *const name = test[@"name"];
|
|
|
|
if(!name) return;
|
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
// Compare against a test set if one has been supplied.
|
|
|
|
if(_testSet && ![_testSet containsObject:name]) return;
|
2019-12-15 02:40:21 +00:00
|
|
|
|
2019-12-14 19:26:33 +00:00
|
|
|
// This is the test class for 68000 execution.
|
|
|
|
struct Test68000: public CPU::MC68000::BusHandler {
|
|
|
|
std::array<uint8_t, 16*1024*1024> ram;
|
|
|
|
CPU::MC68000::Processor<Test68000, true, true> processor;
|
2019-12-14 20:09:06 +00:00
|
|
|
std::function<void(void)> comparitor;
|
2019-12-14 19:26:33 +00:00
|
|
|
|
|
|
|
Test68000() : processor(*this) {
|
|
|
|
}
|
|
|
|
|
2020-09-27 19:10:29 +00:00
|
|
|
void will_perform(uint32_t, uint16_t) {
|
2019-12-14 19:26:33 +00:00
|
|
|
--instructions_remaining_;
|
2019-12-14 20:09:06 +00:00
|
|
|
if(!instructions_remaining_) comparitor();
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-27 19:10:29 +00:00
|
|
|
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
2019-12-14 19:26:33 +00:00
|
|
|
using Microcycle = CPU::MC68000::Microcycle;
|
|
|
|
if(cycle.data_select_active()) {
|
|
|
|
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
|
|
|
}
|
|
|
|
return HalfCycles(0);
|
|
|
|
}
|
|
|
|
|
2019-12-14 20:09:06 +00:00
|
|
|
void run_for_instructions(int instructions, const std::function<void(void)> &compare) {
|
2019-12-14 19:26:33 +00:00
|
|
|
instructions_remaining_ = instructions + 1; // i.e. run up to the will_perform of the instruction after.
|
2019-12-14 20:09:06 +00:00
|
|
|
comparitor = std::move(compare);
|
2019-12-14 19:26:33 +00:00
|
|
|
while(instructions_remaining_) {
|
|
|
|
processor.run_for(HalfCycles(2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int instructions_remaining_;
|
|
|
|
};
|
2019-12-14 20:09:06 +00:00
|
|
|
auto uniqueTest68000 = std::make_unique<Test68000>();
|
|
|
|
auto test68000 = uniqueTest68000.get();
|
|
|
|
memset(test68000->ram.data(), 0xce, test68000->ram.size());
|
|
|
|
|
|
|
|
{
|
|
|
|
// Apply initial memory state.
|
|
|
|
NSArray<NSNumber *> *const initialMemory = test[@"initial memory"];
|
|
|
|
NSEnumerator<NSNumber *> *enumerator = [initialMemory objectEnumerator];
|
|
|
|
while(true) {
|
|
|
|
NSNumber *const address = [enumerator nextObject];
|
|
|
|
NSNumber *const value = [enumerator nextObject];
|
|
|
|
|
|
|
|
if(!address || !value) break;
|
|
|
|
test68000->ram[address.integerValue ^ 1] = value.integerValue; // Effect a short-resolution endianness swap.
|
|
|
|
}
|
2019-12-14 19:26:33 +00:00
|
|
|
|
2019-12-14 20:09:06 +00:00
|
|
|
// Apply initial processor state.
|
|
|
|
NSDictionary *const initialState = test[@"initial state"];
|
|
|
|
auto state = test68000->processor.get_state();
|
|
|
|
for(int c = 0; c < 8; ++c) {
|
|
|
|
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
|
|
|
|
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
|
2019-12-14 19:26:33 +00:00
|
|
|
|
2019-12-14 20:09:06 +00:00
|
|
|
state.data[c] = uint32_t([initialState[dX] integerValue]);
|
|
|
|
if(c < 7)
|
|
|
|
state.address[c] = uint32_t([initialState[aX] integerValue]);
|
|
|
|
}
|
|
|
|
state.supervisor_stack_pointer = uint32_t([initialState[@"a7"] integerValue]);
|
|
|
|
state.user_stack_pointer = uint32_t([initialState[@"usp"] integerValue]);
|
|
|
|
state.status = [initialState[@"sr"] integerValue];
|
|
|
|
test68000->processor.set_state(state);
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the thing.
|
2020-02-22 17:34:16 +00:00
|
|
|
const auto comparitor = [=] {
|
2019-12-14 20:09:06 +00:00
|
|
|
// Test the end state.
|
|
|
|
NSDictionary *const finalState = test[@"final state"];
|
|
|
|
const auto state = test68000->processor.get_state();
|
|
|
|
for(int c = 0; c < 8; ++c) {
|
|
|
|
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
|
|
|
|
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
|
|
|
|
|
2019-12-16 02:26:47 +00:00
|
|
|
if(state.data[c] != [finalState[dX] integerValue]) [_failures addObject:name];
|
|
|
|
if(c < 7 && state.address[c] != [finalState[aX] integerValue]) [_failures addObject:name];
|
|
|
|
|
2019-12-14 20:09:06 +00:00
|
|
|
XCTAssertEqual(state.data[c], [finalState[dX] integerValue], @"%@: D%d inconsistent", name, c);
|
|
|
|
if(c < 7) {
|
|
|
|
XCTAssertEqual(state.address[c], [finalState[aX] integerValue], @"%@: A%d inconsistent", name, c);
|
|
|
|
}
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
2019-12-16 02:26:47 +00:00
|
|
|
if(state.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) [_failures addObject:name];
|
|
|
|
if(state.user_stack_pointer != [finalState[@"usp"] integerValue]) [_failures addObject:name];
|
|
|
|
if(state.status != [finalState[@"sr"] integerValue]) [_failures addObject:name];
|
|
|
|
|
2019-12-14 20:09:06 +00:00
|
|
|
XCTAssertEqual(state.supervisor_stack_pointer, [finalState[@"a7"] integerValue], @"%@: A7 inconsistent", name);
|
|
|
|
XCTAssertEqual(state.user_stack_pointer, [finalState[@"usp"] integerValue], @"%@: USP inconsistent", name);
|
|
|
|
XCTAssertEqual(state.status, [finalState[@"sr"] integerValue], @"%@: Status inconsistent", name);
|
2019-12-26 00:50:12 +00:00
|
|
|
XCTAssertEqual(state.program_counter - 4, [finalState[@"pc"] integerValue], @"%@: Program counter inconsistent", name);
|
2019-12-14 20:09:06 +00:00
|
|
|
|
|
|
|
// Test final memory state.
|
|
|
|
NSArray<NSNumber *> *const finalMemory = test[@"final memory"];
|
|
|
|
NSEnumerator *enumerator = [finalMemory objectEnumerator];
|
|
|
|
while(true) {
|
|
|
|
NSNumber *const address = [enumerator nextObject];
|
|
|
|
NSNumber *const value = [enumerator nextObject];
|
|
|
|
|
|
|
|
if(!address || !value) break;
|
|
|
|
XCTAssertEqual(test68000->ram[address.integerValue ^ 1], value.integerValue, @"%@: Memory at location %@ inconsistent", name, address);
|
2019-12-16 02:26:47 +00:00
|
|
|
if(test68000->ram[address.integerValue ^ 1] != value.integerValue) [_failures addObject:name];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Consider collating extra detail.
|
|
|
|
if([_failures containsObject:name]) {
|
2019-12-16 05:01:18 +00:00
|
|
|
[_failingOpcodes addObject:@((test68000->ram[0x101] << 8) | test68000->ram[0x100])];
|
2019-12-14 20:09:06 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
test68000->run_for_instructions(1, comparitor);
|
2019-12-14 19:26:33 +00:00
|
|
|
}
|
|
|
|
|
2022-05-02 00:40:57 +00:00
|
|
|
- (void)testOperationExecutor:(NSDictionary *)test {
|
|
|
|
// Only entries with a name are valid.
|
|
|
|
NSString *const name = test[@"name"];
|
|
|
|
if(!name) return;
|
|
|
|
|
|
|
|
// Compare against a test set if one has been supplied.
|
|
|
|
if(_testSet && ![_testSet containsObject:name]) return;
|
|
|
|
|
|
|
|
// This is the test class for 68000 execution.
|
|
|
|
struct Test68000 {
|
|
|
|
std::array<uint8_t, 16*1024*1024> ram;
|
|
|
|
InstructionSet::M68k::Executor<InstructionSet::M68k::Model::M68000, Test68000> processor;
|
|
|
|
|
|
|
|
Test68000() : processor(*this) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_for_instructions(int instructions) {
|
|
|
|
processor.run_for_instructions(instructions);
|
|
|
|
}
|
|
|
|
|
2022-05-02 12:00:56 +00:00
|
|
|
// Initial test-case implementation:
|
|
|
|
// do a very sedate read and write.
|
|
|
|
|
2022-05-02 00:40:57 +00:00
|
|
|
template <typename IntT> IntT read(uint32_t address) {
|
2022-05-02 12:00:56 +00:00
|
|
|
if constexpr (sizeof(IntT) == 1) {
|
|
|
|
return IntT(ram[address & 0xffffff]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 2) {
|
|
|
|
return IntT(
|
|
|
|
(ram[address & 0xffffff] << 8) |
|
|
|
|
ram[(address+1) & 0xffffff]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
return IntT(
|
|
|
|
(ram[address & 0xffffff] << 24) |
|
|
|
|
(ram[(address+1) & 0xffffff] << 16) |
|
|
|
|
(ram[(address+2) & 0xffffff] << 8) |
|
|
|
|
ram[(address+3) & 0xffffff]
|
|
|
|
);
|
|
|
|
}
|
2022-05-02 00:40:57 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename IntT> void write(uint32_t address, IntT value) {
|
2022-05-02 12:00:56 +00:00
|
|
|
if constexpr (sizeof(IntT) == 1) {
|
|
|
|
ram[address & 0xffffff] = uint8_t(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 2) {
|
|
|
|
ram[address & 0xffffff] = uint8_t(value >> 8);
|
|
|
|
ram[(address+1) & 0xffffff] = uint8_t(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (sizeof(IntT) == 4) {
|
|
|
|
ram[address & 0xffffff] = uint8_t(value >> 24);
|
|
|
|
ram[(address+1) & 0xffffff] = uint8_t(value >> 16);
|
|
|
|
ram[(address+2) & 0xffffff] = uint8_t(value >> 8);
|
|
|
|
ram[(address+3) & 0xffffff] = uint8_t(value);
|
|
|
|
}
|
2022-05-02 00:40:57 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
auto uniqueTest68000 = std::make_unique<Test68000>();
|
|
|
|
auto test68000 = uniqueTest68000.get();
|
|
|
|
memset(test68000->ram.data(), 0xce, test68000->ram.size());
|
|
|
|
|
|
|
|
{
|
|
|
|
// Apply initial memory state.
|
|
|
|
NSArray<NSNumber *> *const initialMemory = test[@"initial memory"];
|
|
|
|
NSEnumerator<NSNumber *> *enumerator = [initialMemory objectEnumerator];
|
|
|
|
while(true) {
|
|
|
|
NSNumber *const address = [enumerator nextObject];
|
|
|
|
NSNumber *const value = [enumerator nextObject];
|
|
|
|
|
|
|
|
if(!address || !value) break;
|
2022-05-02 16:57:45 +00:00
|
|
|
test68000->ram[address.integerValue] = value.integerValue;
|
2022-05-02 00:40:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply initial processor state.
|
2022-05-02 16:57:45 +00:00
|
|
|
NSDictionary *const initialState = test[@"initial state"];
|
|
|
|
auto state = test68000->processor.get_state();
|
|
|
|
for(int c = 0; c < 8; ++c) {
|
|
|
|
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
|
|
|
|
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
|
|
|
|
|
|
|
|
state.data[c] = uint32_t([initialState[dX] integerValue]);
|
|
|
|
if(c < 7)
|
|
|
|
state.address[c] = uint32_t([initialState[aX] integerValue]);
|
|
|
|
}
|
|
|
|
state.supervisor_stack_pointer = uint32_t([initialState[@"a7"] integerValue]);
|
|
|
|
state.user_stack_pointer = uint32_t([initialState[@"usp"] integerValue]);
|
|
|
|
state.status = [initialState[@"sr"] integerValue];
|
|
|
|
state.program_counter = uint32_t([initialState[@"pc"] integerValue]);
|
|
|
|
test68000->processor.set_state(state);
|
2022-05-02 00:40:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the thing.
|
|
|
|
test68000->run_for_instructions(1);
|
|
|
|
|
|
|
|
// Test the end state.
|
|
|
|
NSDictionary *const finalState = test[@"final state"];
|
2022-05-02 16:57:45 +00:00
|
|
|
const auto state = test68000->processor.get_state();
|
|
|
|
for(int c = 0; c < 8; ++c) {
|
|
|
|
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
|
|
|
|
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
|
|
|
|
|
|
|
|
if(state.data[c] != [finalState[dX] integerValue]) [_failures addObject:name];
|
|
|
|
if(c < 7 && state.address[c] != [finalState[aX] integerValue]) [_failures addObject:name];
|
|
|
|
|
|
|
|
XCTAssertEqual(state.data[c], [finalState[dX] integerValue], @"%@: D%d inconsistent", name, c);
|
|
|
|
if(c < 7) {
|
|
|
|
XCTAssertEqual(state.address[c], [finalState[aX] integerValue], @"%@: A%d inconsistent", name, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(state.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) [_failures addObject:name];
|
|
|
|
if(state.user_stack_pointer != [finalState[@"usp"] integerValue]) [_failures addObject:name];
|
|
|
|
if(state.status != [finalState[@"sr"] integerValue]) [_failures addObject:name];
|
|
|
|
|
|
|
|
XCTAssertEqual(state.supervisor_stack_pointer, [finalState[@"a7"] integerValue], @"%@: A7 inconsistent", name);
|
|
|
|
XCTAssertEqual(state.user_stack_pointer, [finalState[@"usp"] integerValue], @"%@: USP inconsistent", name);
|
|
|
|
XCTAssertEqual(state.status, [finalState[@"sr"] integerValue], @"%@: Status inconsistent", name);
|
|
|
|
XCTAssertEqual(state.program_counter, [finalState[@"pc"] integerValue], @"%@: Program counter inconsistent", name);
|
2022-05-02 00:40:57 +00:00
|
|
|
|
|
|
|
// Test final memory state.
|
|
|
|
NSArray<NSNumber *> *const finalMemory = test[@"final memory"];
|
|
|
|
NSEnumerator *enumerator = [finalMemory objectEnumerator];
|
|
|
|
while(true) {
|
|
|
|
NSNumber *const address = [enumerator nextObject];
|
|
|
|
NSNumber *const value = [enumerator nextObject];
|
|
|
|
|
|
|
|
if(!address || !value) break;
|
2022-05-02 16:57:45 +00:00
|
|
|
XCTAssertEqual(test68000->ram[address.integerValue], value.integerValue, @"%@: Memory at location %@ inconsistent", name, address);
|
|
|
|
if(test68000->ram[address.integerValue] != value.integerValue) [_failures addObject:name];
|
2022-05-02 00:40:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Consider collating extra detail.
|
|
|
|
if([_failures containsObject:name]) {
|
2022-05-02 16:57:45 +00:00
|
|
|
[_failingOpcodes addObject:@((test68000->ram[0x100] << 8) | test68000->ram[0x101])];
|
2022-05-02 00:40:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-14 19:26:33 +00:00
|
|
|
@end
|