Add overload to carry-add negatives

jsmoo
Nick Krichevsky 2022-04-23 16:37:50 -04:00
parent 418a659e07
commit e6b25cd54b
3 changed files with 55 additions and 26 deletions

View File

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

View File

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

View File

@ -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);