diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index f9a785306..573999fbc 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -90,6 +90,73 @@ template void Processor: active_program_->destination->halves.low.halves.low = uint8_t(result); } break; + // BRA: alters the program counter, exclusively via the prefetch queue. + case Operation::BRA: { + const int8_t byte_offset = int8_t(prefetch_queue_.halves.high.halves.low); + + // A non-zero offset byte branches by just that amount; otherwise use the word + // after as an offset. In both cases, treat as signed. + if(byte_offset) { + program_counter_.full = (program_counter_.full + byte_offset) - 2; + } else { + program_counter_.full += int16_t(prefetch_queue_.halves.low.full); + } + } break; + + // Bcc: evaluates the relevant condition and displacement size and then: + // if condition is false, schedules bus operations to get past this instruction; + // otherwise applies the offset and schedules bus operations to refill the prefetch queue. + case Operation::Bcc: { + // Grab the 8-bit offset. + 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 + } + + // Schedule something appropriate, by rewriting the program for this instruction temporarily. + if(should_branch) { + if(byte_offset) { + program_counter_.full = (program_counter_.full + byte_offset) - 2; // - 2 because this should be calculated from the high word of the prefetch. + } else { + program_counter_.full += int16_t(prefetch_queue_.halves.low.full); + } + active_micro_op_->bus_program = branch_taken_bus_steps_; + } else { + if(byte_offset) { + active_micro_op_->bus_program = branch_byte_not_taken_bus_steps_; + } else { + active_micro_op_->bus_program = branch_word_not_taken_bus_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. diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 8bf0c6dee..8942e3550 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -238,6 +238,8 @@ struct ProcessorStorageConstructor { MOVEtoSR, // six lowest bits are [mode, register], decoding to MOVE SR CMPI, // eight lowest bits are [size, mode, register], decoding to CMPI + BRA, // eight lowest bits are ignored, and an 'n np np' is scheduled + Bcc, // twelve lowest bits are ignored, only a PerformAction is scheduled }; using Operation = ProcessorStorage::Operation; @@ -280,9 +282,12 @@ struct ProcessorStorageConstructor { {0xffc0, 0x46c0, Operation::MOVEtoSR, Decoder::MOVEtoSR}, // 6-19 (p473) - {0xffc0, 0x0c00, Operation::CMPb, Decoder::CMPI}, - {0xffc0, 0x0c40, Operation::CMPw, Decoder::CMPI}, - {0xffc0, 0x0c80, Operation::CMPl, Decoder::CMPI}, + {0xffc0, 0x0c00, Operation::CMPb, Decoder::CMPI}, // 4-79 (p183) + {0xffc0, 0x0c40, Operation::CMPw, Decoder::CMPI}, // 4-79 (p183) + {0xffc0, 0x0c80, Operation::CMPl, Decoder::CMPI}, // 4-79 (p183) + + {0xff00, 0x6000, Operation::BRA, Decoder::BRA}, // 4-55 (p159) + {0xf000, 0x6000, Operation::Bcc, Decoder::Bcc}, // 4-25 (p129) }; std::vector micro_op_pointers(65536, std::numeric_limits::max()); @@ -307,6 +312,18 @@ struct ProcessorStorageConstructor { const int source_mode = (instruction >> 3) & 7; switch(mapping.decoder) { + // This decoder actually decodes nothing; it just schedules a PerformOperation followed by an empty step. + case Decoder::Bcc: { + op(Action::PerformOperation); + op(); + } break; + + // A little artificial, there's nothing really to decode for BRA. + case Decoder::BRA: { + op(Action::PerformOperation, seq("n np np")); + op(); + } break; + // Decodes the format used by ABCD and SBCD. case Decoder::Decimal: { const int destination = (instruction >> 9) & 7; @@ -944,17 +961,23 @@ struct ProcessorStorageConstructor { CPU::MC68000::ProcessorStorage::ProcessorStorage() { ProcessorStorageConstructor constructor(*this); - // Create the exception programs. + // 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"); // Install operations. constructor.install_instructions(); - // Realise the exception programs as direct pointers. - reset_program_ = &all_bus_steps_[reset_offset]; + // 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]; // Set initial state. Largely TODO. - active_step_ = reset_program_; + active_step_ = reset_bus_steps_; effective_address_[0] = 0; is_supervisor_ = 1; } diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 1e370b965..58a81e6dd 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -50,7 +50,9 @@ class ProcessorStorage { MOVEtoSR, MOVEfromSR, - CMPb, CMPw, CMPl + CMPb, CMPw, CMPl, + + BRA, Bcc }; /*! @@ -232,8 +234,13 @@ class ProcessorStorage { // A lookup table from instructions to implementations. Program instructions[65536]; - // Special programs, for exception handlers. - BusStep *reset_program_; + // Special steps for exception handlers. + BusStep *reset_bus_steps_; + + // Special micro-op sequences for conditionals. + BusStep *branch_taken_bus_steps_; + BusStep *branch_byte_not_taken_bus_steps_; + BusStep *branch_word_not_taken_bus_steps_; // Current bus step pointer, and outer program pointer. Program *active_program_ = nullptr;