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(0x00FF, 0x01, 1, 1; "overflow with half carry")]
|
||||
#[test_case(0x000A, 0x0C, 1, 0; "half carry")]
|
||||
#[test_case(0x0018, -0xC, 1, 0; "negative half carry")]
|
||||
#[test_case(0x000E, -0x0F, 1, 1; "underflow with 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")]
|
||||
fn test_load_effective_address_flags(
|
||||
starting_sp: u16,
|
||||
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.
|
||||
// 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
|
||||
|
@ -54,12 +70,40 @@ mod tests {
|
|||
assert_eq!((expected, half_carry, full_carry), a.add_with_carry(b));
|
||||
}
|
||||
|
||||
#[test_case(0x0000, 0x00, 0, false, false; "no addition")]
|
||||
#[test_case(0x00FF, 0x10, 271, false, true; "overflow")]
|
||||
#[test_case(0x00C0, 0x7F, 319, false, true; "overflow 2")]
|
||||
#[test_case(0x00FF, 0x01, 256, true, true; "overflow with half carry")]
|
||||
#[test_case(0x0F0A, 0x0C, 3862, true, false; "half carry")]
|
||||
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));
|
||||
macro_rules! u16_u8_positive_cases {
|
||||
($test: item) => {
|
||||
#[test_case(0x0000, 0x00, 0, false, false; "no addition")]
|
||||
#[test_case(0x00FF, 0x10, 271, false, true; "overflow")]
|
||||
#[test_case(0x00C0, 0x7F, 319, false, true; "overflow 2")]
|
||||
#[test_case(0x00FF, 0x01, 256, true, true; "overflow with half carry")]
|
||||
#[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
|
||||
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
|
||||
|
||||
let (new_sp, half_carry, carry) = if offset >= 0 {
|
||||
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 (new_sp, half_carry, carry) = current_sp.add_with_carry(offset);
|
||||
|
||||
let half_carry_bit = if half_carry { 1 } else { 0 };
|
||||
let carry_bit = if carry { 1 } else { 0 };
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
|
|
Loading…
Reference in New Issue