Add overload to carry-add negatives
parent
418a659e07
commit
e6b25cd54b
|
@ -548,8 +548,9 @@ mod tests {
|
||||||
#[test_case(0x00C0, 0x7f, 0, 1; "overflow 2")]
|
#[test_case(0x00C0, 0x7f, 0, 1; "overflow 2")]
|
||||||
#[test_case(0x00FF, 0x01, 1, 1; "overflow with half carry")]
|
#[test_case(0x00FF, 0x01, 1, 1; "overflow with half carry")]
|
||||||
#[test_case(0x000A, 0x0C, 1, 0; "half carry")]
|
#[test_case(0x000A, 0x0C, 1, 0; "half carry")]
|
||||||
#[test_case(0x0018, -0xC, 1, 0; "negative half carry")]
|
#[test_case(0x0018, -0x4, 1, 0; "negative half carry")]
|
||||||
#[test_case(0x000E, -0x0F, 1, 1; "underflow with half carry")]
|
#[test_case(0x000F, -0x10, 1, 1; "underflow with half carry")]
|
||||||
|
#[test_case(0x000E, -0x0F, 0, 1; "underflow with no half carry")]
|
||||||
fn test_load_effective_address_flags(
|
fn test_load_effective_address_flags(
|
||||||
starting_sp: u16,
|
starting_sp: u16,
|
||||||
add_value: i8,
|
add_value: i8,
|
||||||
|
|
|
@ -24,6 +24,22 @@ 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;
|
||||||
|
|
||||||
|
(total, half_carry, carry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `did_8_bit_half_carry` checks whether or not the given addition would have done an 8 bit half carry.
|
/// `did_8_bit_half_carry` checks whether or not the given addition would have done an 8 bit half carry.
|
||||||
// NOTE: These generics are not as generic as they could be. The Into<u16> is just a shortcut because we
|
// NOTE: These generics are not as generic as they could be. The Into<u16> is just a shortcut because we
|
||||||
// only use up to u16s
|
// only use up to u16s
|
||||||
|
@ -54,12 +70,40 @@ mod tests {
|
||||||
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case(0x0000, 0x00, 0, false, false; "no addition")]
|
macro_rules! u16_u8_positive_cases {
|
||||||
#[test_case(0x00FF, 0x10, 271, false, true; "overflow")]
|
($test: item) => {
|
||||||
#[test_case(0x00C0, 0x7F, 319, false, true; "overflow 2")]
|
#[test_case(0x0000, 0x00, 0, false, false; "no addition")]
|
||||||
#[test_case(0x00FF, 0x01, 256, true, true; "overflow with half carry")]
|
#[test_case(0x00FF, 0x10, 271, false, true; "overflow")]
|
||||||
#[test_case(0x0F0A, 0x0C, 3862, true, false; "half carry")]
|
#[test_case(0x00C0, 0x7F, 319, false, true; "overflow 2")]
|
||||||
fn test_u8_u16_carrying_add(a: u16, b: u8, expected: u16, half_carry: bool, full_carry: bool) {
|
#[test_case(0x00FF, 0x01, 256, true, true; "overflow with half carry")]
|
||||||
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
#[test_case(0x0F0A, 0x0C, 3862, true, false; "half carry")]
|
||||||
|
$test
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
u16_u8_positive_cases! {
|
||||||
|
fn test_u8_u16_carrying_add(a: u16, b: u8, expected: u16, half_carry: bool, full_carry: bool) {
|
||||||
|
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// our positive testcases should work just the same
|
||||||
|
u16_u8_positive_cases! {
|
||||||
|
// negative numbers, though, have their own cases to consider
|
||||||
|
//
|
||||||
|
// this first testcase is one that I generated by taking the difference of these two lists
|
||||||
|
// (there were many more examples but I picked one)
|
||||||
|
// In [1]: for sp in range(0xFFFF):
|
||||||
|
// ...: for n in range(1, 0xFF):
|
||||||
|
// ...: a.append((sp, n))
|
||||||
|
// ...: 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")]
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,26 +31,10 @@ impl InstructionRunner<SixteenBitLoadInstruction> for SixteenBitLoadRunner {
|
||||||
.registers
|
.registers
|
||||||
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
|
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
|
||||||
|
|
||||||
let (new_sp, half_carry, carry) = if offset >= 0 {
|
let (new_sp, half_carry, carry) = current_sp.add_with_carry(offset);
|
||||||
let unsigned_offset = u8::try_from(offset)
|
|
||||||
.expect("failed to put positive (or zero) 8 bit value into unsigned eight bit, which should always work...");
|
|
||||||
|
|
||||||
current_sp.add_with_carry(unsigned_offset)
|
|
||||||
} else {
|
|
||||||
let sixteen_bit_offset = u16::try_from(i16::from(offset).abs())
|
|
||||||
.expect("failed to convert an abs'd 16 bit value to a u16");
|
|
||||||
// TODO: This could be less gross but it's theo nly place we do it...
|
|
||||||
let (new_sp, underflow) = current_sp.overflowing_sub(sixteen_bit_offset);
|
|
||||||
// https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/
|
|
||||||
let half_carry =
|
|
||||||
((current_sp & 0xf) + (sixteen_bit_offset & 0xf)) & 0x10 == 0x10;
|
|
||||||
|
|
||||||
(new_sp, half_carry, underflow)
|
|
||||||
};
|
|
||||||
|
|
||||||
let half_carry_bit = if half_carry { 1 } else { 0 };
|
let half_carry_bit = if half_carry { 1 } else { 0 };
|
||||||
let carry_bit = if carry { 1 } else { 0 };
|
let carry_bit = if carry { 1 } else { 0 };
|
||||||
|
|
||||||
processor
|
processor
|
||||||
.registers
|
.registers
|
||||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
|
Loading…
Reference in New Issue