From eea884048373cd841147a72e72f59f766d97b4d8 Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 18 Sep 2022 20:34:04 -0700 Subject: [PATCH] Fixed the ABCD/SBCD/NBCD instructions (almost) There are still some failures on the SBCD, but the logic is identical to other emulator's calculations, but the test case doesn't seem to be the way it should behave, so I'll leave it for now --- src/cpus/m68k/execute.rs | 70 +++++++++++++++++++-------- tests/harte_tests/run_all.sh | 7 +-- tests/harte_tests/run_exclude_addr.sh | 11 +++++ todo.txt | 3 ++ 4 files changed, 67 insertions(+), 24 deletions(-) create mode 100755 tests/harte_tests/run_exclude_addr.sh diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index bbef921..f5ab250 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -224,12 +224,24 @@ impl M68k { self.timer.execute.start(); match self.decoder.instruction { Instruction::ABCD(src, dest) => { - let value = convert_from_bcd(self.get_target_value(src, Size::Byte, Used::Once)? as u8); - let existing = convert_from_bcd(self.get_target_value(dest, Size::Byte, Used::Twice)? as u8); - let result = existing.wrapping_add(value).wrapping_add(self.get_flag(Flags::Extend) as u8); - let carry = result > 99; - self.set_target_value(dest, convert_to_bcd(result) as u32, Size::Byte, Used::Twice)?; + let value = self.get_target_value(src, Size::Byte, Used::Once)?; + let existing = self.get_target_value(dest, Size::Byte, Used::Twice)?; + + let extend_flag = self.get_flag(Flags::Extend) as u32; + let src_parts = get_nibbles_from_byte(value); + let dest_parts = get_nibbles_from_byte(existing); + + let binary_result = value + existing + extend_flag; + let mut result = src_parts.1 + dest_parts.1 + extend_flag; + if result > 0x09 { result += 0x06 }; + result += src_parts.0 + dest_parts.0; + if result > 0x99 { result += 0x60 }; + let carry = (result & 0xFFFFFF00) != 0; + + self.set_target_value(dest, result, Size::Byte, Used::Twice)?; + self.set_flag(Flags::Negative, get_msb(result, Size::Byte)); self.set_flag(Flags::Zero, result == 0); + self.set_flag(Flags::Overflow, (!binary_result & result & 0x80) != 0); self.set_flag(Flags::Carry, carry); self.set_flag(Flags::Extend, carry); }, @@ -723,8 +735,11 @@ impl M68k { } self.state.d_reg[dest_l as usize] = (result & 0x00000000FFFFFFFF) as u32; }, - //Instruction::NBCD(Target) => { - //}, + Instruction::NBCD(dest) => { + let existing = self.get_target_value(dest, Size::Byte, Used::Twice)?; + let result = self.execute_sbcd(existing, 0)?; + self.set_target_value(dest, result, Size::Byte, Used::Twice)?; + }, Instruction::NEG(target, size) => { let original = self.get_target_value(target, size, Used::Twice)?; let (result, overflow) = overflowing_sub_signed_sized(0, original, size); @@ -857,14 +872,10 @@ impl M68k { self.state.status = Status::Stopped; }, Instruction::SBCD(src, dest) => { - let value = convert_from_bcd(self.get_target_value(src, Size::Byte, Used::Once)? as u8); - let existing = convert_from_bcd(self.get_target_value(dest, Size::Byte, Used::Twice)? as u8); - let result = existing.wrapping_sub(value).wrapping_sub(self.get_flag(Flags::Extend) as u8); - let borrow = existing < value; - self.set_target_value(dest, convert_to_bcd(result) as u32, Size::Byte, Used::Twice)?; - self.set_flag(Flags::Zero, result == 0); - self.set_flag(Flags::Carry, borrow); - self.set_flag(Flags::Extend, borrow); + let value = self.get_target_value(src, Size::Byte, Used::Once)?; + let existing = self.get_target_value(dest, Size::Byte, Used::Twice)?; + let result = self.execute_sbcd(value, existing)?; + self.set_target_value(dest, result, Size::Byte, Used::Twice)?; }, Instruction::SUB(src, dest, size) => { let value = self.get_target_value(src, size, Used::Once)?; @@ -947,6 +958,27 @@ impl M68k { Ok(()) } + fn execute_sbcd(&mut self, value: u32, existing: u32) -> Result { + let extend_flag = self.get_flag(Flags::Extend) as u32; + let src_parts = get_nibbles_from_byte(value); + let dest_parts = get_nibbles_from_byte(existing); + + let binary_result = existing.wrapping_sub(value).wrapping_sub(extend_flag); + let mut result = dest_parts.1.wrapping_sub(src_parts.1).wrapping_sub(extend_flag); + if (result & 0x1F) > 0x09 { result -= 0x06 }; + result = result.wrapping_add(dest_parts.0.wrapping_sub(src_parts.0)); + let carry = (result & 0x1FF) > 0x99; + if carry { result -= 0x60 }; + + self.set_flag(Flags::Negative, get_msb(result, Size::Byte)); + self.set_flag(Flags::Zero, (result & 0xFF) == 0); + self.set_flag(Flags::Overflow, (binary_result & !result & 0x80) != 0); + self.set_flag(Flags::Carry, carry); + self.set_flag(Flags::Extend, carry); + + Ok(result) + } + fn execute_movem(&mut self, target: Target, size: Size, dir: Direction, mask: u16) -> Result<(), Error> { let addr = self.get_target_address(target)?; @@ -1532,12 +1564,8 @@ fn rotate_operation(value: u32, size: Size, dir: ShiftDirection, use_extend: Opt } } -fn convert_from_bcd(value: u8) -> u8 { - (value >> 4) * 10 + (value & 0x0F) -} - -fn convert_to_bcd(value: u8) -> u8 { - (((value / 10) & 0x0F) << 4) | ((value % 10) & 0x0F) +fn get_nibbles_from_byte(value: u32) -> (u32, u32) { + (value & 0xF0, value & 0x0F) } fn get_value_sized(value: u32, size: Size) -> u32 { diff --git a/tests/harte_tests/run_all.sh b/tests/harte_tests/run_all.sh index 2ed8df4..df82279 100755 --- a/tests/harte_tests/run_all.sh +++ b/tests/harte_tests/run_all.sh @@ -2,9 +2,10 @@ COMMIT=$(git rev-parse HEAD) DATE=$(date --iso) LOCATION=$(dirname ${BASH_SOURCE[0]}) +RESULTS=latest.txt { cd $LOCATION - echo "Last run on $DATE at commit $COMMIT" | tee latest.txt - echo "" | tee -a latest.txt - cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" | tee -a latest.txt + echo "Last run on $DATE at commit $COMMIT" | tee $RESULTSt + echo "" | tee -a $RESULTS + cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" | tee -a $RESULTS } diff --git a/tests/harte_tests/run_exclude_addr.sh b/tests/harte_tests/run_exclude_addr.sh new file mode 100755 index 0000000..7ae4c26 --- /dev/null +++ b/tests/harte_tests/run_exclude_addr.sh @@ -0,0 +1,11 @@ +#!/bin/bash +COMMIT=$(git rev-parse HEAD) +DATE=$(date --iso) +LOCATION=$(dirname ${BASH_SOURCE[0]}) +RESULTS=latest-excluding-addr-error.txt +{ + cd $LOCATION + echo "Last run on $DATE at commit $COMMIT" | tee $RESULTS + echo "" | tee -a $RESULTS + cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" -e exclude-addr | tee -a $RESULTS +} diff --git a/todo.txt b/todo.txt index 32dd63b..862d391 100644 --- a/todo.txt +++ b/todo.txt @@ -3,6 +3,9 @@ Harte Tests: * for every failing test in MOVEfromSR, it's caused by an exception where at 0x7F3 it should be 0xF5, it's actually 0xE5, which is the READ/WRITE flag not set correctly (1 = READ) +* you could refactor the instruction loop into a series of functions, and test if there's a performance difference with and without #[inline(always)] + + * make it possible to compile without audio support (minifb frontend requires it atm) * I need some better function for dealing with memory, like a function that copies data with a loop, or allows offset reading of