From e11990e4533ba46443cd980c7d49bf5404335d98 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 27 May 2022 14:56:04 -0400 Subject: [PATCH] Make an attempt at DIVS timing. --- .../Implementation/68000Mk2Implementation.hpp | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp index 2cd80f9e5..a9ddfb43e 100644 --- a/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp +++ b/Processors/68000Mk2/Implementation/68000Mk2Implementation.hpp @@ -2523,12 +2523,44 @@ void ProcessorBase::did_bit_op(int bit_position) { dynamic_instruction_length_ = int(bit_position > 15); } -template void ProcessorBase::did_divu(uint32_t, uint32_t) { - // TODO: calculate cost. -} +template void ProcessorBase::did_divu(uint32_t dividend, uint32_t divisor) { + if(!divisor) { + dynamic_instruction_length_ = 4; // nn nn precedes the usual exception activity. + return; + } -template void ProcessorBase::did_divs(int32_t, int32_t) { - // TODO: calculate cost. + if(did_overflow) { + dynamic_instruction_length_ = 3; // Just a quick nn n, and then on to prefetch. + return; + } + + // Calculate cost; this is based on the flowchart in yacht.txt. + // I could actually calculate the division result using this code, + // since this is a classic divide algorithm, but would rather that + // errors produce incorrect timing only, not incorrect timing plus + // incorrect results. + dynamic_instruction_length_ = 3; // 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; + dynamic_instruction_length_ += 2; // 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; + dynamic_instruction_length_ += 3; // i.e. the original nn plus one further n before going down the MSB=0 route. + } else { + dynamic_instruction_length_ += 4; // 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. + } + } + } } #define convert_to_bit_count_16(x) \ @@ -2537,6 +2569,49 @@ template void ProcessorBase::did_divs(int32_t, int32_t) { x = ((x & 0xf0f0) >> 4) + (x & 0x0f0f); \ x = ((x & 0xff00) >> 8) + (x & 0x00ff); +template void ProcessorBase::did_divs(int32_t dividend, int32_t divisor) { + // The route to spotting divide by 0 is just nn nn. + if(!divisor) { + dynamic_instruction_length_ = 4; // nn nn precedes the usual exception activity. + return; + } + + // It's either five or six microcycles to get into the main loop, depending + // on dividend sign. + dynamic_instruction_length_ = 5 + (dividend < 0); + + if(did_overflow) { + return; + } + + // There's always a cost of four microcycles per bit, plus an additional + // one for each that is non-zero. + // + // The sign bit does not count here; it's the low fifteen bits that matter + // only, in the unsigned version of the result. + dynamic_instruction_length_ += 60; + + int result_bits = abs(dividend / divisor) & 0x7fff; + convert_to_bit_count_16(result_bits); + dynamic_instruction_length_ += result_bits; + + // Determine the tail cost; a divisor of less than 0 leads to one exit, + // a divisor of greater than zero makes the result a function of the + // sign of the dividend. + // + // In all cases, this is counting from 'No more bits' in the Yacht diagram. + if(divisor < 0) { + dynamic_instruction_length_ += 4; + return; + } + + if(dividend < 0) { + dynamic_instruction_length_ += 5; + } else { + dynamic_instruction_length_ += 3; + } +} + template void ProcessorBase::did_mulu(IntT multiplier) { // Count number of bits set. convert_to_bit_count_16(multiplier);