Fix 68000 ASR carry and extend for oversized shifts
This commit is contained in:
1 parent
0f49993b4d
commit
8aaae8cbc1
5 files changed
+104
-1
No files matched your search
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(CLKM68kRightShift LANGUAGES CXX)
|
||||
add_executable(clk-m68k-right-shift main.cpp)
|
||||
target_compile_features(clk-m68k-right-shift PRIVATE cxx_std_20)
|
||||
target_include_directories(clk-m68k-right-shift PRIVATE ../..)
|
||||
enable_testing()
|
||||
add_test(NAME m68k-right-shift COMMAND clk-m68k-right-shift)
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Original, firmware-free stimuli and a one-bit-at-a-time specification model.
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace RightShiftTest {
|
||||
struct Case {
|
||||
uint32_t value, count, result;
|
||||
uint16_t ccr, opcode, initial_ccr;
|
||||
unsigned width;
|
||||
bool logical, immediate;
|
||||
};
|
||||
inline std::vector<Case> cases(bool logical) {
|
||||
std::vector<Case> out;
|
||||
for(unsigned width : {8u, 16u, 32u}) {
|
||||
const uint32_t mask = uint32_t(0xffffffffu >> (32-width));
|
||||
const uint32_t sign = uint32_t(1) << (width-1);
|
||||
for(uint32_t low : {0u, 1u, sign-1, sign, mask, sign | 0x25u})
|
||||
for(unsigned x : {0u, 1u})
|
||||
for(bool immediate : {false, true}) {
|
||||
const std::vector<uint32_t> counts = immediate ?
|
||||
std::vector<uint32_t>{1,2,3,4,5,6,7,8} :
|
||||
std::vector<uint32_t>{0,1,width-1,width,width+1,63,64,65,128,0xddb92f2c};
|
||||
for(uint32_t count : counts) {
|
||||
uint32_t v = low & mask, carry = 0;
|
||||
const unsigned n = count & 63;
|
||||
for(unsigned i=0; i<n; ++i) {
|
||||
carry = v & 1;
|
||||
v = (v >> 1) | (!logical ? v & sign : 0);
|
||||
}
|
||||
const uint16_t ccr = uint16_t(((n ? carry : x)<<4) |
|
||||
((v & sign) ? 8 : 0) | (!v ? 4 : 0) | (n ? carry : 0));
|
||||
// Preserve upper D3 bits for byte/word operations.
|
||||
const uint32_t upper = 0x5aa55aa5u & ~mask;
|
||||
const uint16_t size = width == 8 ? 0 : width == 16 ? 0x40 : 0x80;
|
||||
const uint16_t opcode = uint16_t(0xe003 | size | (logical ? 8 : 0) |
|
||||
(immediate ? (count & 7)<<9 : 0x820));
|
||||
out.push_back({upper | low, count, upper | v, ccr, opcode,
|
||||
uint16_t(0x0f | x<<4), width, logical, immediate});
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Independent 68000 right-shift regression
|
||||
|
||||
Original MIT-licensed test stimuli. Requires CMake 3.19+, a C++20 compiler and the CPU headers in this checkout; no firmware, disk images or external libraries are required.
|
||||
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
cmake -S Experiments/M68kRightShift -B build/m68k-right-shift
|
||||
cmake --build build/m68k-right-shift --config Release
|
||||
ctest --test-dir build/m68k-right-shift -C Release --output-on-failure
|
||||
```
|
||||
|
||||
The suite checks 648 ASR and 648 LSR cases across byte, word and longword operands, six operand patterns and both initial X states. Register counts include 0, 1, width-1, width, width+1, 63, 64, 65, 128 and a large value with effective count 44. Immediate-count cases cover 1 through 8. An independent one-bit-at-a-time model supplies expected results and all X/N/Z/V/C flags, including preserved X at zero effective count. Initial N/Z/V/C are set to test recomputation.
|
||||
|
||||
The executable tests the shared CPU primitive, not instruction decoding. It also checks preserved supervisor/interrupt state and exactly one timing notification carrying the modulo-64 count; it does not independently measure physical instruction timings.
|
||||
|
||||
Specification: Motorola [M68000 Family Programmer's Reference Manual, ASL/ASR pp. 4-21 to 4-22 and LSL/LSR pp. 4-113 to 4-114](https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf#page=125). ASR shifts the sign bit back into the operand and transfers the last shifted-out bit to C/X. Zero count clears C/V and preserves X; register counts are modulo 64.
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "Cases.hpp"
|
||||
#include "InstructionSets/M68k/Perform.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace InstructionSet::M68k;
|
||||
struct Timing {
|
||||
int count=-1, calls=0;
|
||||
template<typename T> void did_shift(int n) { count=n; ++calls; }
|
||||
};
|
||||
template<typename T, Operation asr, Operation lsr> bool test(const RightShiftTest::Case &c) {
|
||||
T result=T(c.value);
|
||||
Status status; status.set_status(uint16_t(0x2700 | c.initial_ccr));
|
||||
Timing timing;
|
||||
if(c.logical) Primitive::shift<lsr>(c.count,result,status,timing);
|
||||
else Primitive::shift<asr>(c.count,result,status,timing);
|
||||
return result==T(c.result) && status.ccr()==c.ccr &&
|
||||
(status.status() & 0xffe0)==0x2700 && timing.calls==1 && timing.count==int(c.count&63);
|
||||
}
|
||||
int main() {
|
||||
unsigned total=0, failures=0;
|
||||
for(bool logical : {false,true}) for(const auto &c : RightShiftTest::cases(logical)) {
|
||||
++total;
|
||||
const bool ok = c.width==8 ? test<uint8_t,Operation::ASRb,Operation::LSRb>(c) :
|
||||
c.width==16 ? test<uint16_t,Operation::ASRw,Operation::LSRw>(c) :
|
||||
test<uint32_t,Operation::ASRl,Operation::LSRl>(c);
|
||||
if(!ok) ++failures;
|
||||
}
|
||||
std::cout << "Right-shift conformance: " << failures << '/' << total << " failures\n";
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -378,7 +378,9 @@ void shift(const uint32_t source, IntT &destination, Status &status, FlowControl
|
||||
case Type::ASR:
|
||||
case Type::LSR: {
|
||||
if(shift > size) {
|
||||
status.carry_flag = status.extend_flag = 0;
|
||||
// ASR continues shifting out the sign bit; LSR shifts out zeroes.
|
||||
status.carry_flag = status.extend_flag =
|
||||
type == Type::ASR && (destination & Numeric::top_bit<IntT>());
|
||||
} else {
|
||||
status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user