From c447655047a4277e9e95ae18fc1e9df8331f918c Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Mon, 24 Jun 2019 16:51:43 -0400
Subject: [PATCH] Resolves assumption that shifts greater than the bit count of
 the relevant int are well-defined in C.

---
 .../Mac/Clock SignalTests/68000Tests.mm       | 92 +++++++++++++++++++
 .../Implementation/68000Implementation.hpp    | 16 ++--
 2 files changed, 102 insertions(+), 6 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/68000Tests.mm b/OSBindings/Mac/Clock SignalTests/68000Tests.mm
index 17660b150..04ac7f9fa 100644
--- a/OSBindings/Mac/Clock SignalTests/68000Tests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/68000Tests.mm	
@@ -1644,6 +1644,98 @@ class CPU::MC68000::ProcessorStorageTests {
 	XCTAssertEqual(16, _machine->get_cycle_count());
 }
 
+// MARK: LSL
+
+- (void)testLSLb_Dn_2 {
+	_machine->set_program({
+		0xe529		// LSL.b D2, D1
+	});
+	auto state = _machine->get_processor_state();
+	state.data[1] = 0xce3dd567;
+	state.data[2] = 2;
+
+	_machine->set_processor_state(state);
+	_machine->run_for_instructions(1);
+
+	state = _machine->get_processor_state();
+	XCTAssertEqual(state.data[1], 0xce3dd59c);
+	XCTAssertEqual(state.data[2], 2);
+	XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Extend | Flag::Negative | Flag::Carry);
+	XCTAssertEqual(10, _machine->get_cycle_count());
+}
+
+- (void)testLSLb_Dn_69 {
+	_machine->set_program({
+		0xe529		// LSL.b D2, D1
+	});
+	auto state = _machine->get_processor_state();
+	state.data[1] = 0xce3dd567;
+	state.data[2] = 0x69;
+
+	_machine->set_processor_state(state);
+	_machine->run_for_instructions(1);
+
+	state = _machine->get_processor_state();
+	XCTAssertEqual(state.data[1], 0xce3dd500);
+	XCTAssertEqual(state.data[2], 0x69);
+	XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Zero);
+	XCTAssertEqual(88, _machine->get_cycle_count());
+}
+
+- (void)testLSLw_Dn_0 {
+	_machine->set_program({
+		0xe569		// LSL.w D2, D1
+	});
+	auto state = _machine->get_processor_state();
+	state.data[1] = 0xce3dd567;
+	state.data[2] = 0;
+
+	_machine->set_processor_state(state);
+	_machine->run_for_instructions(1);
+
+	state = _machine->get_processor_state();
+	XCTAssertEqual(state.data[1], 0xce3dd567);
+	XCTAssertEqual(state.data[2], 0);
+	XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Negative);
+	XCTAssertEqual(6, _machine->get_cycle_count());
+}
+
+- (void)testLSLw_Dn_b {
+	_machine->set_program({
+		0xe569		// LSL.w D2, D1
+	});
+	auto state = _machine->get_processor_state();
+	state.data[1] = 0xce3dd567;
+	state.data[2] = 0xb;
+
+	_machine->set_processor_state(state);
+	_machine->run_for_instructions(1);
+
+	state = _machine->get_processor_state();
+	XCTAssertEqual(state.data[1], 0xce3d3800);
+	XCTAssertEqual(state.data[2], 0xb);
+	XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Extend | Flag::Carry);
+	XCTAssertEqual(28, _machine->get_cycle_count());
+}
+
+- (void)testLSLl_Dn {
+	_machine->set_program({
+		0xe5a9		// LSL.l D2, D1
+	});
+	auto state = _machine->get_processor_state();
+	state.data[1] = 0xce3dd567;
+	state.data[2] = 0x20;
+
+	_machine->set_processor_state(state);
+	_machine->run_for_instructions(1);
+
+	state = _machine->get_processor_state();
+	XCTAssertEqual(state.data[1], 0);
+	XCTAssertEqual(state.data[2], 0x20);
+	XCTAssertEqual(state.status & Flag::ConditionCodes, Flag::Extend | Flag::Carry | Flag::Zero);
+	XCTAssertEqual(72, _machine->get_cycle_count());
+}
+
 // MARK: MOVEM
 
 - (void)testMOVEM {
diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp
index 5a45d1fa3..a7870eaf8 100644
--- a/Processors/68000/Implementation/68000Implementation.hpp
+++ b/Processors/68000/Implementation/68000Implementation.hpp
@@ -1557,7 +1557,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
 	if(!shift_count) {	\
 		carry_flag_ = 0;	\
 	} else {	\
-		destination = decltype(destination)(value << shift_count);	\
+		destination = (shift_count < size) ? decltype(destination)(value << shift_count) : 0;	\
 		extend_flag_ = carry_flag_ = decltype(carry_flag_)(value) & decltype(carry_flag_)( (1 << (size - 1)) >> (shift_count - 1) );	\
 	}	\
 \
@@ -1583,10 +1583,14 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
 	if(!shift_count) {	\
 		carry_flag_ = 0;	\
 	} else {	\
-		destination = decltype(destination)(\
-			(value >> shift_count) |	\
-			((value & decltype(value)(1 << (size - 1)) ? 0xffffffff : 0x000000000) << (size - shift_count))	\
-		);	\
+		destination = (shift_count < size) ?	\
+				decltype(destination)(\
+					(value >> shift_count) |	\
+					((value & decltype(value)(1 << (size - 1)) ? 0xffffffff : 0x000000000) << (size - shift_count))	\
+				) :	\
+				decltype(destination)(	\
+					(value & decltype(value)(1 << (size - 1))) ? 0xffffffff : 0x000000000	\
+				);	\
 		extend_flag_ = carry_flag_ = decltype(carry_flag_)(value) & decltype(carry_flag_)(1 << (shift_count - 1));	\
 	}	\
 \
@@ -1634,7 +1638,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
 	if(!shift_count) {	\
 		carry_flag_ = 0;	\
 	} else {	\
-		destination = value >> shift_count;	\
+		destination = (shift_count < size) ? (value >> shift_count) : 0;	\
 		extend_flag_ = carry_flag_ = value & decltype(carry_flag_)(1 << (shift_count - 1));	\
 	}	\
 \