mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-27 06:35:04 +00:00
Attempts DIVU and DIVS.
Reportedly leaving 10965 operations now unimplemented.
This commit is contained in:
parent
796203859f
commit
e75b386f7d
@ -571,7 +571,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
case Operation::MULU: {
|
||||
active_program_->destination->full =
|
||||
active_program_->destination->halves.low.full * active_program_->source->halves.low.full;
|
||||
carry_flag_ = overflow_flag_ = 0;
|
||||
carry_flag_ = overflow_flag_ = 0; // TODO: "set if overflow".
|
||||
zero_result_ = active_program_->destination->full;
|
||||
negative_flag_ = zero_result_ & 0x80000000;
|
||||
|
||||
@ -590,7 +590,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
case Operation::MULS: {
|
||||
active_program_->destination->full =
|
||||
int16_t(active_program_->destination->halves.low.full) * int16_t(active_program_->source->halves.low.full);
|
||||
carry_flag_ = overflow_flag_ = 0;
|
||||
carry_flag_ = overflow_flag_ = 0; // TODO: "set if overflow".
|
||||
zero_result_ = active_program_->destination->full;
|
||||
negative_flag_ = zero_result_ & 0x80000000;
|
||||
|
||||
@ -608,6 +608,123 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
active_step_->microcycle.length = HalfCycles(4 * number_of_pairs + 38*2);
|
||||
} break;
|
||||
|
||||
/*
|
||||
Divisions.
|
||||
*/
|
||||
|
||||
case Operation::DIVU: {
|
||||
// An attempt to divide by zero schedules an exception.
|
||||
if(!active_program_->source->halves.low.full) {
|
||||
// TODO: schedule an exception.
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t dividend = active_program_->destination->full;
|
||||
uint32_t divisor = active_program_->source->halves.low.full;
|
||||
const auto quotient = dividend / divisor;
|
||||
|
||||
carry_flag_ = 0;
|
||||
|
||||
// If overflow would occur, appropriate flags are set and the result is not written back.
|
||||
if(quotient >= 65536) {
|
||||
overflow_flag_ = 1;
|
||||
// TODO: is what should happen to the other flags known?
|
||||
active_step_->microcycle.length = HalfCycles(3*2*2);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint16_t remainder = uint16_t(dividend % divisor);
|
||||
active_program_->destination->halves.high.full = remainder;
|
||||
active_program_->destination->halves.low.full = quotient;
|
||||
|
||||
overflow_flag_ = 0;
|
||||
zero_result_ = quotient;
|
||||
negative_flag_ = zero_result_ & 0x8000;
|
||||
|
||||
// Calculate cost; this is based on the flowchart in yacht.txt.
|
||||
// I could actually calculate the division result here, since this is
|
||||
// a classic divide algorithm, but would rather that errors produce
|
||||
// incorrect timing only, not incorrect timing plus incorrect results.
|
||||
int cycles_expended = 6; // Covers the nn n to get into the loop.
|
||||
|
||||
divisor <<= 16;
|
||||
for(int c = 0; c < 15; ++c) {
|
||||
if(dividend & 0x80000000) {
|
||||
dividend = (dividend << 1) - divisor;
|
||||
cycles_expended += 4; // Easy; just the fixed nn iteration cost.
|
||||
} else {
|
||||
dividend <<= 1;
|
||||
|
||||
// Yacht.txt, and indeed a real microprogram, would just subtract here
|
||||
// and test the sign of the result, but this is easier to follow:
|
||||
if (dividend >= divisor) {
|
||||
dividend -= divisor;
|
||||
cycles_expended += 6; // i.e. the original nn plus one further n before going down the MSB=0 route.
|
||||
} else {
|
||||
cycles_expended += 8; // The costliest path (since in real life it's a subtraction and then a step
|
||||
// back from there) — all costs accrue. So the fixed nn loop plus another n,
|
||||
// plus another one.
|
||||
}
|
||||
}
|
||||
}
|
||||
active_step_->microcycle.length = HalfCycles(cycles_expended * 2);
|
||||
} break;
|
||||
|
||||
case Operation::DIVS: {
|
||||
// An attempt to divide by zero schedules an exception.
|
||||
if(!active_program_->source->halves.low.full) {
|
||||
// TODO: schedule an exception.
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t dividend = int32_t(active_program_->destination->full);
|
||||
int32_t divisor = int16_t(active_program_->source->halves.low.full);
|
||||
const auto quotient = dividend / divisor;
|
||||
|
||||
int cycles_expended = 12; // Covers the nn nnn n to get beyond the sign test.
|
||||
if(dividend < 0) {
|
||||
cycles_expended += 2; // An additional microycle applies if the dividend is negative.
|
||||
}
|
||||
|
||||
// Check for overflow. If it exists, work here is already done.
|
||||
if(quotient > 32767 || quotient < -32768) {
|
||||
overflow_flag_ = 1;
|
||||
active_step_->microcycle.length = HalfCycles(3*2*2);
|
||||
break;
|
||||
}
|
||||
|
||||
overflow_flag_ = 0;
|
||||
zero_result_ = quotient;
|
||||
negative_flag_ = zero_result_ & 0x8000;
|
||||
|
||||
// TODO: check sign rules here; am I necessarily giving the remainder the correct sign?
|
||||
// (and, if not, am I counting it in the correct direction?)
|
||||
const uint16_t remainder = uint16_t(dividend % divisor);
|
||||
active_program_->destination->halves.high.full = remainder;
|
||||
active_program_->destination->halves.low.full = quotient;
|
||||
|
||||
// Algorithm here: there is a fixed three-microcycle cost per bit set
|
||||
// in the unsigned quotient; there is an additional microcycle for
|
||||
// every bit that is set. Also, since the possibility of overflow
|
||||
// was already dealt with, it's now a smaller number.
|
||||
int positive_quotient = abs(quotient);
|
||||
for(int c = 0; c < 15; ++c) {
|
||||
if(positive_quotient & 0x8000) cycles_expended += 2;
|
||||
}
|
||||
|
||||
// There's then no way to terminate the loop that isn't at least six cycles long.
|
||||
cycles_expended += 6;
|
||||
|
||||
if(divisor < 0) {
|
||||
cycles_expended += 2;
|
||||
} else if(dividend < 0) {
|
||||
cycles_expended += 4;
|
||||
}
|
||||
active_step_->microcycle.length = HalfCycles(cycles_expended * 2);
|
||||
} break;
|
||||
|
||||
/*
|
||||
MOVEM: multi-word moves.
|
||||
*/
|
||||
|
@ -424,6 +424,7 @@ struct ProcessorStorageConstructor {
|
||||
MOVEq, // Maps a destination register to a MOVEQ.
|
||||
|
||||
MULU_MULS, // Maps a destination register and a source mode and register to a MULU or MULS.
|
||||
DIVU_DIVS, // Maps a destination register and a source mode and register to a DIVU or DIVS.
|
||||
|
||||
RESET, // Maps to a RESET.
|
||||
|
||||
@ -647,6 +648,9 @@ struct ProcessorStorageConstructor {
|
||||
{0xf1c0, 0xc0c0, Operation::MULU, Decoder::MULU_MULS}, // 4-139 (p243)
|
||||
{0xf1c0, 0xc1c0, Operation::MULS, Decoder::MULU_MULS}, // 4-136 (p240)
|
||||
|
||||
{0xf1c0, 0x80c0, Operation::DIVU, Decoder::DIVU_DIVS}, // 4-97 (p201)
|
||||
{0xf1c0, 0x81c0, Operation::DIVS, Decoder::DIVU_DIVS}, // 4-93 (p197)
|
||||
|
||||
{0xfff0, 0x4e60, Operation::MOVEAl, Decoder::MOVEUSP}, // 6-21 (p475)
|
||||
|
||||
{0xfff0, 0x4e40, Operation::TRAP, Decoder::TRAP}, // 4-188 (p292)
|
||||
@ -1025,6 +1029,53 @@ struct ProcessorStorageConstructor {
|
||||
}
|
||||
} break;
|
||||
|
||||
case Decoder::DIVU_DIVS: {
|
||||
const int destination_register = (instruction >> 9) & 7;
|
||||
storage_.instructions[instruction].set_source(storage_, ea_mode, ea_register);
|
||||
storage_.instructions[instruction].set_destination(storage_, Dn, destination_register);
|
||||
|
||||
const int mode = combined_mode(ea_mode, ea_register);
|
||||
switch(mode) {
|
||||
default: continue;
|
||||
|
||||
case Dn: // [DIVU/DIVS] Dn, Dn
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
op(Action::None, seq("np"));
|
||||
break;
|
||||
|
||||
case Ind: // [DIVU/DIVS] (An), Dn
|
||||
case PostInc: // [DIVU/DIVS] (An)+, Dn
|
||||
op(Action::None, seq("nr", { a(ea_register) }));
|
||||
op(Action::PerformOperation, seq("r np"));
|
||||
if(mode == PostInc) {
|
||||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||||
}
|
||||
break;
|
||||
|
||||
case PreDec: // [DIVU/DIVS] -(An), Dn
|
||||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("nr", { a(ea_register) }));
|
||||
op(Action::PerformOperation, seq("r np"));
|
||||
break;
|
||||
|
||||
case XXXl: // [DIVU/DIVS] (XXX).l, Dn
|
||||
op(Action::None, seq("np"));
|
||||
case XXXw: // [DIVU/DIVS] (XXX).w, Dn
|
||||
case d16An: // [DIVU/DIVS] (d16, An), Dn
|
||||
case d16PC: // [DIVU/DIVS] (d16, PC), Dn
|
||||
case d8AnXn: // [DIVU/DIVS] (d8, An, Xn), Dn
|
||||
case d8PCXn: // [DIVU/DIVS] (d8, PC, Xn), Dn
|
||||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq("np nr", { ea(0) }));
|
||||
op(Action::PerformOperation, seq("r np"));
|
||||
break;
|
||||
|
||||
case Imm: // [DIVU/DIVS] #, Dn
|
||||
// DEVIATION FROM YACHT.TXT. It shows an additional np, which is incorrect.
|
||||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||||
op(Action::PerformOperation, seq("r np"));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Decoder::MULU_MULS: {
|
||||
const int destination_register = (instruction >> 9) & 7;
|
||||
storage_.instructions[instruction].set_source(storage_, ea_mode, ea_register);
|
||||
@ -1034,13 +1085,13 @@ struct ProcessorStorageConstructor {
|
||||
switch(mode) {
|
||||
default: continue;
|
||||
|
||||
case Dn:
|
||||
case Dn: // [MULU/MULS] Dn, Dn
|
||||
op(Action::None, seq("np"));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
break;
|
||||
|
||||
case Ind:
|
||||
case PostInc:
|
||||
case Ind: // [MULU/MULS] (An), Dn
|
||||
case PostInc: // [MULU/MULS] (An)+, Dn
|
||||
op(Action::None, seq("nr np", { a(ea_register) }));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
if(mode == PostInc) {
|
||||
@ -1048,28 +1099,24 @@ struct ProcessorStorageConstructor {
|
||||
}
|
||||
break;
|
||||
|
||||
case PreDec:
|
||||
case PreDec: // [MULU/MULS] -(An), Dn
|
||||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("n nr np", { a(ea_register) }));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
break;
|
||||
|
||||
case d16An:
|
||||
case d16PC:
|
||||
case d8AnXn:
|
||||
case d8PCXn:
|
||||
op(calc_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("n np nr np", mode), { ea(0) }));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
break;
|
||||
|
||||
case XXXl:
|
||||
case XXXl: // [MULU/MULS] (XXX).l, Dn
|
||||
op(Action::None, seq("np"));
|
||||
case XXXw:
|
||||
op(address_assemble_for_mode(mode) | MicroOp::SourceMask, seq("np nr np", { ea(0) }));
|
||||
case XXXw: // [MULU/MULS] (XXX).w, Dn
|
||||
case d16An: // [MULU/MULS] (d16, An), Dn
|
||||
case d16PC: // [MULU/MULS] (d16, PC), Dn
|
||||
case d8AnXn: // [MULU/MULS] (d8, An, Xn), Dn
|
||||
case d8PCXn: // [MULU/MULS] (d8, PX, Xn), Dn
|
||||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("n np nr np", mode), { ea(0) }));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
break;
|
||||
|
||||
case Imm:
|
||||
// DEVIATION FROM YACHT.TXT. It has an additional np, which I need to figure out.
|
||||
case Imm: // [MULU/MULS] #, Dn
|
||||
// DEVIATION FROM YACHT.TXT. It shows an additional np, which is incorrect.
|
||||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||||
op(Action::PerformOperation, seq("r"));
|
||||
break;
|
||||
|
@ -98,6 +98,7 @@ class ProcessorStorage {
|
||||
ORb, ORw, ORl,
|
||||
|
||||
MULU, MULS,
|
||||
DIVU, DIVS,
|
||||
|
||||
RTE_RTR,
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user