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
This commit is contained in:
transistor 2022-09-18 20:34:04 -07:00
parent 267ef488ab
commit eea8840483
4 changed files with 67 additions and 24 deletions

View File

@ -224,12 +224,24 @@ impl M68k {
self.timer.execute.start(); self.timer.execute.start();
match self.decoder.instruction { match self.decoder.instruction {
Instruction::ABCD(src, dest) => { Instruction::ABCD(src, dest) => {
let value = convert_from_bcd(self.get_target_value(src, Size::Byte, Used::Once)? as u8); let value = self.get_target_value(src, Size::Byte, Used::Once)?;
let existing = convert_from_bcd(self.get_target_value(dest, Size::Byte, Used::Twice)? as u8); let existing = self.get_target_value(dest, Size::Byte, Used::Twice)?;
let result = existing.wrapping_add(value).wrapping_add(self.get_flag(Flags::Extend) as u8);
let carry = result > 99; let extend_flag = self.get_flag(Flags::Extend) as u32;
self.set_target_value(dest, convert_to_bcd(result) as u32, Size::Byte, Used::Twice)?; 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::Zero, result == 0);
self.set_flag(Flags::Overflow, (!binary_result & result & 0x80) != 0);
self.set_flag(Flags::Carry, carry); self.set_flag(Flags::Carry, carry);
self.set_flag(Flags::Extend, 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; 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) => { Instruction::NEG(target, size) => {
let original = self.get_target_value(target, size, Used::Twice)?; let original = self.get_target_value(target, size, Used::Twice)?;
let (result, overflow) = overflowing_sub_signed_sized(0, original, size); let (result, overflow) = overflowing_sub_signed_sized(0, original, size);
@ -857,14 +872,10 @@ impl M68k {
self.state.status = Status::Stopped; self.state.status = Status::Stopped;
}, },
Instruction::SBCD(src, dest) => { Instruction::SBCD(src, dest) => {
let value = convert_from_bcd(self.get_target_value(src, Size::Byte, Used::Once)? as u8); let value = self.get_target_value(src, Size::Byte, Used::Once)?;
let existing = convert_from_bcd(self.get_target_value(dest, Size::Byte, Used::Twice)? as u8); let existing = self.get_target_value(dest, Size::Byte, Used::Twice)?;
let result = existing.wrapping_sub(value).wrapping_sub(self.get_flag(Flags::Extend) as u8); let result = self.execute_sbcd(value, existing)?;
let borrow = existing < value; self.set_target_value(dest, result, Size::Byte, Used::Twice)?;
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);
}, },
Instruction::SUB(src, dest, size) => { Instruction::SUB(src, dest, size) => {
let value = self.get_target_value(src, size, Used::Once)?; let value = self.get_target_value(src, size, Used::Once)?;
@ -947,6 +958,27 @@ impl M68k {
Ok(()) Ok(())
} }
fn execute_sbcd(&mut self, value: u32, existing: u32) -> Result<u32, Error> {
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> { fn execute_movem(&mut self, target: Target, size: Size, dir: Direction, mask: u16) -> Result<(), Error> {
let addr = self.get_target_address(target)?; 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 { fn get_nibbles_from_byte(value: u32) -> (u32, u32) {
(value >> 4) * 10 + (value & 0x0F) (value & 0xF0, value & 0x0F)
}
fn convert_to_bcd(value: u8) -> u8 {
(((value / 10) & 0x0F) << 4) | ((value % 10) & 0x0F)
} }
fn get_value_sized(value: u32, size: Size) -> u32 { fn get_value_sized(value: u32, size: Size) -> u32 {

View File

@ -2,9 +2,10 @@
COMMIT=$(git rev-parse HEAD) COMMIT=$(git rev-parse HEAD)
DATE=$(date --iso) DATE=$(date --iso)
LOCATION=$(dirname ${BASH_SOURCE[0]}) LOCATION=$(dirname ${BASH_SOURCE[0]})
RESULTS=latest.txt
{ {
cd $LOCATION cd $LOCATION
echo "Last run on $DATE at commit $COMMIT" | tee latest.txt echo "Last run on $DATE at commit $COMMIT" | tee $RESULTSt
echo "" | tee -a latest.txt echo "" | tee -a $RESULTS
cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" | tee -a latest.txt cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" | tee -a $RESULTS
} }

View File

@ -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
}

View File

@ -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) * 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) * 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 * I need some better function for dealing with memory, like a function that copies data with a loop, or allows offset reading of