From 8aaae8cbc173bbb56ceea28e529ee13d1db90ce9 Mon Sep 17 00:00:00 2001 From: Chris Hopper Date: Thu, 22 Oct 2026 16:06:15 +0100 Subject: [PATCH] Fix 68000 ASR carry and extend for oversized shifts --- Experiments/M68kRightShift/CMakeLists.txt | 7 +++ Experiments/M68kRightShift/Cases.hpp | 46 +++++++++++++++++++ Experiments/M68kRightShift/README.md | 17 +++++++ Experiments/M68kRightShift/main.cpp | 31 +++++++++++++ .../Implementation/PerformImplementation.hpp | 4 +- 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 Experiments/M68kRightShift/CMakeLists.txt create mode 100644 Experiments/M68kRightShift/Cases.hpp create mode 100644 Experiments/M68kRightShift/README.md create mode 100644 Experiments/M68kRightShift/main.cpp diff --git a/Experiments/M68kRightShift/CMakeLists.txt b/Experiments/M68kRightShift/CMakeLists.txt new file mode 100644 index 000000000..33d6c4fdb --- /dev/null +++ b/Experiments/M68kRightShift/CMakeLists.txt @@ -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) diff --git a/Experiments/M68kRightShift/Cases.hpp b/Experiments/M68kRightShift/Cases.hpp new file mode 100644 index 000000000..cc86ba619 --- /dev/null +++ b/Experiments/M68kRightShift/Cases.hpp @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// Original, firmware-free stimuli and a one-bit-at-a-time specification model. +#pragma once +#include +#include + +namespace RightShiftTest { +struct Case { + uint32_t value, count, result; + uint16_t ccr, opcode, initial_ccr; + unsigned width; + bool logical, immediate; +}; +inline std::vector cases(bool logical) { + std::vector 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 counts = immediate ? + std::vector{1,2,3,4,5,6,7,8} : + std::vector{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> 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; +} +} diff --git a/Experiments/M68kRightShift/README.md b/Experiments/M68kRightShift/README.md new file mode 100644 index 000000000..81f01f5a4 --- /dev/null +++ b/Experiments/M68kRightShift/README.md @@ -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. diff --git a/Experiments/M68kRightShift/main.cpp b/Experiments/M68kRightShift/main.cpp new file mode 100644 index 000000000..d29b78fbc --- /dev/null +++ b/Experiments/M68kRightShift/main.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +#include "Cases.hpp" +#include "InstructionSets/M68k/Perform.hpp" +#include + +using namespace InstructionSet::M68k; +struct Timing { + int count=-1, calls=0; + template void did_shift(int n) { count=n; ++calls; } +}; +template 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(c.count,result,status,timing); + else Primitive::shift(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(c) : + c.width==16 ? test(c) : + test(c); + if(!ok) ++failures; + } + std::cout << "Right-shift conformance: " << failures << '/' << total << " failures\n"; + return failures ? 1 : 0; +} diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 3ba047001..4515dbad9 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -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()); } else { status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1; }