1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Implements DBcc.

This commit is contained in:
Thomas Harte 2019-04-06 23:21:01 -04:00
parent 0af57806da
commit 06a2f59bd0
4 changed files with 100 additions and 59 deletions

View File

@ -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

View File

@ -166,34 +166,7 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
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 <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
}
} 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 <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
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) {

View File

@ -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<size_t> micro_op_pointers(65536, std::numeric_limits<size_t>::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;

View File

@ -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;
};