mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Merge branch 'master' into InMacintosh
This commit is contained in:
commit
5d5bd6791b
@ -250,6 +250,8 @@ enum class AddressingMode: uint8_t {
|
||||
/// .q; value is embedded in the opcode.
|
||||
Quick = 0b01'110,
|
||||
};
|
||||
/// Guaranteed to be 1+[largest value used by AddressingMode].
|
||||
static constexpr int AddressingModeCount = 0b10'110;
|
||||
|
||||
/*!
|
||||
A preinstruction is as much of an instruction as can be decoded with
|
||||
|
@ -190,6 +190,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
|
||||
void run_for(const Cycles cycles) final {
|
||||
mc68000_.run_for(cycles);
|
||||
flush();
|
||||
}
|
||||
|
||||
using Microcycle = CPU::MC68000Mk2::Microcycle;
|
||||
|
@ -278,6 +278,7 @@
|
||||
4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; };
|
||||
4B5B37312777C7FC0047F238 /* IPF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5B372F2777C7FC0047F238 /* IPF.cpp */; };
|
||||
4B5B37322777C7FC0047F238 /* IPF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5B372F2777C7FC0047F238 /* IPF.cpp */; };
|
||||
4B5D497C28513F870076E2F9 /* IPF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5B372F2777C7FC0047F238 /* IPF.cpp */; };
|
||||
4B5D5C9725F56FC7001B4623 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; };
|
||||
4B5D5C9825F56FC7001B4623 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; };
|
||||
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; };
|
||||
@ -641,6 +642,7 @@
|
||||
4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; };
|
||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
||||
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
|
||||
4BA6B6AE284EDAC100A3B7A8 /* 68000OldVsNew.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6B6AD284EDAC000A3B7A8 /* 68000OldVsNew.mm */; };
|
||||
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; };
|
||||
4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; };
|
||||
4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; };
|
||||
@ -1683,6 +1685,7 @@
|
||||
4BA3AE44283317CB00328FED /* RegisterSet.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RegisterSet.hpp; sourceTree = "<group>"; };
|
||||
4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+StdVector.h"; sourceTree = "<group>"; };
|
||||
4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+StdVector.mm"; sourceTree = "<group>"; };
|
||||
4BA6B6AD284EDAC000A3B7A8 /* 68000OldVsNew.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000OldVsNew.mm; sourceTree = "<group>"; };
|
||||
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MasterSystemVDPTests.mm; sourceTree = "<group>"; };
|
||||
4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MediaTarget.hpp; sourceTree = "<group>"; };
|
||||
4BAA167B21582B1D008A3276 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
@ -4235,6 +4238,7 @@
|
||||
4B75F978280D7C5100121055 /* 68000DecoderTests.mm */,
|
||||
4B7C79FF282C3BCA002D6C0B /* 68000flamewingTests.mm */,
|
||||
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
|
||||
4BA6B6AD284EDAC000A3B7A8 /* 68000OldVsNew.mm */,
|
||||
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
|
||||
4BD388872239E198002D14B5 /* 68000Tests.mm */,
|
||||
4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */,
|
||||
@ -5975,6 +5979,7 @@
|
||||
4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */,
|
||||
4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */,
|
||||
4B778F0823A5EC150000D260 /* CSW.cpp in Sources */,
|
||||
4BA6B6AE284EDAC100A3B7A8 /* 68000OldVsNew.mm in Sources */,
|
||||
4B778F5323A5F23F0000D260 /* SerialBus.cpp in Sources */,
|
||||
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
|
||||
4B7752BA28217F160073E2C5 /* Bitplanes.cpp in Sources */,
|
||||
@ -6118,6 +6123,7 @@
|
||||
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */,
|
||||
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
||||
4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */,
|
||||
4B5D497C28513F870076E2F9 /* IPF.cpp in Sources */,
|
||||
4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||
4B778EF323A5DB230000D260 /* PCMSegment.cpp in Sources */,
|
||||
4B778F0D23A5EC150000D260 /* ZX80O81P.cpp in Sources */,
|
||||
|
436
OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm
Normal file
436
OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm
Normal file
@ -0,0 +1,436 @@
|
||||
//
|
||||
// 68000ArithmeticTests.m
|
||||
// Clock SignalTests
|
||||
//
|
||||
// Created by Thomas Harte on 28/06/2019.
|
||||
//
|
||||
// Largely ported from the tests of the Portable 68k Emulator.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "68000.hpp"
|
||||
#include "68000Mk2.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
namespace {
|
||||
|
||||
struct RandomStore {
|
||||
using CollectionT = std::unordered_map<uint32_t, std::pair<uint8_t, uint8_t>>;
|
||||
CollectionT values;
|
||||
|
||||
void flag(uint32_t address, uint8_t participant) {
|
||||
values[address].first |= participant;
|
||||
}
|
||||
|
||||
bool has(uint32_t address, uint8_t participant) {
|
||||
auto entry = values.find(address);
|
||||
if(entry == values.end()) return false;
|
||||
return entry->second.first & participant;
|
||||
}
|
||||
|
||||
uint8_t value(uint32_t address, uint8_t participant) {
|
||||
auto entry = values.find(address);
|
||||
if(entry != values.end()) {
|
||||
entry->second.first |= participant;
|
||||
return entry->second.second;
|
||||
}
|
||||
|
||||
const uint8_t value = uint8_t(rand() >> 8);
|
||||
values[address] = std::make_pair(participant, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
values.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Transaction {
|
||||
HalfCycles timestamp;
|
||||
uint8_t function_code = 0;
|
||||
uint32_t address = 0;
|
||||
uint16_t value = 0;
|
||||
bool address_strobe = false;
|
||||
bool read = false;
|
||||
int data_strobes = 0;
|
||||
|
||||
bool operator !=(const Transaction &rhs) const {
|
||||
if(timestamp != rhs.timestamp) return true;
|
||||
// if(function_code != rhs.function_code) return true;
|
||||
if(address != rhs.address) return true;
|
||||
if(value != rhs.value) return true;
|
||||
if(address_strobe != rhs.address_strobe) return true;
|
||||
if(data_strobes != rhs.data_strobes) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void print() const {
|
||||
printf("%d: %d%d%d %c %c%c @ %06x %s %04x\n",
|
||||
timestamp.as<int>(),
|
||||
(function_code >> 2) & 1,
|
||||
(function_code >> 1) & 1,
|
||||
(function_code >> 0) & 1,
|
||||
address_strobe ? 'a' : '-',
|
||||
(data_strobes & 1) ? 'b' : '-',
|
||||
(data_strobes & 2) ? 'w' : '-',
|
||||
address,
|
||||
read ? "->" : "<-",
|
||||
value);
|
||||
}
|
||||
};
|
||||
|
||||
struct HarmlessStopException {};
|
||||
|
||||
struct BusHandler {
|
||||
BusHandler(RandomStore &_store, uint8_t _participant) : store(_store), participant(_participant) {}
|
||||
|
||||
void will_perform(uint32_t, uint16_t) {
|
||||
--instructions;
|
||||
if(instructions < 0) {
|
||||
throw HarmlessStopException{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, bool is_supervisor) {
|
||||
Transaction transaction;
|
||||
|
||||
// Fill all of the transaction record except the data field; will do that after
|
||||
// any potential read.
|
||||
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
||||
transaction.function_code = 0b111;
|
||||
} else {
|
||||
transaction.function_code = is_supervisor ? 0x4 : 0x0;
|
||||
transaction.function_code |= (cycle.operation & Microcycle::IsData) ? 0x1 : 0x2;
|
||||
}
|
||||
transaction.address_strobe = cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress);
|
||||
transaction.data_strobes = cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord);
|
||||
if(cycle.address) transaction.address = *cycle.address & 0xffff'ff;
|
||||
transaction.timestamp = time;
|
||||
transaction.read = cycle.operation & Microcycle::Read;
|
||||
|
||||
time += cycle.length;
|
||||
|
||||
// Do the operation...
|
||||
const uint32_t address = cycle.address ? (*cycle.address & 0xffff'ff) : 0;
|
||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
default: break;
|
||||
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
if(!store.has(address, participant)) {
|
||||
ram[address] = store.value(address, participant);
|
||||
}
|
||||
if(!store.has(address+1, participant)) {
|
||||
ram[address+1] = store.value(address+1, participant);
|
||||
}
|
||||
|
||||
cycle.set_value16((ram[address] << 8) | ram[address + 1]);
|
||||
break;
|
||||
case Microcycle::SelectByte | Microcycle::Read:
|
||||
if(!store.has(address, participant)) {
|
||||
ram[address] = store.value(address, participant);
|
||||
}
|
||||
|
||||
if(address & 1) {
|
||||
cycle.set_value8_low(ram[address]);
|
||||
} else {
|
||||
cycle.set_value8_high(ram[address]);
|
||||
}
|
||||
break;
|
||||
case Microcycle::SelectWord:
|
||||
ram[address] = cycle.value8_high();
|
||||
ram[address+1] = cycle.value8_low();
|
||||
store.flag(address, participant);
|
||||
store.flag(address+1, participant);
|
||||
break;
|
||||
case Microcycle::SelectByte:
|
||||
ram[address] = (address & 1) ? cycle.value8_low() : cycle.value8_high();
|
||||
store.flag(address, participant);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Add the data value if relevant.
|
||||
if(transaction.data_strobes) {
|
||||
transaction.value = cycle.value16();
|
||||
}
|
||||
|
||||
// Push back only if interesting.
|
||||
if(transaction.address_strobe || transaction.data_strobes || transaction.function_code == 7) {
|
||||
if(transaction_delay) {
|
||||
--transaction_delay;
|
||||
|
||||
// Start counting time only from the first recorded transaction.
|
||||
if(!transaction_delay) {
|
||||
time = HalfCycles(0);
|
||||
}
|
||||
} else {
|
||||
transactions.push_back(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
void flush() {}
|
||||
|
||||
int transaction_delay;
|
||||
int instructions;
|
||||
|
||||
HalfCycles time;
|
||||
std::vector<Transaction> transactions;
|
||||
std::array<uint8_t, 16*1024*1024> ram;
|
||||
|
||||
void set_default_vectors() {
|
||||
// Establish that all exception vectors point to 1024-byte blocks of memory.
|
||||
for(int c = 0; c < 256; c++) {
|
||||
const uint32_t target = (c + 2) << 10;
|
||||
const uint32_t address = c << 2;
|
||||
ram[address + 0] = uint8_t(target >> 24);
|
||||
ram[address + 1] = uint8_t(target >> 16);
|
||||
ram[address + 2] = uint8_t(target >> 8);
|
||||
ram[address + 3] = uint8_t(target >> 0);
|
||||
|
||||
store.flag(address+0, participant);
|
||||
store.flag(address+1, participant);
|
||||
store.flag(address+2, participant);
|
||||
store.flag(address+3, participant);
|
||||
}
|
||||
}
|
||||
|
||||
RandomStore &store;
|
||||
const uint8_t participant;
|
||||
};
|
||||
|
||||
using OldProcessor = CPU::MC68000::Processor<BusHandler, true, true>;
|
||||
using NewProcessor = CPU::MC68000Mk2::Processor<BusHandler, true, true, true>;
|
||||
|
||||
template <typename M68000> struct Tester {
|
||||
Tester(RandomStore &store, uint8_t participant) : bus_handler(store, participant), processor(bus_handler) {}
|
||||
|
||||
void run_instructions(int instructions) {
|
||||
bus_handler.instructions = instructions;
|
||||
|
||||
try {
|
||||
processor.run_for(HalfCycles(5000)); // Arbitrary, but will definitely exceed any one instruction (by quite a distance).
|
||||
} catch (const HarmlessStopException &) {}
|
||||
}
|
||||
|
||||
void reset_with_opcode(uint16_t opcode) {
|
||||
bus_handler.transactions.clear();
|
||||
bus_handler.set_default_vectors();
|
||||
|
||||
const uint32_t address = 3 << 10;
|
||||
bus_handler.ram[address + 0] = uint8_t(opcode >> 8);
|
||||
bus_handler.ram[address + 1] = uint8_t(opcode >> 0);
|
||||
bus_handler.store.flag(address, bus_handler.participant);
|
||||
bus_handler.store.flag(address+1, bus_handler.participant);
|
||||
|
||||
bus_handler.transaction_delay = 12; // i.e. ignore everything from the RESET sequence.
|
||||
bus_handler.time = HalfCycles(0);
|
||||
|
||||
processor.reset();
|
||||
}
|
||||
|
||||
BusHandler bus_handler;
|
||||
M68000 processor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@interface M68000OldVsNewTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation M68000OldVsNewTests
|
||||
|
||||
- (void)testOldVsNew {
|
||||
RandomStore random_store;
|
||||
auto oldTester = std::make_unique<Tester<OldProcessor>>(random_store, 0x01);
|
||||
auto newTester = std::make_unique<Tester<NewProcessor>>(random_store, 0x02);
|
||||
InstructionSet::M68k::Predecoder<InstructionSet::M68k::Model::M68000> decoder;
|
||||
|
||||
// Use a fixed seed to guarantee continuity across repeated runs.
|
||||
srand(68000);
|
||||
|
||||
std::set<InstructionSet::M68k::Operation> ignore_list = {
|
||||
//
|
||||
// Operations that do the wrong thing on the old 68000:
|
||||
//
|
||||
InstructionSet::M68k::Operation::ABCD, // Old implementation doesn't match flamewing tests, sometimes produces incorrect results.
|
||||
InstructionSet::M68k::Operation::SBCD, // Old implementation doesn't match flamewing tests, sometimes produces incorrect results.
|
||||
InstructionSet::M68k::Operation::JSR, // Old implementation ends up skipping stack space if the destination throws an address error.
|
||||
InstructionSet::M68k::Operation::MOVEtoSR, // Old implementation doesn't repeat a PC fetch.
|
||||
InstructionSet::M68k::Operation::MOVEtoCCR, // Old implementation doesn't repeat a PC fetch.
|
||||
|
||||
//
|
||||
// Operations with definite timing deficiencies versus Yacht.txt on the old 68000:
|
||||
//
|
||||
InstructionSet::M68k::Operation::CMPAl, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::CLRb, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::CLRw, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NEGXb, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NEGXw, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NEGb, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NEGw, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NOTb, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::NOTw, // Old implementation omits an idle cycle before -(An)
|
||||
InstructionSet::M68k::Operation::TRAP, // Old implementation relocates the idle state near the end to the beginning.
|
||||
InstructionSet::M68k::Operation::TRAPV, // Old implementation relocates the idle state near the end to the beginning.
|
||||
InstructionSet::M68k::Operation::CHK, // Old implementation pauses four cycles too long.
|
||||
InstructionSet::M68k::Operation::TAS, // Old implementation just doesn't match published cycle counts.
|
||||
|
||||
//
|
||||
// Operations with timing discrepancies between the two 68000 implementations
|
||||
// that I think are _more_ accurate now, but possibly still need work:
|
||||
//
|
||||
InstructionSet::M68k::Operation::MULU,
|
||||
InstructionSet::M68k::Operation::MULS,
|
||||
InstructionSet::M68k::Operation::DIVU,
|
||||
InstructionSet::M68k::Operation::DIVS,
|
||||
};
|
||||
|
||||
int testsRun = 0;
|
||||
std::set<InstructionSet::M68k::Operation> failing_operations;
|
||||
for(int c = 0; c < 65536; c++) {
|
||||
// printf("%04x\n", c);
|
||||
|
||||
// Test only defined opcodes that aren't STOP (which will never teminate).
|
||||
const auto instruction = decoder.decode(uint16_t(c));
|
||||
if(
|
||||
instruction.operation == InstructionSet::M68k::Operation::Undefined ||
|
||||
instruction.operation == InstructionSet::M68k::Operation::STOP
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this operation is known to diverge, ignore it. It's dealt with.
|
||||
if(ignore_list.find(instruction.operation) != ignore_list.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test each 1000 times.
|
||||
for(int test = 0; test < 1000; test++) {
|
||||
++testsRun;
|
||||
|
||||
// Establish with certainty the initial memory state.
|
||||
random_store.clear();
|
||||
newTester->reset_with_opcode(c);
|
||||
oldTester->reset_with_opcode(c);
|
||||
|
||||
// Generate a random initial register state.
|
||||
auto oldState = oldTester->processor.get_state();
|
||||
auto newState = newTester->processor.get_state();
|
||||
|
||||
for(int c = 0; c < 8; c++) {
|
||||
oldState.data[c] = newState.registers.data[c] = rand() ^ (rand() << 1);
|
||||
if(c != 7) oldState.address[c] = newState.registers.address[c] = rand() << 1;
|
||||
}
|
||||
// Fully to paper over the two 68000s' different ways of doing a faked
|
||||
// reset, pick a random status such that:
|
||||
//
|
||||
// (i) supervisor mode is active;
|
||||
// (ii) trace is inactive; and
|
||||
// (iii) interrupt level is 7.
|
||||
oldState.status = newState.registers.status = (rand() | (1 << 13) | (7 << 8)) & ~(1 << 15);
|
||||
oldState.user_stack_pointer = newState.registers.user_stack_pointer = rand() << 1;
|
||||
oldState.supervisor_stack_pointer = newState.registers.supervisor_stack_pointer = 0x800;
|
||||
|
||||
newTester->processor.set_state(newState);
|
||||
oldTester->processor.set_state(oldState);
|
||||
|
||||
// Run a single instruction.
|
||||
newTester->run_instructions(1);
|
||||
oldTester->run_instructions(1);
|
||||
|
||||
// Grab final states.
|
||||
oldState = oldTester->processor.get_state();
|
||||
newState = newTester->processor.get_state();
|
||||
|
||||
// Compare bus activity only if it doesn't look like an address
|
||||
// error occurred. Don't check those as the old 68000 appears to be wrong
|
||||
// most of the time about function codes, and that bleeds into the stacked data.
|
||||
//
|
||||
// Net effect will be 50% fewer transaction comparisons for instructions that
|
||||
// can trigger an address error.
|
||||
const auto &oldTransactions = oldTester->bus_handler.transactions;
|
||||
const auto &newTransactions = newTester->bus_handler.transactions;
|
||||
if(oldState.program_counter != 0x1404 || newState.registers.program_counter != 0x1404) {
|
||||
auto newIt = newTransactions.begin();
|
||||
auto oldIt = oldTransactions.begin();
|
||||
while(newIt != newTransactions.end() && oldIt != oldTransactions.end()) {
|
||||
if(*newIt != *oldIt) {
|
||||
printf("Mismatch in %s, test %d:\n", instruction.to_string().c_str(), test);
|
||||
|
||||
auto repeatIt = newTransactions.begin();
|
||||
while(repeatIt != newIt) {
|
||||
repeatIt->print();
|
||||
++repeatIt;
|
||||
}
|
||||
printf("---\n");
|
||||
while(newIt != newTransactions.end()) {
|
||||
printf("n: "); newIt->print();
|
||||
++newIt;
|
||||
}
|
||||
printf("\n");
|
||||
while(oldIt != oldTransactions.end()) {
|
||||
printf("o: "); oldIt->print();
|
||||
++oldIt;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
failing_operations.insert(instruction.operation);
|
||||
break;
|
||||
}
|
||||
|
||||
++newIt;
|
||||
++oldIt;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare registers.
|
||||
bool mismatch = false;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
mismatch |= oldState.data[c] != newState.registers.data[c];
|
||||
if(c != 7) mismatch |= oldState.address[c] != newState.registers.address[c];
|
||||
}
|
||||
mismatch |= oldState.status != newState.registers.status;
|
||||
mismatch |= oldState.program_counter != newState.registers.program_counter;
|
||||
mismatch |= oldState.user_stack_pointer != newState.registers.user_stack_pointer;
|
||||
mismatch |= oldState.supervisor_stack_pointer != newState.registers.supervisor_stack_pointer;
|
||||
|
||||
if(mismatch) {
|
||||
failing_operations.insert(instruction.operation);
|
||||
printf("Registers don't match after %s, test %d\n", instruction.to_string().c_str(), test);
|
||||
for(const auto &transaction: newTransactions) {
|
||||
printf("n: "); transaction.print();
|
||||
}
|
||||
printf("\n");
|
||||
for(const auto &transaction: oldTransactions) {
|
||||
printf("o: "); transaction.print();
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// TODO: more detail here!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d tests run\n", testsRun);
|
||||
if(failing_operations.empty()) {
|
||||
printf("No failures\n");
|
||||
} else {
|
||||
printf("\nAll failing operations:\n");
|
||||
for(const auto operation: failing_operations) {
|
||||
printf("%d,\n", int(operation));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the test as passed or failed.
|
||||
XCTAssert(failing_operations.empty());
|
||||
}
|
||||
|
||||
@end
|
@ -189,6 +189,9 @@
|
||||
// PC.
|
||||
XCTAssertEqual(stack_frame[5], 0x0000);
|
||||
XCTAssertEqual(stack_frame[6], 0x1004);
|
||||
|
||||
// Check that A7 ended up in the proper location.
|
||||
XCTAssertEqual(_machine->get_processor_state().registers.stack_pointer(), 0x1f8);
|
||||
}
|
||||
|
||||
- (void)testShiftDuration {
|
||||
|
@ -462,6 +462,8 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform = false> cla
|
||||
return e_clock_phase_;
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
T &bus_handler_;
|
||||
};
|
||||
|
@ -2237,6 +2237,15 @@ void ProcessorStorage::set_status(uint16_t status) {
|
||||
apply_status(status);
|
||||
}
|
||||
|
||||
template <class T, bool dtack_is_implicit, bool signal_will_perform> void Processor<T, dtack_is_implicit, signal_will_perform>::reset() {
|
||||
execution_state_ = ExecutionState::Executing;
|
||||
active_step_ = reset_bus_steps_;
|
||||
effective_address_[0] = 0;
|
||||
is_supervisor_ = 1;
|
||||
interrupt_level_ = 7;
|
||||
half_cycles_left_to_run_ = HalfCycles(0);
|
||||
}
|
||||
|
||||
#undef status
|
||||
#undef apply_status
|
||||
#undef apply_ccr
|
||||
|
@ -175,17 +175,17 @@ struct Microcycle {
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns non-zero if this is a byte read and 68000 LDS is asserted.
|
||||
@returns non-zero if the 68000 LDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int lower_data_select() const {
|
||||
return (operation & SelectByte) & ((*address & 1) << 3);
|
||||
return ((operation & SelectByte) & (*address & 1)) | (operation & SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns non-zero if this is a byte read and 68000 UDS is asserted.
|
||||
@returns non-zero if the 68000 UDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int upper_data_select() const {
|
||||
return (operation & SelectByte) & ~((*address & 1) << 3);
|
||||
return ((operation & SelectByte) & ~(*address & 1)) | (operation & SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -229,21 +229,18 @@ struct Microcycle {
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the value currently on the high 8 lines of the data bus if any;
|
||||
@c 0xff otherwise. Assumes this is a write cycle.
|
||||
@returns the value currently on the high 8 lines of the data bus.
|
||||
*/
|
||||
forceinline uint8_t value8_high() const {
|
||||
const uint8_t values[] = { uint8_t(value->w), value->b};
|
||||
const uint8_t values[] = { uint8_t(value->w >> 8), value->b};
|
||||
return values[operation & SelectByte];
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the value currently on the low 8 lines of the data bus if any;
|
||||
@c 0xff otherwise. Assumes this is a write cycle.
|
||||
@returns the value currently on the low 8 lines of the data bus.
|
||||
*/
|
||||
forceinline uint8_t value8_low() const {
|
||||
const uint8_t values[] = { uint8_t(value->w), value->b};
|
||||
return values[operation & SelectByte];
|
||||
return value->b;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -394,6 +391,8 @@ template <class BusHandler, bool dtack_is_implicit = true, bool permit_overrun =
|
||||
class Processor: private ProcessorBase {
|
||||
public:
|
||||
Processor(BusHandler &bus_handler) : ProcessorBase(), bus_handler_(bus_handler) {}
|
||||
Processor(const Processor& rhs) = delete;
|
||||
Processor& operator=(const Processor& rhs) = delete;
|
||||
|
||||
void run_for(HalfCycles duration);
|
||||
|
||||
@ -441,6 +440,8 @@ class Processor: private ProcessorBase {
|
||||
return e_clock_phase_;
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
BusHandler &bus_handler_;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,8 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
ProcessorBase() {
|
||||
read_program_announce.address = read_program.address = &program_counter_.l;
|
||||
}
|
||||
ProcessorBase(const ProcessorBase& rhs) = delete;
|
||||
ProcessorBase& operator=(const ProcessorBase& rhs) = delete;
|
||||
|
||||
int state_ = std::numeric_limits<int>::min();
|
||||
|
||||
@ -210,6 +212,12 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
// Reset.
|
||||
Microcycle reset_cycle { Microcycle::Reset, HalfCycles(248) };
|
||||
|
||||
// Interrupt acknowledge.
|
||||
Microcycle interrupt_cycles[2] = {
|
||||
{ Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress },
|
||||
{ Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte },
|
||||
};
|
||||
|
||||
// Holding spot when awaiting DTACK/etc.
|
||||
Microcycle awaiting_dtack;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user