From 06a2f59bd0b1018e9d4e72970adccd56254a571d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 6 Apr 2019 23:21:01 -0400 Subject: [PATCH] Implements DBcc. --- .../Mac/Clock SignalTests/EmuTOSTests.mm | 2 +- .../Implementation/68000Implementation.hpp | 95 ++++++++----------- .../68000/Implementation/68000Storage.cpp | 22 +++++ .../68000/Implementation/68000Storage.hpp | 40 +++++++- 4 files changed, 100 insertions(+), 59 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm index 6fdf55898..7136ab7d4 100644 --- a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm +++ b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm @@ -115,7 +115,7 @@ class EmuTOS: public CPU::MC68000::BusHandler { - (void)testStartup { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. - _machine->run_for(HalfCycles(4000)); + _machine->run_for(HalfCycles(400000)); } @end diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 6b9a3fdc1..c15a8ea21 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -166,34 +166,7 @@ template void Processor: const int8_t byte_offset = int8_t(prefetch_queue_.halves.high.halves.low); // Test the conditional. - bool should_branch; - switch(prefetch_queue_.halves.high.halves.high & 0xf) { - default: - case 0x00: should_branch = true; break; // true - case 0x01: should_branch = false; break; // false - case 0x02: should_branch = zero_result_ && !carry_flag_; break; // high - case 0x03: should_branch = !zero_result_ || carry_flag_; break; // low or same - case 0x04: should_branch = !carry_flag_; break; // carry clear - case 0x05: should_branch = carry_flag_; break; // carry set - case 0x06: should_branch = zero_result_; break; // not equal - case 0x07: should_branch = !zero_result_; break; // equal - case 0x08: should_branch = !overflow_flag_; break; // overflow clear - case 0x09: should_branch = overflow_flag_; break; // overflow set - case 0x0a: should_branch = !negative_flag_; break; // positive - case 0x0b: should_branch = negative_flag_; break; // negative - case 0x0c: - should_branch = (negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_); - break; // greater than or equal - case 0x0d: - should_branch = (negative_flag_ || !overflow_flag_) && (!negative_flag_ || overflow_flag_); - break; // less than - case 0x0e: - should_branch = zero_result_ && ((negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_)); - break; // greater than - case 0x0f: - should_branch = (!zero_result_ || negative_flag_) && (!overflow_flag_ || !negative_flag_) && overflow_flag_; - break; // less than or equal - } + const bool should_branch = evaluate_condition(prefetch_queue_.halves.high.halves.high); // Schedule something appropriate, by rewriting the program for this instruction temporarily. if(should_branch) { @@ -213,6 +186,30 @@ template void Processor: } } break; + case Operation::DBcc: { + // Decide what sort of DBcc this is. + if(!evaluate_condition(prefetch_queue_.halves.high.halves.high)) { + -- active_program_->source->halves.low.full; + const auto target_program_counter = program_counter_.full + int16_t(prefetch_queue_.halves.low.full) - 2; + + if(active_program_->source->halves.low.full == 0xffff) { + // This DBcc will be ignored as the counter has underflowed. + // Schedule n np np np and continue. Assumed: the first np + // is from where the branch would have been if taken? + active_micro_op_->bus_program = dbcc_condition_false_no_branch_steps_; + dbcc_false_address_ = target_program_counter; + } else { + // Take the branch. Change PC and schedule n np np. + active_micro_op_->bus_program = dbcc_condition_false_branch_steps_; + program_counter_.full = target_program_counter; + } + } else { + // This DBcc will be ignored as the condition is true; + // perform nn np np and continue. + active_micro_op_->bus_program = dbcc_condition_true_steps_; + } + } break; + /* CMP.b, CMP.l and CMP.w: sets the condition flags (other than extend) based on a subtraction of the source from the destination; the result of the subtraction is not stored. @@ -412,35 +409,23 @@ template void Processor: overflow_flag_ = carry_flag_ = 0; break; - case int(MicroOp::Action::Decrement1): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full -= 1; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full -= 1; - break; + // Increments and decrements. +#define Adjust(op, quantity) \ + case int(op) | MicroOp::SourceMask: active_program_->source_address->full += quantity; break; \ + case int(op) | MicroOp::DestinationMask: active_program_->destination_address->full += quantity; break; \ + case int(op) | MicroOp::SourceMask | MicroOp::DestinationMask: \ + active_program_->destination_address->full += quantity; \ + active_program_->source_address->full += quantity; \ + break; - case int(MicroOp::Action::Decrement2): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full -= 2; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full -= 2; - break; + Adjust(MicroOp::Action::Decrement1, -1); + Adjust(MicroOp::Action::Decrement2, -2); + Adjust(MicroOp::Action::Decrement4, -4); + Adjust(MicroOp::Action::Increment1, 1); + Adjust(MicroOp::Action::Increment2, 2); + Adjust(MicroOp::Action::Increment4, 4); - case int(MicroOp::Action::Decrement4): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full -= 4; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full -= 4; - break; - - case int(MicroOp::Action::Increment1): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full += 1; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full += 1; - break; - - case int(MicroOp::Action::Increment2): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full += 2; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full += 2; - break; - - case int(MicroOp::Action::Increment4): - if(active_micro_op_->action & MicroOp::SourceMask) active_program_->source_address->full += 4; - if(active_micro_op_->action & MicroOp::DestinationMask) active_program_->destination_address->full += 4; - break; +#undef Adjust case int(MicroOp::Action::SignExtendWord): if(active_micro_op_->action & MicroOp::SourceMask) { diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index a95a98b37..0a6e6e5c2 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -269,6 +269,7 @@ struct ProcessorStorageConstructor { BTST, // bit 9,10,11 are register, six lowest bits are [mode, register], decoding to BTST BTSTIMM, // six lowest bits are [mode, register], decoding to BTST # CMP, + DBcc, // the low three bits nominate a register; everything else is decoded in real time }; using Operation = ProcessorStorage::Operation; @@ -340,6 +341,8 @@ struct ProcessorStorageConstructor { {0xf1c0, 0x0100, Operation::BTSTb, Decoder::BTST}, // 4-62 (p166) {0xffc0, 0x0800, Operation::BTSTb, Decoder::BTSTIMM}, // 4-63 (p167) + + {0xf0f8, 0x50c8, Operation::DBcc, Decoder::DBcc}, // 4-91 (p195) }; std::vector micro_op_pointers(65536, std::numeric_limits::max()); @@ -1044,6 +1047,15 @@ struct ProcessorStorageConstructor { } } break; + case Decoder::DBcc: { + storage_.instructions[instruction].source = &storage_.data_[ea_register]; + + // Jump straight into deciding what steps to take next, + // which will be selected dynamically. + op(Action::PerformOperation); + op(); + } break; + case Decoder::JMP: { storage_.instructions[instruction].source = &storage_.effective_address_[0]; const int mode = combined_mode(ea_mode, ea_register); @@ -1824,19 +1836,29 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { // Create the special programs. const size_t reset_offset = constructor.assemble_program("n n n n n nn nF nf nV nv np np"); + const size_t branch_taken_offset = constructor.assemble_program("n np np"); const size_t branch_byte_not_taken_offset = constructor.assemble_program("nn np"); const size_t branch_word_not_taken_offset = constructor.assemble_program("nn np np"); + const size_t dbcc_condition_true_offset = constructor.assemble_program("nn np np"); + const size_t dbcc_condition_false_no_branch_offset = constructor.assemble_program("n nr np np", { &dbcc_false_address_ }); + const size_t dbcc_condition_false_branch_offset = constructor.assemble_program("n np np"); + // Install operations. constructor.install_instructions(); // Realise the special programs as direct pointers. reset_bus_steps_ = &all_bus_steps_[reset_offset]; + branch_taken_bus_steps_ = &all_bus_steps_[branch_taken_offset]; branch_byte_not_taken_bus_steps_ = &all_bus_steps_[branch_byte_not_taken_offset]; branch_word_not_taken_bus_steps_ = &all_bus_steps_[branch_word_not_taken_offset]; + dbcc_condition_true_steps_ = &all_bus_steps_[dbcc_condition_true_offset]; + dbcc_condition_false_no_branch_steps_ = &all_bus_steps_[dbcc_condition_false_no_branch_offset]; + dbcc_condition_false_branch_steps_ = &all_bus_steps_[dbcc_condition_false_branch_offset]; + // Set initial state. Largely TODO. active_step_ = reset_bus_steps_; effective_address_[0] = 0; diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index ecefecb69..cc1ed2a4e 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -59,7 +59,9 @@ class ProcessorStorage { CMPb, CMPw, CMPl, BTSTb, BTSTl, - BRA, Bcc, JMP, + JMP, + BRA, Bcc, + DBcc, }; /*! @@ -187,7 +189,7 @@ class ProcessorStorage { AssembleLongWordAddressFromPrefetch, /// Copies the next two prefetch words into one of the bus_data_. - AssembleLongWordDataFromPrefetch + AssembleLongWordDataFromPrefetch, }; static const int SourceMask = 1 << 30; static const int DestinationMask = 1 << 29; @@ -251,11 +253,16 @@ class ProcessorStorage { // Special steps for exception handlers. BusStep *reset_bus_steps_; - // Special micro-op sequences for conditionals. + // Special micro-op sequences and storage for conditionals. BusStep *branch_taken_bus_steps_; BusStep *branch_byte_not_taken_bus_steps_; BusStep *branch_word_not_taken_bus_steps_; + uint32_t dbcc_false_address_; + BusStep *dbcc_condition_true_steps_; + BusStep *dbcc_condition_false_no_branch_steps_; + BusStep *dbcc_condition_false_branch_steps_; + // Current bus step pointer, and outer program pointer. Program *active_program_ = nullptr; MicroOp *active_micro_op_ = nullptr; @@ -267,6 +274,33 @@ class ProcessorStorage { /// Sets or clears the supervisor flag, ensuring the stack pointer is properly updated. void set_is_supervisor(bool); + inline bool evaluate_condition(uint8_t code) { + switch(code & 0xf) { + default: + case 0x00: return true; // true + case 0x01: return false; // false + case 0x02: return zero_result_ && !carry_flag_; // high + case 0x03: return !zero_result_ || carry_flag_; // low or same + case 0x04: return !carry_flag_; // carry clear + case 0x05: return carry_flag_; // carry set + case 0x06: return zero_result_; // not equal + case 0x07: return !zero_result_; // equal + case 0x08: return !overflow_flag_; // overflow clear + case 0x09: return overflow_flag_; // overflow set + case 0x0a: return !negative_flag_; // positive + case 0x0b: return negative_flag_; // negative + case 0x0c: // greater than or equal + return (negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_); + case 0x0d: // less than + return (negative_flag_ || !overflow_flag_) && (!negative_flag_ || overflow_flag_); + case 0x0e: // greater than + return zero_result_ && ((negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_)); + case 0x0f: // less than or equal + return (!zero_result_ || negative_flag_) && (!overflow_flag_ || !negative_flag_) && overflow_flag_; + } + } + + private: friend class ProcessorStorageConstructor; };