1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Attempts DIVU and DIVS.

Reportedly leaving 10965 operations now unimplemented.
This commit is contained in:
Thomas Harte 2019-04-26 22:22:35 -04:00
parent 796203859f
commit e75b386f7d
3 changed files with 184 additions and 19 deletions

View File

@ -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.
*/

View File

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

View File

@ -98,6 +98,7 @@ class ProcessorStorage {
ORb, ORw, ORl,
MULU, MULS,
DIVU, DIVS,
RTE_RTR,