From 68f810883dfd49fc3391d7c1cc4b46d9b439290d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 2 Sep 2022 16:52:27 -0400 Subject: [PATCH 01/12] Begin process of creating on-disk tests. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 8ba3bd9ef..507decc3d 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -238,6 +238,85 @@ template struct Tester { M68000 processor; }; +void print_state(FILE *target, const CPU::MC68000Mk2::State &state) { + for(int c = 0; c < 8; c++) { + fprintf(target, "\"D%d\": %u, ", c, state.registers.data[c]); + } + + for(int c = 0; c < 7; c++) { + fprintf(target, "\"A%d\": %u, ", c, state.registers.address[c]); + } + + fprintf(target, "\"USP\": %u, ", state.registers.user_stack_pointer); + fprintf(target, "\"SSP\": %u, ", state.registers.supervisor_stack_pointer); + fprintf(target, "\"SR\": %u, ", state.registers.status); + fprintf(target, "\"PC\": %u", state.registers.program_counter - 4); +} + +void print_transactions(FILE *target, const std::vector &transactions, HalfCycles end) { + auto iterator = transactions.begin(); + bool is_first = true; + do { + if(!is_first) fprintf(target, ", "); + is_first = false; + fprintf(target, "["); + + auto next = iterator + 1; + + // Attempt to pair off transactions to reproduct YACHT notation. + bool is_access = true; + if(!iterator->address_strobe && !iterator->data_strobes) { + fprintf(target, "\"n\", "); + is_access = false; + } else { + assert(!iterator->data_strobes); + if(next->read) { + fprintf(target, "\"nr\", "); + } else { + fprintf(target, "\"nw\", "); + } + ++next; + } + HalfCycles length; + if(next == transactions.end()) { + length = end - iterator->timestamp; + } else { + length = next->timestamp - iterator->timestamp; + } + fprintf(target, "%d, ", length.as() >> 1); + + fprintf(target, "%d, ", iterator->function_code); + + if(is_access) { + --next; + fprintf(target, "%d, ", iterator->address & 0xff'ffff); + + switch(next->data_strobes) { + default: assert(false); + case 1: { + if(next->address & 1) { + fprintf(target, "\"-L\", "); + } else { + fprintf(target, "\"U-\", "); + } + } break; + case 2: fprintf(target, "\"UL\", "); break; + break; + } + fprintf(target, "%d", next->value); + + ++next; + } else { + fprintf(target, "null, "); + fprintf(target, "\"--\", "); + fprintf(target, "null"); + } + + fprintf(target, "]"); + iterator = next; + } while(iterator != transactions.end()); +} + } @interface M68000OldVsNewTests : XCTestCase @@ -245,6 +324,86 @@ template struct Tester { @implementation M68000OldVsNewTests +- (void)testGenerate { + srand(68000); + + NSString *const tempDir = NSTemporaryDirectory(); + NSLog(@"Outputting to %@", tempDir); + + RandomStore random_store; + auto tester = std::make_unique>(random_store, 0x02); + InstructionSet::M68k::Predecoder decoder; + + for(int c = 0; c < 65536; 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; + } + + NSString *const targetName = [NSString stringWithFormat:@"%@%04x.json", tempDir, c]; + FILE *const target = fopen(targetName.UTF8String, "wt"); + + bool is_first_test = true; + fprintf(target, "["); + + // Test each 10000 times. + for(int test = 0; test < 10000; test++) { + if(!is_first_test) fprintf(target, ",\n"); + is_first_test = false; + + // Establish with certainty the initial memory state. + random_store.clear(); + tester->reset_with_opcode(c); + + // Generate a random initial register state. + auto initialState = tester->processor.get_state(); + + for(int c = 0; c < 8; c++) { + initialState.registers.data[c] = rand() ^ (rand() << 1); + if(c != 7) initialState.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. + initialState.registers.status = (rand() | (1 << 13) | (7 << 8)) & ~(1 << 15); + initialState.registers.user_stack_pointer = rand() << 1; + initialState.registers.supervisor_stack_pointer = 0x800; + + // Dump initial state. + fprintf(target, "{ \"name\": \"%04x %s %d\", ", c, instruction.to_string().c_str(), test + 1); + fprintf(target, "\"initial\": {"); + print_state(target, initialState); + + tester->processor.set_state(initialState); + + // Run a single instruction. + tester->run_instructions(1); + + // Grab and output final states. + const auto finalState = tester->processor.get_state(); + fprintf(target, "}, \"final\": {"); + print_state(target, finalState); + + // Output total length and bus activity. + fprintf(target, "}, \"length\": %d, ", tester->bus_handler.time.as() >> 1); + + fprintf(target, "\"transactions\": ["); + print_transactions(target, tester->bus_handler.transactions, tester->bus_handler.time); + fprintf(target, "]}"); + } + + fprintf(target, "\n]\n"); + fclose(target); + } +} + - (void)testOldVsNew { RandomStore random_store; auto oldTester = std::make_unique>(random_store, 0x01); From cee3f7805962ffdec3c07591c245d8a1dd8c4e5c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 3 Sep 2022 15:45:06 -0400 Subject: [PATCH 02/12] Attempt to output only relevant RAM. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 90 ++++++++++++++++--- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 507decc3d..3598a2410 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -15,6 +15,7 @@ #include #include #include +#include namespace { @@ -116,7 +117,7 @@ struct BusHandler { time += cycle.length; // Do the operation... - const uint32_t address = cycle.address ? (*cycle.address & 0xffff'ff) : 0; + const uint32_t address = cycle.address ? (*cycle.address & 0xff'ffff) : 0; switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) { default: break; @@ -153,7 +154,6 @@ struct BusHandler { break; } - // Add the data value if relevant. if(transaction.data_strobes) { transaction.value = cycle.value16(); @@ -238,7 +238,7 @@ template struct Tester { M68000 processor; }; -void print_state(FILE *target, const CPU::MC68000Mk2::State &state) { +void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::vector &transactions, bool is_initial) { for(int c = 0; c < 8; c++) { fprintf(target, "\"D%d\": %u, ", c, state.registers.data[c]); } @@ -250,7 +250,73 @@ void print_state(FILE *target, const CPU::MC68000Mk2::State &state) { fprintf(target, "\"USP\": %u, ", state.registers.user_stack_pointer); fprintf(target, "\"SSP\": %u, ", state.registers.supervisor_stack_pointer); fprintf(target, "\"SR\": %u, ", state.registers.status); - fprintf(target, "\"PC\": %u", state.registers.program_counter - 4); + fprintf(target, "\"PC\": %u, ", state.registers.program_counter - 4); + + fprintf(target, "\"ram\": ["); + + // Compute RAM from transactions; if this is the initial state then RAM should + // be everything that was subject to a read which had not previously been + // subject to a write. Otherwise it can just be everything. + std::map ram; + if(is_initial) { + std::set written_addresses; + + for(const auto &transaction: transactions) { + switch(transaction.data_strobes) { + default: continue; + case 1: + if(transaction.read) { + if(ram.find(transaction.address) == ram.end()) { + ram[transaction.address] = transaction.value; + } + } else { + written_addresses.insert(transaction.address); + } + break; + case 2: + if(transaction.read) { + if(ram.find(transaction.address) == ram.end()) { + ram[transaction.address] = uint8_t(transaction.value >> 8); + } + if(ram.find(transaction.address+1) == ram.end()) { + ram[transaction.address+1] = uint8_t(transaction.value); + } + } else { + written_addresses.insert(transaction.address); + written_addresses.insert(transaction.address + 1); + } + break; + } + } + } else { + for(const auto &transaction: transactions) { + switch(transaction.data_strobes) { + default: continue; + case 1: + if(ram.find(transaction.address) == ram.end()) { + ram[transaction.address] = transaction.value; + } + break; + case 2: + if(ram.find(transaction.address) == ram.end()) { + ram[transaction.address] = uint8_t(transaction.value >> 8); + } + if(ram.find(transaction.address+1) == ram.end()) { + ram[transaction.address+1] = uint8_t(transaction.value); + } + break; + } + } + + } + + bool is_first = true; + for(const auto &pair: ram) { + if(!is_first) fprintf(target, ", "); + is_first = false; + fprintf(target, "[%d, %d]", pair.first, pair.second); + } + fprintf(target, "]"); } void print_transactions(FILE *target, const std::vector &transactions, HalfCycles end) { @@ -376,20 +442,22 @@ void print_transactions(FILE *target, const std::vector &transactio initialState.registers.user_stack_pointer = rand() << 1; initialState.registers.supervisor_stack_pointer = 0x800; - // Dump initial state. - fprintf(target, "{ \"name\": \"%04x %s %d\", ", c, instruction.to_string().c_str(), test + 1); - fprintf(target, "\"initial\": {"); - print_state(target, initialState); - + // Set state. tester->processor.set_state(initialState); // Run a single instruction. tester->run_instructions(1); - // Grab and output final states. const auto finalState = tester->processor.get_state(); + + // Output initial state. + fprintf(target, "{ \"name\": \"%04x [%s] %d\", ", c, instruction.to_string().c_str(), test + 1); + fprintf(target, "\"initial\": {"); + print_state(target, initialState, tester->bus_handler.transactions, true); + + // Output final state. fprintf(target, "}, \"final\": {"); - print_state(target, finalState); + print_state(target, finalState, tester->bus_handler.transactions, false); // Output total length and bus activity. fprintf(target, "}, \"length\": %d, ", tester->bus_handler.time.as() >> 1); From effe8c102d862dff3a6a907abd78d4a70e671caa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2022 21:52:20 -0400 Subject: [PATCH 03/12] Provide a direct `to_string` on `Operation`. --- InstructionSets/M68k/Instruction.cpp | 324 +++++++++++++-------------- InstructionSets/M68k/Instruction.hpp | 2 + 2 files changed, 152 insertions(+), 174 deletions(-) diff --git a/InstructionSets/M68k/Instruction.cpp b/InstructionSets/M68k/Instruction.cpp index c52ebe0ee..16420bb8d 100644 --- a/InstructionSets/M68k/Instruction.cpp +++ b/InstructionSets/M68k/Instruction.cpp @@ -56,233 +56,209 @@ std::string Preinstruction::operand_description(int index, int opcode) const { } } -std::string Preinstruction::to_string(int opcode) const { - bool flip_operands = false; - const char *instruction; +namespace { +const char *_to_string(Operation operation, bool is_quick) { switch(operation) { - case Operation::Undefined: return "None"; - case Operation::NOP: instruction = "NOP"; break; - case Operation::ABCD: instruction = "ABCD"; break; - case Operation::SBCD: instruction = "SBCD"; break; - case Operation::NBCD: instruction = "NBCD"; break; + case Operation::Undefined: return "None"; + case Operation::NOP: return "NOP"; + case Operation::ABCD: return "ABCD"; + case Operation::SBCD: return "SBCD"; + case Operation::NBCD: return "NBCD"; - case Operation::ADDb: instruction = "ADD.b"; break; - case Operation::ADDw: instruction = "ADD.w"; break; - case Operation::ADDl: instruction = "ADD.l"; break; + case Operation::ADDb: return "ADD.b"; + case Operation::ADDw: return "ADD.w"; + case Operation::ADDl: return "ADD.l"; - case Operation::ADDAw: - if(mode<0>() == AddressingMode::Quick) { - instruction = "ADD.w"; - } else { - instruction = "ADDA.w"; - } - break; - case Operation::ADDAl: - if(mode<0>() == AddressingMode::Quick) { - instruction = "ADD.l"; - } else { - instruction = "ADDA.l"; - } - break; + case Operation::ADDAw: return is_quick ? "ADD.w" : "ADDA.w"; + case Operation::ADDAl: return is_quick ? "ADD.l" : "ADDA.l"; - case Operation::ADDXb: instruction = "ADDX.b"; break; - case Operation::ADDXw: instruction = "ADDX.w"; break; - case Operation::ADDXl: instruction = "ADDX.l"; break; + case Operation::ADDXb: return "ADDX.b"; + case Operation::ADDXw: return "ADDX.w"; + case Operation::ADDXl: return "ADDX.l"; - case Operation::SUBb: instruction = "SUB.b"; break; - case Operation::SUBw: instruction = "SUB.w"; break; - case Operation::SUBl: instruction = "SUB.l"; break; + case Operation::SUBb: return "SUB.b"; + case Operation::SUBw: return "SUB.w"; + case Operation::SUBl: return "SUB.l"; - case Operation::SUBAw: - if(mode<0>() == AddressingMode::Quick) { - instruction = "SUB.w"; - } else { - instruction = "SUBA.w"; - } - break; - case Operation::SUBAl: - if(mode<0>() == AddressingMode::Quick) { - instruction = "SUB.l"; - } else { - instruction = "SUBA.l"; - } - break; + case Operation::SUBAw: return is_quick ? "SUB.w" : "SUBA.w"; + case Operation::SUBAl: return is_quick ? "SUB.l" : "SUBA.l"; - case Operation::SUBXb: instruction = "SUBX.b"; break; - case Operation::SUBXw: instruction = "SUBX.w"; break; - case Operation::SUBXl: instruction = "SUBX.l"; break; + case Operation::SUBXb: return "SUBX.b"; + case Operation::SUBXw: return "SUBX.w"; + case Operation::SUBXl: return "SUBX.l"; - case Operation::MOVEb: instruction = "MOVE.b"; break; - case Operation::MOVEw: instruction = "MOVE.w"; break; - case Operation::MOVEl: - if(mode<0>() == AddressingMode::Quick) { - instruction = "MOVE.q"; - } else { - instruction = "MOVE.l"; - } - break; + case Operation::MOVEb: return "MOVE.b"; + case Operation::MOVEw: return "MOVE.w"; + case Operation::MOVEl: return is_quick ? "MOVE.q" : "MOVE.l"; - case Operation::MOVEAw: instruction = "MOVEA.w"; break; - case Operation::MOVEAl: instruction = "MOVEA.l"; break; + case Operation::MOVEAw: return "MOVEA.w"; + case Operation::MOVEAl: return "MOVEA.l"; - case Operation::LEA: instruction = "LEA"; break; - case Operation::PEA: instruction = "PEA"; break; + case Operation::LEA: return "LEA"; + case Operation::PEA: return "PEA"; - case Operation::MOVEtoSR: instruction = "MOVEtoSR"; break; - case Operation::MOVEfromSR: instruction = "MOVEfromSR"; break; - case Operation::MOVEtoCCR: instruction = "MOVEtoCCR"; break; - case Operation::MOVEtoUSP: instruction = "MOVEtoUSP"; break; - case Operation::MOVEfromUSP: instruction = "MOVEfromUSP"; break; + case Operation::MOVEtoSR: return "MOVEtoSR"; + case Operation::MOVEfromSR: return "MOVEfromSR"; + case Operation::MOVEtoCCR: return "MOVEtoCCR"; + case Operation::MOVEtoUSP: return "MOVEtoUSP"; + case Operation::MOVEfromUSP: return "MOVEfromUSP"; - case Operation::ORItoSR: instruction = "ORItoSR"; break; - case Operation::ORItoCCR: instruction = "ORItoCCR"; break; - case Operation::ANDItoSR: instruction = "ANDItoSR"; break; - case Operation::ANDItoCCR: instruction = "ANDItoCCR"; break; - case Operation::EORItoSR: instruction = "EORItoSR"; break; - case Operation::EORItoCCR: instruction = "EORItoCCR"; break; + case Operation::ORItoSR: return "ORItoSR"; + case Operation::ORItoCCR: return "ORItoCCR"; + case Operation::ANDItoSR: return "ANDItoSR"; + case Operation::ANDItoCCR: return "ANDItoCCR"; + case Operation::EORItoSR: return "EORItoSR"; + case Operation::EORItoCCR: return "EORItoCCR"; - case Operation::BTST: instruction = "BTST"; break; - case Operation::BCLR: instruction = "BCLR"; break; - case Operation::BCHG: instruction = "BCHG"; break; - case Operation::BSET: instruction = "BSET"; break; + case Operation::BTST: return "BTST"; + case Operation::BCLR: return "BCLR"; + case Operation::BCHG: return "BCHG"; + case Operation::BSET: return "BSET"; - case Operation::CMPb: instruction = "CMP.b"; break; - case Operation::CMPw: instruction = "CMP.w"; break; - case Operation::CMPl: instruction = "CMP.l"; break; + case Operation::CMPb: return "CMP.b"; + case Operation::CMPw: return "CMP.w"; + case Operation::CMPl: return "CMP.l"; - case Operation::CMPAw: instruction = "CMPA.w"; break; - case Operation::CMPAl: instruction = "CMPA.l"; break; + case Operation::CMPAw: return "CMPA.w"; + case Operation::CMPAl: return "CMPA.l"; - case Operation::TSTb: instruction = "TST.b"; break; - case Operation::TSTw: instruction = "TST.w"; break; - case Operation::TSTl: instruction = "TST.l"; break; + case Operation::TSTb: return "TST.b"; + case Operation::TSTw: return "TST.w"; + case Operation::TSTl: return "TST.l"; - case Operation::JMP: instruction = "JMP"; break; - case Operation::JSR: instruction = "JSR"; break; - case Operation::RTS: instruction = "RTS"; break; - case Operation::DBcc: instruction = "DBcc"; break; - case Operation::Scc: instruction = "Scc"; break; + case Operation::JMP: return "JMP"; + case Operation::JSR: return "JSR"; + case Operation::RTS: return "RTS"; + case Operation::DBcc: return "DBcc"; + case Operation::Scc: return "Scc"; case Operation::Bccb: case Operation::Bccl: - case Operation::Bccw: instruction = "Bcc"; break; + case Operation::Bccw: return "Bcc"; case Operation::BSRb: case Operation::BSRl: - case Operation::BSRw: instruction = "BSR"; break; + case Operation::BSRw: return "BSR"; - case Operation::CLRb: instruction = "CLR.b"; break; - case Operation::CLRw: instruction = "CLR.w"; break; - case Operation::CLRl: instruction = "CLR.l"; break; + case Operation::CLRb: return "CLR.b"; + case Operation::CLRw: return "CLR.w"; + case Operation::CLRl: return "CLR.l"; - case Operation::NEGXb: instruction = "NEGX.b"; break; - case Operation::NEGXw: instruction = "NEGX.w"; break; - case Operation::NEGXl: instruction = "NEGX.l"; break; + case Operation::NEGXb: return "NEGX.b"; + case Operation::NEGXw: return "NEGX.w"; + case Operation::NEGXl: return "NEGX.l"; - case Operation::NEGb: instruction = "NEG.b"; break; - case Operation::NEGw: instruction = "NEG.w"; break; - case Operation::NEGl: instruction = "NEG.l"; break; + case Operation::NEGb: return "NEG.b"; + case Operation::NEGw: return "NEG.w"; + case Operation::NEGl: return "NEG.l"; - case Operation::ASLb: instruction = "ASL.b"; break; - case Operation::ASLw: instruction = "ASL.w"; break; - case Operation::ASLl: instruction = "ASL.l"; break; - case Operation::ASLm: instruction = "ASL.w"; break; + case Operation::ASLb: return "ASL.b"; + case Operation::ASLw: return "ASL.w"; + case Operation::ASLl: return "ASL.l"; + case Operation::ASLm: return "ASL.w"; - case Operation::ASRb: instruction = "ASR.b"; break; - case Operation::ASRw: instruction = "ASR.w"; break; - case Operation::ASRl: instruction = "ASR.l"; break; - case Operation::ASRm: instruction = "ASR.w"; break; + case Operation::ASRb: return "ASR.b"; + case Operation::ASRw: return "ASR.w"; + case Operation::ASRl: return "ASR.l"; + case Operation::ASRm: return "ASR.w"; - case Operation::LSLb: instruction = "LSL.b"; break; - case Operation::LSLw: instruction = "LSL.w"; break; - case Operation::LSLl: instruction = "LSL.l"; break; - case Operation::LSLm: instruction = "LSL.w"; break; + case Operation::LSLb: return "LSL.b"; + case Operation::LSLw: return "LSL.w"; + case Operation::LSLl: return "LSL.l"; + case Operation::LSLm: return "LSL.w"; - case Operation::LSRb: instruction = "LSR.b"; break; - case Operation::LSRw: instruction = "LSR.w"; break; - case Operation::LSRl: instruction = "LSR.l"; break; - case Operation::LSRm: instruction = "LSR.w"; break; + case Operation::LSRb: return "LSR.b"; + case Operation::LSRw: return "LSR.w"; + case Operation::LSRl: return "LSR.l"; + case Operation::LSRm: return "LSR.w"; - case Operation::ROLb: instruction = "ROL.b"; break; - case Operation::ROLw: instruction = "ROL.w"; break; - case Operation::ROLl: instruction = "ROL.l"; break; - case Operation::ROLm: instruction = "ROL.w"; break; + case Operation::ROLb: return "ROL.b"; + case Operation::ROLw: return "ROL.w"; + case Operation::ROLl: return "ROL.l"; + case Operation::ROLm: return "ROL.w"; - case Operation::RORb: instruction = "ROR.b"; break; - case Operation::RORw: instruction = "ROR.w"; break; - case Operation::RORl: instruction = "ROR.l"; break; - case Operation::RORm: instruction = "ROR.w"; break; + case Operation::RORb: return "ROR.b"; + case Operation::RORw: return "ROR.w"; + case Operation::RORl: return "ROR.l"; + case Operation::RORm: return "ROR.w"; - case Operation::ROXLb: instruction = "ROXL.b"; break; - case Operation::ROXLw: instruction = "ROXL.w"; break; - case Operation::ROXLl: instruction = "ROXL.l"; break; - case Operation::ROXLm: instruction = "ROXL.w"; break; + case Operation::ROXLb: return "ROXL.b"; + case Operation::ROXLw: return "ROXL.w"; + case Operation::ROXLl: return "ROXL.l"; + case Operation::ROXLm: return "ROXL.w"; - case Operation::ROXRb: instruction = "ROXR.b"; break; - case Operation::ROXRw: instruction = "ROXR.w"; break; - case Operation::ROXRl: instruction = "ROXR.l"; break; - case Operation::ROXRm: instruction = "ROXR.w"; break; + case Operation::ROXRb: return "ROXR.b"; + case Operation::ROXRw: return "ROXR.w"; + case Operation::ROXRl: return "ROXR.l"; + case Operation::ROXRm: return "ROXR.w"; - case Operation::MOVEMtoMl: instruction = "MOVEM.l"; break; - case Operation::MOVEMtoMw: instruction = "MOVEM.w"; break; - case Operation::MOVEMtoRl: - instruction = "MOVEM.l"; - flip_operands = true; - break; - case Operation::MOVEMtoRw: - instruction = "MOVEM.w"; - flip_operands = true; - break; + case Operation::MOVEMtoMl: return "MOVEM.l"; + case Operation::MOVEMtoMw: return "MOVEM.w"; + case Operation::MOVEMtoRl: return "MOVEM.l"; + case Operation::MOVEMtoRw: return "MOVEM.w"; - case Operation::MOVEPl: instruction = "MOVEP.l"; break; - case Operation::MOVEPw: instruction = "MOVEP.w"; break; + case Operation::MOVEPl: return "MOVEP.l"; + case Operation::MOVEPw: return "MOVEP.w"; - case Operation::ANDb: instruction = "AND.b"; break; - case Operation::ANDw: instruction = "AND.w"; break; - case Operation::ANDl: instruction = "AND.l"; break; + case Operation::ANDb: return "AND.b"; + case Operation::ANDw: return "AND.w"; + case Operation::ANDl: return "AND.l"; - case Operation::EORb: instruction = "EOR.b"; break; - case Operation::EORw: instruction = "EOR.w"; break; - case Operation::EORl: instruction = "EOR.l"; break; + case Operation::EORb: return "EOR.b"; + case Operation::EORw: return "EOR.w"; + case Operation::EORl: return "EOR.l"; - case Operation::NOTb: instruction = "NOT.b"; break; - case Operation::NOTw: instruction = "NOT.w"; break; - case Operation::NOTl: instruction = "NOT.l"; break; + case Operation::NOTb: return "NOT.b"; + case Operation::NOTw: return "NOT.w"; + case Operation::NOTl: return "NOT.l"; - case Operation::ORb: instruction = "OR.b"; break; - case Operation::ORw: instruction = "OR.w"; break; - case Operation::ORl: instruction = "OR.l"; break; + case Operation::ORb: return "OR.b"; + case Operation::ORw: return "OR.w"; + case Operation::ORl: return "OR.l"; - case Operation::MULU: instruction = "MULU"; break; - case Operation::MULS: instruction = "MULS"; break; - case Operation::DIVU: instruction = "DIVU"; break; - case Operation::DIVS: instruction = "DIVS"; break; + case Operation::MULU: return "MULU"; + case Operation::MULS: return "MULS"; + case Operation::DIVU: return "DIVU"; + case Operation::DIVS: return "DIVS"; - case Operation::RTE: instruction = "RTE"; break; - case Operation::RTR: instruction = "RTR"; break; + case Operation::RTE: return "RTE"; + case Operation::RTR: return "RTR"; - case Operation::TRAP: instruction = "TRAP"; break; - case Operation::TRAPV: instruction = "TRAPV"; break; - case Operation::CHK: instruction = "CHK"; break; + case Operation::TRAP: return "TRAP"; + case Operation::TRAPV: return "TRAPV"; + case Operation::CHK: return "CHK"; - case Operation::EXG: instruction = "EXG"; break; - case Operation::SWAP: instruction = "SWAP"; break; + case Operation::EXG: return "EXG"; + case Operation::SWAP: return "SWAP"; - case Operation::TAS: instruction = "TAS"; break; + case Operation::TAS: return "TAS"; - case Operation::EXTbtow: instruction = "EXT.w"; break; - case Operation::EXTwtol: instruction = "EXT.l"; break; + case Operation::EXTbtow: return "EXT.w"; + case Operation::EXTwtol: return "EXT.l"; - case Operation::LINKw: instruction = "LINK"; break; - case Operation::UNLINK: instruction = "UNLINK"; break; + case Operation::LINKw: return "LINK"; + case Operation::UNLINK: return "UNLINK"; - case Operation::STOP: instruction = "STOP"; break; - case Operation::RESET: instruction = "RESET"; break; + case Operation::STOP: return "STOP"; + case Operation::RESET: return "RESET"; default: assert(false); } +} + +} + +const char *InstructionSet::M68k::to_string(Operation operation) { + return _to_string(operation, false); +} + +std::string Preinstruction::to_string(int opcode) const { + if(operation == Operation::Undefined) return "None"; + + const char *const instruction = _to_string(operation, mode<0>() == AddressingMode::Quick); + const bool flip_operands = (operation == Operation::MOVEMtoRl) || (operation == Operation::MOVEMtoRw); const std::string operand1 = operand_description(0 ^ int(flip_operands), opcode); const std::string operand2 = operand_description(1 ^ int(flip_operands), opcode); diff --git a/InstructionSets/M68k/Instruction.hpp b/InstructionSets/M68k/Instruction.hpp index 75c55ed29..f84c98666 100644 --- a/InstructionSets/M68k/Instruction.hpp +++ b/InstructionSets/M68k/Instruction.hpp @@ -104,6 +104,8 @@ enum class Operation: uint8_t { Max = RESET }; +const char *to_string(Operation op); + template constexpr bool requires_supervisor(Operation op) { switch(op) { From b6da1019bd5f7365206f44c88e92dfd346b8a7fe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2022 21:52:48 -0400 Subject: [PATCH 04/12] Bucket tests by operation, aim for ~1,000,000 total. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 3598a2410..5bfd534dd 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -14,8 +14,8 @@ #include #include +#include #include -#include namespace { @@ -257,9 +257,9 @@ void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::v // Compute RAM from transactions; if this is the initial state then RAM should // be everything that was subject to a read which had not previously been // subject to a write. Otherwise it can just be everything. - std::map ram; + std::unordered_map ram; if(is_initial) { - std::set written_addresses; + std::unordered_set written_addresses; for(const auto &transaction: transactions) { switch(transaction.data_strobes) { @@ -392,14 +392,12 @@ void print_transactions(FILE *target, const std::vector &transactio - (void)testGenerate { srand(68000); - - NSString *const tempDir = NSTemporaryDirectory(); - NSLog(@"Outputting to %@", tempDir); - + InstructionSet::M68k::Predecoder decoder; RandomStore random_store; auto tester = std::make_unique>(random_store, 0x02); - InstructionSet::M68k::Predecoder decoder; + // Bucket opcodes by operation. + std::unordered_map> opcodesByOperation; for(int c = 0; c < 65536; c++) { // Test only defined opcodes that aren't STOP (which will never teminate). const auto instruction = decoder.decode(uint16_t(c)); @@ -410,20 +408,37 @@ void print_transactions(FILE *target, const std::vector &transactio continue; } - NSString *const targetName = [NSString stringWithFormat:@"%@%04x.json", tempDir, c]; + const auto operation = to_string(instruction.operation); + opcodesByOperation[operation].push_back(c); + } + NSLog(@"Generating for %lu operations", opcodesByOperation.size()); + + // Find somewhere to write to. + NSString *const tempDir = NSTemporaryDirectory(); + NSLog(@"Outputting to %@", tempDir); + + // Aim to get at least 1,000,000 tests total. + const auto testsPerOperation = int((1'000'000 + (opcodesByOperation.size() - 1)) / opcodesByOperation.size()); + + // Generate by operation. + for(const auto &pair: opcodesByOperation) { + NSString *const targetName = [NSString stringWithFormat:@"%@%s.json", tempDir, pair.first]; FILE *const target = fopen(targetName.UTF8String, "wt"); bool is_first_test = true; fprintf(target, "["); - // Test each 10000 times. - for(int test = 0; test < 10000; test++) { + // Test each for the selected number of iterations. + for(int test = 0; test < testsPerOperation; test++) { if(!is_first_test) fprintf(target, ",\n"); is_first_test = false; // Establish with certainty the initial memory state. random_store.clear(); - tester->reset_with_opcode(c); + + const auto opcodeIndex = int(rand() * pair.second.size() / RAND_MAX); + const uint16_t opcode = pair.second[opcodeIndex]; + tester->reset_with_opcode(opcode); // Generate a random initial register state. auto initialState = tester->processor.get_state(); @@ -451,7 +466,7 @@ void print_transactions(FILE *target, const std::vector &transactio const auto finalState = tester->processor.get_state(); // Output initial state. - fprintf(target, "{ \"name\": \"%04x [%s] %d\", ", c, instruction.to_string().c_str(), test + 1); + fprintf(target, "{ \"name\": \"%04x [%s] %d\", ", opcode, decoder.decode(opcode).to_string().c_str(), test + 1); fprintf(target, "\"initial\": {"); print_state(target, initialState, tester->bus_handler.transactions, true); From 93c1f7fc905b420061674ac222b7d23974a61927 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2022 22:00:04 -0400 Subject: [PATCH 05/12] Include prefetch in 68000 state. --- OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm | 2 ++ Processors/68000Mk2/68000Mk2.hpp | 1 + .../68000Mk2/Implementation/68000Mk2Implementation.hpp | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 5bfd534dd..2d8fc46d0 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -252,6 +252,8 @@ void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::v fprintf(target, "\"SR\": %u, ", state.registers.status); fprintf(target, "\"PC\": %u, ", state.registers.program_counter - 4); + fprintf(target, "\"prefetch\": [%u, %u], ", state.prefetch[0], state.prefetch[1]); + fprintf(target, "\"ram\": ["); // Compute RAM from transactions; if this is the initial state then RAM should diff --git a/Processors/68000Mk2/68000Mk2.hpp b/Processors/68000Mk2/68000Mk2.hpp index 00269f06d..be908db02 100644 --- a/Processors/68000Mk2/68000Mk2.hpp +++ b/Processors/68000Mk2/68000Mk2.hpp @@ -354,6 +354,7 @@ class BusHandler { }; struct State { + uint16_t prefetch[2]; InstructionSet::M68k::RegisterSet registers; }; diff --git a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp index f6d15c328..f03fe23fe 100644 --- a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp +++ b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp @@ -3025,6 +3025,9 @@ CPU::MC68000Mk2::State Processor From 0fe94b2e6df3102ad59a23ca0797b6680725fceb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2022 22:26:30 -0400 Subject: [PATCH 06/12] Capture ::SameAddress versus ::NewAddress, for TAS recognition. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 2d8fc46d0..6938d7d25 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -57,16 +57,18 @@ struct Transaction { uint32_t address = 0; uint16_t value = 0; bool address_strobe = false; + bool same_address = false; bool read = false; int data_strobes = 0; - bool operator !=(const Transaction &rhs) const { + 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; + if(same_address != rhs.same_address) return true; return false; } @@ -109,6 +111,7 @@ struct BusHandler { transaction.function_code |= (cycle.operation & Microcycle::IsData) ? 0x1 : 0x2; } transaction.address_strobe = cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress); + transaction.same_address = cycle.operation & Microcycle::SameAddress; transaction.data_strobes = cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord); if(cycle.address) transaction.address = *cycle.address & 0xffff'ff; transaction.timestamp = time; @@ -338,12 +341,23 @@ void print_transactions(FILE *target, const std::vector &transactio is_access = false; } else { assert(!iterator->data_strobes); - if(next->read) { - fprintf(target, "\"nr\", "); - } else { - fprintf(target, "\"nw\", "); + + // Check how many transactions this address persists for; + // that'll allow a TAS to be recognised here. + while(next->same_address && next != transactions.end()) { + ++next; + } + --next; + + if(next == iterator + 1) { + if(next->read) { + fprintf(target, "\"r\", "); + } else { + fprintf(target, "\"w\", "); + } + } else { + fprintf(target, "\"t\", "); } - ++next; } HalfCycles length; if(next == transactions.end()) { @@ -351,33 +365,21 @@ void print_transactions(FILE *target, const std::vector &transactio } else { length = next->timestamp - iterator->timestamp; } - fprintf(target, "%d, ", length.as() >> 1); - - fprintf(target, "%d, ", iterator->function_code); + fprintf(target, "%d", length.as() >> 1); if(is_access) { - --next; + fprintf(target, ", %d, ", iterator->function_code); fprintf(target, "%d, ", iterator->address & 0xff'ffff); switch(next->data_strobes) { default: assert(false); - case 1: { - if(next->address & 1) { - fprintf(target, "\"-L\", "); - } else { - fprintf(target, "\"U-\", "); - } - } break; - case 2: fprintf(target, "\"UL\", "); break; + case 1: fprintf(target, "\".b\", "); break; + case 2: fprintf(target, "\".w\", "); break; break; } fprintf(target, "%d", next->value); ++next; - } else { - fprintf(target, "null, "); - fprintf(target, "\"--\", "); - fprintf(target, "null"); } fprintf(target, "]"); @@ -413,7 +415,6 @@ void print_transactions(FILE *target, const std::vector &transactio const auto operation = to_string(instruction.operation); opcodesByOperation[operation].push_back(c); } - NSLog(@"Generating for %lu operations", opcodesByOperation.size()); // Find somewhere to write to. NSString *const tempDir = NSTemporaryDirectory(); @@ -423,7 +424,9 @@ void print_transactions(FILE *target, const std::vector &transactio const auto testsPerOperation = int((1'000'000 + (opcodesByOperation.size() - 1)) / opcodesByOperation.size()); // Generate by operation. + NSLog(@"Generating %d tests each for %lu operations", testsPerOperation, opcodesByOperation.size()); for(const auto &pair: opcodesByOperation) { + NSLog(@"Generating %s", pair.first); NSString *const targetName = [NSString stringWithFormat:@"%@%s.json", tempDir, pair.first]; FILE *const target = fopen(targetName.UTF8String, "wt"); From 1a7509e86016479192ed9dfb5a061de6fac58bd3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2022 22:26:45 -0400 Subject: [PATCH 07/12] Properly announce ::SameAddress. --- Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp index f03fe23fe..7c1585b62 100644 --- a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp +++ b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp @@ -341,7 +341,7 @@ void Processor Date: Tue, 6 Sep 2022 11:26:16 -0400 Subject: [PATCH 08/12] Provide a route to operation that factors in addressing mode. --- InstructionSets/M68k/Instruction.cpp | 4 ++++ InstructionSets/M68k/Instruction.hpp | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/InstructionSets/M68k/Instruction.cpp b/InstructionSets/M68k/Instruction.cpp index 16420bb8d..e41d0b118 100644 --- a/InstructionSets/M68k/Instruction.cpp +++ b/InstructionSets/M68k/Instruction.cpp @@ -269,3 +269,7 @@ std::string Preinstruction::to_string(int opcode) const { return result; } + +const char *Preinstruction::operation_string() const { + return _to_string(operation, mode<0>() == AddressingMode::Quick); +} diff --git a/InstructionSets/M68k/Instruction.hpp b/InstructionSets/M68k/Instruction.hpp index f84c98666..de3871762 100644 --- a/InstructionSets/M68k/Instruction.hpp +++ b/InstructionSets/M68k/Instruction.hpp @@ -348,6 +348,12 @@ class Preinstruction { /// is supplied then any quick fields in this instruction will be decoded; /// otherwise they'll be printed as just 'Q'. std::string to_string(int opcode = -1) const; + + /// Produces a slightly-more-idiomatic version of the operation name than + /// a direct to_string(instruction.operation) would, given that this decoder + /// sometimes aliases operations, disambiguating based on addressing mode + /// (e.g. MOVEQ is MOVE.l with the Q addressing mode). + const char *operation_string() const; }; } From 2c44ddfa95aaf06bfe09d971f8ea1cf43b084050 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2022 11:26:38 -0400 Subject: [PATCH 09/12] Better bucket, and attempt to cover exceptions. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 6938d7d25..b08503cab 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -243,17 +243,17 @@ template struct Tester { void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::vector &transactions, bool is_initial) { for(int c = 0; c < 8; c++) { - fprintf(target, "\"D%d\": %u, ", c, state.registers.data[c]); + fprintf(target, "\"d%d\": %u, ", c, state.registers.data[c]); } for(int c = 0; c < 7; c++) { - fprintf(target, "\"A%d\": %u, ", c, state.registers.address[c]); + fprintf(target, "\"a%d\": %u, ", c, state.registers.address[c]); } - fprintf(target, "\"USP\": %u, ", state.registers.user_stack_pointer); - fprintf(target, "\"SSP\": %u, ", state.registers.supervisor_stack_pointer); - fprintf(target, "\"SR\": %u, ", state.registers.status); - fprintf(target, "\"PC\": %u, ", state.registers.program_counter - 4); + fprintf(target, "\"usp\": %u, ", state.registers.user_stack_pointer); + fprintf(target, "\"ssp\": %u, ", state.registers.supervisor_stack_pointer); + fprintf(target, "\"sr\": %u, ", state.registers.status); + fprintf(target, "\"pc\": %u, ", state.registers.program_counter - 4); fprintf(target, "\"prefetch\": [%u, %u], ", state.prefetch[0], state.prefetch[1]); @@ -358,6 +358,9 @@ void print_transactions(FILE *target, const std::vector &transactio } else { fprintf(target, "\"t\", "); } + + // Include next in the calculation of time below. + ++next; } HalfCycles length; if(next == transactions.end()) { @@ -368,6 +371,10 @@ void print_transactions(FILE *target, const std::vector &transactio fprintf(target, "%d", length.as() >> 1); if(is_access) { + // Undo the 'move to one after' step that allowed next to be included + // in this transaction's cycle count. + --next; + fprintf(target, ", %d, ", iterator->function_code); fprintf(target, "%d, ", iterator->address & 0xff'ffff); @@ -412,7 +419,7 @@ void print_transactions(FILE *target, const std::vector &transactio continue; } - const auto operation = to_string(instruction.operation); + const auto operation = instruction.operation_string(); opcodesByOperation[operation].push_back(c); } @@ -430,6 +437,7 @@ void print_transactions(FILE *target, const std::vector &transactio NSString *const targetName = [NSString stringWithFormat:@"%@%s.json", tempDir, pair.first]; FILE *const target = fopen(targetName.UTF8String, "wt"); + const bool force_addresses_even = decoder.decode(pair.second[0]).operation == InstructionSet::M68k::Operation::UNLINK; bool is_first_test = true; fprintf(target, "["); @@ -448,24 +456,34 @@ void print_transactions(FILE *target, const std::vector &transactio // Generate a random initial register state. auto initialState = tester->processor.get_state(); + // Require address pointers to be even 99% of the time, or always for UNLINK. + const bool addresses_are_even = (rand() >= int(float(RAND_MAX) * 0.99f)) || force_addresses_even; for(int c = 0; c < 8; c++) { initialState.registers.data[c] = rand() ^ (rand() << 1); - if(c != 7) initialState.registers.address[c] = rand() << 1; + if(c != 7) { + initialState.registers.address[c] = rand() ^ (rand() << 1); + if(addresses_are_even) initialState.registers.address[c] &= ~1; + } } - // Fully to paper over the two 68000s' different ways of doing a faked - // reset, pick a random status such that: + + // Pick a random status such that: // - // (i) supervisor mode is active; + // (i) supervisor mode is active 99% of the time; // (ii) trace is inactive; and // (iii) interrupt level is 7. - initialState.registers.status = (rand() | (1 << 13) | (7 << 8)) & ~(1 << 15); + const bool is_supervisor = rand() >= int(float(RAND_MAX) * 0.99f); + initialState.registers.status = (rand() | (int(is_supervisor) << 13) | (7 << 8)) & ~(1 << 15); initialState.registers.user_stack_pointer = rand() << 1; - initialState.registers.supervisor_stack_pointer = 0x800; + initialState.registers.supervisor_stack_pointer = rand() << 1; // Set state. tester->processor.set_state(initialState); - // Run a single instruction. + // Run for zero instructions to grab the real initial state (i.e. valid prefetch, ssp, etc). + tester->run_instructions(0); + auto populatedInitialState = tester->processor.get_state(); + + // Run for another instruction to do the actual work. tester->run_instructions(1); const auto finalState = tester->processor.get_state(); @@ -473,7 +491,7 @@ void print_transactions(FILE *target, const std::vector &transactio // Output initial state. fprintf(target, "{ \"name\": \"%04x [%s] %d\", ", opcode, decoder.decode(opcode).to_string().c_str(), test + 1); fprintf(target, "\"initial\": {"); - print_state(target, initialState, tester->bus_handler.transactions, true); + print_state(target, populatedInitialState, tester->bus_handler.transactions, true); // Output final state. fprintf(target, "}, \"final\": {"); From b848b1389a07cd9eff4b0feec84edf083bb285bf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2022 15:08:35 -0400 Subject: [PATCH 10/12] Include gaps in captured transactions, better collect final RAM state. --- .../Mac/Clock SignalTests/68000OldVsNew.mm | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index b08503cab..1724c114a 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -163,7 +163,7 @@ struct BusHandler { } // Push back only if interesting. - if(transaction.address_strobe || transaction.data_strobes || transaction.function_code == 7) { + if(capture_all_transactions || transaction.address_strobe || transaction.data_strobes || transaction.function_code == 7) { if(transaction_delay) { --transaction_delay; @@ -181,6 +181,7 @@ struct BusHandler { int transaction_delay; int instructions; + bool capture_all_transactions = false; HalfCycles time; std::vector transactions; @@ -298,21 +299,14 @@ void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::v switch(transaction.data_strobes) { default: continue; case 1: - if(ram.find(transaction.address) == ram.end()) { - ram[transaction.address] = transaction.value; - } + ram[transaction.address] = transaction.value; break; case 2: - if(ram.find(transaction.address) == ram.end()) { - ram[transaction.address] = uint8_t(transaction.value >> 8); - } - if(ram.find(transaction.address+1) == ram.end()) { - ram[transaction.address+1] = uint8_t(transaction.value); - } + ram[transaction.address] = uint8_t(transaction.value >> 8); + ram[transaction.address+1] = uint8_t(transaction.value); break; } } - } bool is_first = true; @@ -406,6 +400,7 @@ void print_transactions(FILE *target, const std::vector &transactio InstructionSet::M68k::Predecoder decoder; RandomStore random_store; auto tester = std::make_unique>(random_store, 0x02); + tester->bus_handler.capture_all_transactions = true; // Bucket opcodes by operation. std::unordered_map> opcodesByOperation; @@ -480,8 +475,11 @@ void print_transactions(FILE *target, const std::vector &transactio tester->processor.set_state(initialState); // Run for zero instructions to grab the real initial state (i.e. valid prefetch, ssp, etc). + // Then make sure no transactions or time carry over into the actual instruction. tester->run_instructions(0); auto populatedInitialState = tester->processor.get_state(); + tester->bus_handler.transactions.clear(); + tester->bus_handler.time = HalfCycles(0); // Run for another instruction to do the actual work. tester->run_instructions(1); From de8ce3380c2a41d29d9bc158d5508eea56016dfb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2022 20:49:45 -0400 Subject: [PATCH 11/12] Record only 8 bits for byte accesses. --- OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 1724c114a..6802f971d 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -374,11 +374,9 @@ void print_transactions(FILE *target, const std::vector &transactio switch(next->data_strobes) { default: assert(false); - case 1: fprintf(target, "\".b\", "); break; - case 2: fprintf(target, "\".w\", "); break; - break; + case 1: fprintf(target, "\".b\", %d", next->value & 0xff); break; + case 2: fprintf(target, "\".w\", %d", next->value); break; } - fprintf(target, "%d", next->value); ++next; } From dad1d7744e1a50d447e5e0fe32c01633c7e0c9af Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2022 16:41:10 -0400 Subject: [PATCH 12/12] Disable test generation. --- OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm index 6802f971d..6d0655f2e 100644 --- a/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm +++ b/OSBindings/Mac/Clock SignalTests/68000OldVsNew.mm @@ -393,7 +393,8 @@ void print_transactions(FILE *target, const std::vector &transactio @implementation M68000OldVsNewTests -- (void)testGenerate { +//- (void)testGenerate { +- (void)generate { srand(68000); InstructionSet::M68k::Predecoder decoder; RandomStore random_store; @@ -555,7 +556,7 @@ void print_transactions(FILE *target, const std::vector &transactio int testsRun = 0; std::set failing_operations; for(int c = 0; c < 65536; c++) { -// printf("%04x\n", 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));