From 9a666fb8cc43bc8c3d2213ce5d95f8c4410e1470 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 24 Jun 2019 19:43:30 -0400 Subject: [PATCH] Imports NEG tests and fixes NEG.l Dn timing. --- .../Mac/Clock SignalTests/68000Tests.mm | 116 +++++++++++++++++- .../68000/Implementation/68000Storage.cpp | 5 +- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000Tests.mm b/OSBindings/Mac/Clock SignalTests/68000Tests.mm index 0216ae864..8a9c65c9c 100644 --- a/OSBindings/Mac/Clock SignalTests/68000Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000Tests.mm @@ -38,6 +38,10 @@ class RAM68000: public CPU::MC68000::BusHandler { void set_program(const std::vector &program) { memcpy(&ram_[0x1000 >> 1], program.data(), program.size() * sizeof(uint16_t)); + + // Add a NOP suffix, to avoid corrupting flags should the attempt to + // run for a certain number of instructions overrun. + ram_[(0x1000 >> 1) + program.size()] = 0x4e71; } void set_initial_stack_pointer(uint32_t sp) { @@ -50,14 +54,20 @@ class RAM68000: public CPU::MC68000::BusHandler { } void run_for_instructions(int count) { - instructions_remaining_ = count; - if(!has_run_) ++instructions_remaining_; + instructions_remaining_ = count + (has_run_ ? 0 : 1); + finish_reset_if_needed(); + while(instructions_remaining_) { run_for(HalfCycles(2)); } } void run_for(HalfCycles cycles) { + finish_reset_if_needed(); + m68000_.run_for(cycles); + } + + void finish_reset_if_needed() { // If the 68000 hasn't run yet, build in the necessary // cycles to finish the reset program, and set the stored state. if(!has_run_) { @@ -65,8 +75,6 @@ class RAM68000: public CPU::MC68000::BusHandler { m68000_.run_for(HalfCycles(76)); duration_ -= HalfCycles(76); } - - m68000_.run_for(cycles); } uint16_t *ram_at(uint32_t address) { @@ -2139,6 +2147,106 @@ class CPU::MC68000::ProcessorStorageTests { // XCTAssertEqual(46, _machine->get_cycle_count()); } +// MARK: NEG + +- (void)performNEGb:(uint32_t)value { + _machine->set_program({ + 0x4400 // NEG.b D0 + }); + auto state = _machine->get_processor_state(); + state.data[0] = value; + + _machine->set_processor_state(state); + _machine->run_for_instructions(1); + + XCTAssertEqual(4, _machine->get_cycle_count()); +} + +- (void)testNEGb_78 { + [self performNEGb:0x12345678]; + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(state.data[0], 0x12345688); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Carry | Flag::Extend | Flag::Negative); +} + +- (void)testNEGb_00 { + [self performNEGb:0x12345600]; + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(state.data[0], 0x12345600); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Zero); +} + +- (void)testNEGb_80 { + [self performNEGb:0x12345680]; + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(state.data[0], 0x12345680); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Negative | Flag::Overflow | Flag::Extend | Flag::Carry); +} + +- (void)testNEGw { + _machine->set_program({ + 0x4440 // NEG.w D0 + }); + auto state = _machine->get_processor_state(); + state.data[0] = 0x12348000; + + _machine->set_processor_state(state); + _machine->run_for_instructions(1); + + state = _machine->get_processor_state(); + XCTAssertEqual(4, _machine->get_cycle_count()); + XCTAssertEqual(state.data[0], 0x12348000); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Negative | Flag::Overflow | Flag::Extend | Flag::Carry); +} + +- (void)performNEGl:(uint32_t)value { + _machine->set_program({ + 0x4480 // NEG.l D0 + }); + auto state = _machine->get_processor_state(); + state.data[0] = value; + + _machine->set_processor_state(state); + _machine->run_for_instructions(1); + + XCTAssertEqual(6, _machine->get_cycle_count()); +} + +- (void)testNEGl_large { + [self performNEGl:0x12345678]; + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(state.data[0], 0xedcba988); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Carry | Flag::Extend | Flag::Negative); +} + +- (void)testNEGl_small { + [self performNEGl:0xffffffff]; + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(state.data[0], 0x1); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Carry | Flag::Extend); +} + +- (void)testNEGl_XXXl { + _machine->set_program({ + 0x44b9, 0x0000, 0x3000 // NEG.L ($3000).L + }); + *_machine->ram_at(0x3000) = 0xf001; + *_machine->ram_at(0x3002) = 0x2311; + + _machine->run_for_instructions(1); + + const auto state = _machine->get_processor_state(); + XCTAssertEqual(28, _machine->get_cycle_count()); + XCTAssertEqual(*_machine->ram_at(0x3000), 0x0ffe); + XCTAssertEqual(*_machine->ram_at(0x3002), 0xdcef); + XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Extend | Flag::Carry); +} + // MARK: NOP - (void)testNOP { diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index f7556c828..1f8041e47 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -1905,10 +1905,13 @@ struct ProcessorStorageConstructor { default: continue; case bw(Dn): // [CLR/NEG/NEGX/NOT].bw Dn - case l(Dn): // [CLR/NEG/NEGX/NOT].l Dn op(Action::PerformOperation, seq("np")); break; + case l(Dn): // [CLR/NEG/NEGX/NOT].l Dn + op(Action::PerformOperation, seq("np n")); + break; + case bw(Ind): // [CLR/NEG/NEGX/NOT].bw (An) case bw(PostInc): // [CLR/NEG/NEGX/NOT].bw (An)+ op(Action::None, seq("nrd", { a(ea_register) }, !is_byte_access));