mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-18 04:28:57 +00:00
Implements MOVEM to R.
This commit is contained in:
parent
d70229201d
commit
1003e70b5e
@ -350,6 +350,110 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
|
|||||||
set_ccr(active_program_->source->full);
|
set_ccr(active_program_->source->full);
|
||||||
break;
|
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,
|
NEGs: negatives the destination, setting the zero,
|
||||||
negative, overflow and carry flags appropriate, and extend.
|
negative, overflow and carry flags appropriate, and extend.
|
||||||
@ -626,6 +730,29 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
|
|||||||
#undef add_overflow
|
#undef add_overflow
|
||||||
break;
|
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):
|
case int(MicroOp::Action::CopyNextWord):
|
||||||
next_word_ = prefetch_queue_.halves.low.full;
|
next_word_ = prefetch_queue_.halves.low.full;
|
||||||
break;
|
break;
|
||||||
|
@ -1573,7 +1573,7 @@ struct ProcessorStorageConstructor {
|
|||||||
op(Action::PerformOperation);
|
op(Action::PerformOperation);
|
||||||
|
|
||||||
// A final program fetch will cue up the next instruction.
|
// 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;
|
} break;
|
||||||
|
|
||||||
// Decodes the format used by most MOVEs and all MOVEAs.
|
// 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_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");
|
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.
|
// 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::string movem_reads_pattern, movem_writes_pattern;
|
||||||
std::vector<uint32_t *> addresses;
|
std::vector<uint32_t *> addresses;
|
||||||
for(auto c = 0; c < 33; ++c) {
|
for(auto c = 0; c < 64; ++c) {
|
||||||
movem_reads_pattern += "nr ";
|
movem_reads_pattern += "nr ";
|
||||||
if(c != 32) movem_writes_pattern += "nw ";
|
movem_writes_pattern += "nw ";
|
||||||
addresses.push_back(nullptr);
|
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_reads_offset = constructor.assemble_program(movem_reads_pattern, addresses);
|
||||||
const size_t movem_writes_offset = constructor.assemble_program(movem_writes_pattern, addresses);
|
const size_t movem_writes_offset = constructor.assemble_program(movem_writes_pattern, addresses);
|
||||||
|
|
||||||
|
@ -211,6 +211,12 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
/// Copies the low part of the prefetch queue into next_word_.
|
/// Copies the low part of the prefetch queue into next_word_.
|
||||||
CopyNextWord,
|
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 SourceMask = 1 << 30;
|
||||||
static const int DestinationMask = 1 << 29;
|
static const int DestinationMask = 1 << 29;
|
||||||
@ -301,8 +307,9 @@ class ProcessorStorage {
|
|||||||
void set_is_supervisor(bool);
|
void set_is_supervisor(bool);
|
||||||
|
|
||||||
// Transient storage for MOVEM.
|
// Transient storage for MOVEM.
|
||||||
uint32_t movem_addresses_[33];
|
uint32_t movem_addresses_[65];
|
||||||
RegisterPair32 movem_spare_value_;
|
RegisterPair16 movem_spare_value_;
|
||||||
|
uint32_t movem_final_address_;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Evaluates the conditional described by @c code and returns @c true or @c false to
|
Evaluates the conditional described by @c code and returns @c true or @c false to
|
||||||
|
Loading…
Reference in New Issue
Block a user