Fix 0xF8 instruction flags
parent
c63aac5e35
commit
2ade899fc6
|
@ -83,15 +83,23 @@ impl CarryingAdd<u8, u16> for u16 {
|
|||
|
||||
impl CarryingAdd<i8, u16> for u16 {
|
||||
fn add_with_carry(self, rhs: i8) -> (u16, bool, bool) {
|
||||
if rhs >= 0 {
|
||||
return self.add_with_carry(rhs.unsigned_abs());
|
||||
}
|
||||
|
||||
let (total, carry) = self.overflowing_sub(rhs.unsigned_abs().into());
|
||||
// This is a special case of half carry. Though we could probably twiddle the bits to make one
|
||||
// unified half carry check, I'm gonna trust the guy who has debugged a real gameboy.
|
||||
// https://stackoverflow.com/a/7261149/1103734
|
||||
let half_carry = total & 0xF <= self & 0xF;
|
||||
let [upper_8_bits, lower_8_bits] = self.to_be_bytes();
|
||||
let operand = twos_comp_abs(rhs);
|
||||
let (lower_8_bit_total, half_carry, carry) = operand.add_with_carry(lower_8_bits);
|
||||
// Perform the actual "carry" by hand.
|
||||
//
|
||||
// This comes with heavy inspiration from https://www.reddit.com/r/EmuDev/comments/y51i1c/comment/isiizl4/?utm_source=reddit&utm_medium=web2x&context=3
|
||||
// with some tweaks to handle overflow
|
||||
let carry_adjustment = if !carry && rhs < 0 {
|
||||
// Subtract from the 8th bit
|
||||
!(1_u16 << 8)+1
|
||||
} else if carry && rhs > 0 {
|
||||
// Add to the 8th bit
|
||||
1_u16 << 8
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let (total, _final_carry) = u16::from_be_bytes([upper_8_bits, lower_8_bit_total]).overflowing_add(carry_adjustment);
|
||||
|
||||
(total, half_carry, carry)
|
||||
}
|
||||
|
@ -151,6 +159,14 @@ fn did_8bit_full_carry_including_carry_bit<L: Into<u16>, R: Into<u16>>(
|
|||
((lhs.into() & 0xff) + (rhs.into() & 0xff) + u16::from(carry_bit)) > 0xff
|
||||
}
|
||||
|
||||
fn twos_comp_abs(n: i8) -> u8 {
|
||||
if n >= 0 {
|
||||
n.unsigned_abs()
|
||||
} else {
|
||||
(!n.unsigned_abs()) + 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -195,9 +211,9 @@ mod tests {
|
|||
// ...: if ((sp - n) & 0xF) <= (sp & 0xF):
|
||||
// ...: c.append((sp, n))
|
||||
#[test_case(0x1D21, -0x46, 0x1CDB, false, false; "negative; no carry")]
|
||||
#[test_case(0x000F, -0x10, 0xFFFF, true, true; "negative; underflow")]
|
||||
#[test_case(0x000F, -0x7F, 0xFF90, true, true; "negative; underflow 2")]
|
||||
#[test_case(0x249D, -0x08, 0x2495, true, false; "negative; half carry")]
|
||||
#[test_case(0x000F, -0x10, 0xFFFF, false, false; "negative; underflow no carry")]
|
||||
#[test_case(0x000F, -0x7F, 0xFF90, true, false; "negative; underflow 2")]
|
||||
#[test_case(0x249D, -0x08, 0x2495, true, true; "negative; both carries")]
|
||||
fn test_i8_u16_carrying_add(a: u16, b: i8, expected: u16, half_carry: bool, full_carry: bool) {
|
||||
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
||||
}
|
||||
|
|
|
@ -110,7 +110,6 @@ impl InstructionRunner<SixteenBitLoadInstruction> for SixteenBitLoadRunner {
|
|||
];
|
||||
|
||||
let popped_value = u16::from_le_bytes(popped_bytes);
|
||||
println!("{popped_value:x}");
|
||||
|
||||
processor.registers.set_combined_register(dst, popped_value);
|
||||
processor.registers.set_16bit_register(
|
||||
|
|
|
@ -125,9 +125,9 @@ fn test_load_effective_address(start_sp: u16, value: i8, expected_sp: u16) {
|
|||
#[test_case(0x00C0, 0x7f, 0, 1; "overflow 2")]
|
||||
#[test_case(0x00FF, 0x01, 1, 1; "overflow with half carry")]
|
||||
#[test_case(0x000A, 0x0C, 1, 0; "half carry")]
|
||||
#[test_case(0x0018, -0x4, 1, 0; "negative half carry")]
|
||||
#[test_case(0x000F, -0x10, 1, 1; "underflow with half carry")]
|
||||
#[test_case(0x000E, -0x0F, 0, 1; "underflow with no half carry")]
|
||||
#[test_case(0x0018, -0x4, 1, 1; "negative both carry")]
|
||||
#[test_case(0x000F, -0x01, 1, 1; "underflow with half carry")]
|
||||
#[test_case(0x00FE, -0x0F, 0, 1; "underflow with no half carry")]
|
||||
#[test_case(0xFFFF, 0xA, 1, 1; "16 bit overflow results in both carries")]
|
||||
fn test_load_effective_address_flags(starting_sp: u16, add_value: i8, half_carry: u8, carry: u8) {
|
||||
let mut processor = Processor::default();
|
||||
|
|
Loading…
Reference in New Issue