From 1003e70b5eb321b24e3ac091b8a3ecaba805625a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 14 Apr 2019 20:02:18 -0400 Subject: [PATCH] Implements MOVEM to R. --- .../Implementation/68000Implementation.hpp | 127 ++++++++++++++++++ .../68000/Implementation/68000Storage.cpp | 10 +- .../68000/Implementation/68000Storage.hpp | 11 +- 3 files changed, 142 insertions(+), 6 deletions(-) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 7741c2f67..ed1346d0b 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -350,6 +350,110 @@ template void Processor: set_ccr(active_program_->source->full); break; + /* + MOVEM: multi-word moves. + */ + +#define setup_movem(words_per_reg) \ + /* Count the number of long words to move. */ \ + size_t total_to_move = 0; \ + auto mask = next_word_; \ + while(mask) { \ + total_to_move += mask&1; \ + mask >>= 1; \ + } \ + \ + /* Twice that many words plus one will need to be moved */ \ + bus_program = movem_reads_steps_ + (64 - total_to_move*words_per_reg)*2; \ + \ + /* Fill in the proper addresses and targets. */ \ + const auto mode = (decoded_instruction_ >> 3) & 7; \ + uint32_t start_address; \ + if(mode <= 4) { \ + start_address = active_program_->source_address->full; \ + } else { \ + start_address = effective_address_[1].full; \ + } \ + \ + auto step = bus_program; \ + uint32_t *address_storage = movem_addresses_; \ + mask = next_word_; \ + int offset = 0; + + case Operation::MOVEMtoRl: { + setup_movem(2); + + // Everything for move to registers is based on an incrementing + // address; per M68000PRM: + // + // "[If using the postincrement addressing mode then] the incremented address + // register contains the address of the last operand loaded plus the operand length. + // If the addressing register is also loaded from memory, the memory value is ignored + // and the register is written with the postincre- mented effective address." + while(mask) { + if(mask&1) { + address_storage[0] = start_address; + address_storage[1] = start_address + 2; + + step[0].microcycle.address = step[1].microcycle.address = address_storage; + step[2].microcycle.address = step[3].microcycle.address = address_storage + 1; + + const auto target = (offset > 7) ? &address_[offset&7] : &data_[offset]; + step[0].microcycle.value = step[1].microcycle.value = &target->halves.high; + step[2].microcycle.value = step[3].microcycle.value = &target->halves.low; + + start_address += 4; + address_storage += 2; + step += 4; + } + mask >>= 1; + ++offset; + } + + // MOVEM to R always reads one word too many. + address_storage[0] = start_address; + step[0].microcycle.address = step[1].microcycle.address = address_storage; + step[0].microcycle.value = step[1].microcycle.value = &movem_spare_value_; + movem_final_address_ = start_address; + } break; + + case Operation::MOVEMtoRw: { + setup_movem(1); + + while(mask) { + if(mask&1) { + address_storage[0] = start_address; + + step[0].microcycle.address = step[1].microcycle.address = address_storage; + + const auto target = (offset > 7) ? &address_[offset&7] : &data_[offset]; + step[0].microcycle.value = step[1].microcycle.value = &target->halves.low; + + start_address += 2; + ++ address_storage; + step += 2; + } + mask >>= 1; + ++offset; + } + + // MOVEM to R always reads one word too many. + address_storage[0] = start_address; + step[0].microcycle.address = step[1].microcycle.address = address_storage; + step[0].microcycle.value = step[1].microcycle.value = &movem_spare_value_; + movem_final_address_ = start_address; + } break; + + case Operation::MOVEMtoMl: { + setup_movem(2); + } break; + + case Operation::MOVEMtoMw: { + setup_movem(1); + } break; + +#undef setup_movem + /* NEGs: negatives the destination, setting the zero, negative, overflow and carry flags appropriate, and extend. @@ -626,6 +730,29 @@ template void Processor: #undef add_overflow break; + case int(MicroOp::Action::MOVEMtoRComplete): { + // If this was a word-sized move, perform sign extension. + if(active_program_->operation == Operation::MOVEMtoRw) { + auto mask = next_word_; + int offset = 0; + while(mask) { + if(mask&1) { + const auto target = (offset > 7) ? &address_[offset&7] : &data_[offset]; + target->halves.high.full = (target->halves.low.full & 0x8000) ? 0xffff : 0x0000; + } + mask >>= 1; + ++offset; + } + } + + // If the post-increment mode was used, overwrite the source register. + const auto mode = (decoded_instruction_ >> 3) & 7; + if(mode == 3) { + const auto reg = decoded_instruction_ & 7; + address_[reg] = movem_final_address_; + } + } break; + case int(MicroOp::Action::CopyNextWord): next_word_ = prefetch_queue_.halves.low.full; break; diff --git a/Processors/68000/Implementation/68000Storage.cpp b/Processors/68000/Implementation/68000Storage.cpp index 81e237e6e..7cb61af99 100644 --- a/Processors/68000/Implementation/68000Storage.cpp +++ b/Processors/68000/Implementation/68000Storage.cpp @@ -1573,7 +1573,7 @@ struct ProcessorStorageConstructor { op(Action::PerformOperation); // A final program fetch will cue up the next instruction. - op(Action::None, seq("np")); + op(is_to_m ? Action::MOVEMtoMComplete : Action::MOVEMtoRComplete, seq("np")); } break; // Decodes the format used by most MOVEs and all MOVEAs. @@ -2206,15 +2206,17 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() { 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"); - // The reads steps needs to be 33 neatly assembled reads; the writes just the 32. + // The reads steps needs to be 32 long-word reads plus an overflow word; the writes just the long words. // Addresses and data sources/targets will be filled in at runtime, so anything will do here. std::string movem_reads_pattern, movem_writes_pattern; std::vector addresses; - for(auto c = 0; c < 33; ++c) { + for(auto c = 0; c < 64; ++c) { movem_reads_pattern += "nr "; - if(c != 32) movem_writes_pattern += "nw "; + movem_writes_pattern += "nw "; addresses.push_back(nullptr); } + movem_reads_pattern += "nr"; + addresses.push_back(nullptr); const size_t movem_reads_offset = constructor.assemble_program(movem_reads_pattern, addresses); const size_t movem_writes_offset = constructor.assemble_program(movem_writes_pattern, addresses); diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index b1ada2f2f..5bdd668ee 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -211,6 +211,12 @@ class ProcessorStorage { /// Copies the low part of the prefetch queue into next_word_. CopyNextWord, + + /// Performs write-back of post-increment address and/or sign extensions as necessary. + MOVEMtoRComplete, + + /// Performs write-back of pre-decrement address. + MOVEMtoMComplete, }; static const int SourceMask = 1 << 30; static const int DestinationMask = 1 << 29; @@ -301,8 +307,9 @@ class ProcessorStorage { void set_is_supervisor(bool); // Transient storage for MOVEM. - uint32_t movem_addresses_[33]; - RegisterPair32 movem_spare_value_; + uint32_t movem_addresses_[65]; + RegisterPair16 movem_spare_value_; + uint32_t movem_final_address_; /*! Evaluates the conditional described by @c code and returns @c true or @c false to