mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Implements DBcc.
This commit is contained in:
parent
0af57806da
commit
06a2f59bd0
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user