m68000 0.2.2

A Motorola 68000 interpreter, disassembler and assembler (code emitter)
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Tests for the `overflowing_add`, `overflowing_sub`, `carrying_add` and `borrowing_sub` functions.
//!
//! The goal is to ensure that the functions have the correct behaviours on carry/overflow detection
//! to correctly compute the CCR bits during instructions.
//!
//! Currently, `carrying_add` and `borrowing_sub` does not have the correct behaviours on signed operands.

#![feature(bigint_helper_methods)]

macro_rules! test_operator {
    ($operator:expr, $expected:expr) => {
        let res = $operator;
        assert_eq!(res, $expected, "{} -> {:?}, expected {:?}", stringify!($operator), res, $expected);
    }
}

#[test]
fn std_overflowing() {
    // add u8 1
    test_operator!(255u8.overflowing_add(1), (0, true));
    test_operator!(127u8.overflowing_add(1), (128, false));
    // add i8 1
    test_operator!((255u8 as i8).overflowing_add(1), (0, false));
    test_operator!(127i8.overflowing_add(1), (-128, true));

    // add u8 -1
    test_operator!(0u8.overflowing_add(255), (255, false));
    test_operator!(128u8.overflowing_add(255), (127, true));
    // add i8 -1
    test_operator!(0i8.overflowing_add(-1), (-1, false));
    test_operator!((-128i8).overflowing_add(-1), (127, true));

    // sub u8 1
    test_operator!(0u8.overflowing_sub(1), (255, true));
    test_operator!(128u8.overflowing_sub(1), (127, false));
    // sub i8 1
    test_operator!(0i8.overflowing_sub(1), (-1, false));
    test_operator!((-128i8).overflowing_sub(1), (127, true));

    // sub u8 -1
    test_operator!(255u8.overflowing_sub(255), (0, false));
    test_operator!(127u8.overflowing_sub(255), (128, true));
    // sub i8 -1
    test_operator!((255u8 as i8).overflowing_sub(-1), (0, false));
    test_operator!(127i8.overflowing_sub(-1), (-128, true));
}

#[test]
fn std_big_int() {
    // add u8
    test_operator!(255u8.carrying_add(1, false), (0, true));
    test_operator!(255u8.carrying_add(0, true), (0, true));
    test_operator!(255u8.carrying_add(1, true), (1, true));
    test_operator!(0u8.carrying_add(255, false), (255, false));
    test_operator!(0u8.carrying_add(255, true), (0, true));

    // add i8
    test_operator!(127i8.carrying_add(1, false), (-128, true));
    test_operator!(127i8.carrying_add(0, true), (-128, true));
    test_operator!(127i8.carrying_add(1, true), (-127, true));
    test_operator!(127i8.carrying_add(-1, false), (126, false));
    test_operator!(127i8.carrying_add(-1, true), (127, false)); // no intermediate overflow
    test_operator!((-128i8).carrying_add(-1, false), (127, true));
    test_operator!((-128i8).carrying_add(-1, true), (-128, false)); // no intermediate overflow

    // sub u8
    test_operator!(0u8.borrowing_sub(1, false), (255u8, true));
    test_operator!(0u8.borrowing_sub(0, true), (255u8, true));
    test_operator!(0u8.borrowing_sub(1, true), (254u8, true));
    test_operator!(255u8.borrowing_sub(255, false), (0, false));
    test_operator!(255u8.borrowing_sub(255, true), (255u8, true));

    // sub i8
    test_operator!((-128i8).borrowing_sub(1, false), (127, true));
    test_operator!((-128i8).borrowing_sub(0, true), (127, true));
    test_operator!((-128i8).borrowing_sub(1, true), (126, true));
    test_operator!((-128i8).borrowing_sub(-1, false), (-127, false));
    test_operator!((-128i8).borrowing_sub(-1, true), (-128, false)); // no intermediate overflow
    test_operator!(127i8.borrowing_sub(-1, false), (-128, true));
    test_operator!(127i8.borrowing_sub(-1, true), (127, false)); // no intermediate overflow
}

#[test]
fn custom_extended() {
    // add u8
    test_operator!(255u8.extended_add(1, false), (0, true));
    test_operator!(255u8.extended_add(0, true), (0, true));
    test_operator!(255u8.extended_add(1, true), (1, true));
    test_operator!(0u8.extended_add(255, false), (255, false));
    test_operator!(0u8.extended_add(255, true), (0, true));

    // add i8
    test_operator!(127i8.extended_add(1, false), (-128, true));
    test_operator!(127i8.extended_add(0, true), (-128, true));
    test_operator!(127i8.extended_add(1, true), (-127, true));
    test_operator!(127i8.extended_add(-1, false), (126, false));
    test_operator!(127i8.extended_add(-1, true), (127, false)); // no intermediate overflow
    test_operator!((-128i8).extended_add(-1, false), (127, true));
    test_operator!((-128i8).extended_add(-1, true), (-128, false)); // no intermediate overflow
    test_operator!((0i8).extended_add(127, true), (-128, true));

    // sub u8
    test_operator!(0u8.extended_sub(1, false), (255u8, true));
    test_operator!(0u8.extended_sub(0, true), (255u8, true));
    test_operator!(0u8.extended_sub(1, true), (254u8, true));
    test_operator!(255u8.extended_sub(255, false), (0, false));
    test_operator!(255u8.extended_sub(255, true), (255u8, true));

    // sub i8
    test_operator!((-128i8).extended_sub(1, false), (127, true));
    test_operator!((-128i8).extended_sub(0, true), (127, true));
    test_operator!((-128i8).extended_sub(1, true), (126, true));
    test_operator!((-128i8).extended_sub(-1, false), (-127, false));
    test_operator!((-128i8).extended_sub(-1, true), (-128, false)); // no intermediate overflow
    test_operator!(127i8.extended_sub(-1, false), (-128, true));
    test_operator!(127i8.extended_sub(-1, true), (127, false)); // no intermediate overflow
    test_operator!((0i8).extended_sub(-128, true), (127, false));
}

pub trait BigInt {
    fn extended_add(self, rhs: Self, carry: bool) -> (Self, bool) where Self: Sized;
    fn extended_sub(self, rhs: Self, carry: bool) -> (Self, bool) where Self: Sized;
}

macro_rules! impl_bigint {
    ($type:ty, $bigtype:ty) => {
        impl BigInt for $type {
            fn extended_add(self, rhs: Self, carry: bool) -> (Self, bool)
            where Self: Sized {
                let res = self as $bigtype + rhs as $bigtype + carry as $bigtype;
                (res as Self, res < <$type>::MIN as $bigtype || res > <$type>::MAX as $bigtype)
            }

            fn extended_sub(self, rhs: Self, carry: bool) -> (Self, bool)
            where Self: Sized {
                let res = self as $bigtype - rhs as $bigtype - carry as $bigtype;
                (res as Self, res < <$type>::MIN as $bigtype || res > <$type>::MAX as $bigtype)
            }
        }
    };
}

impl_bigint!(u8, i16);
impl_bigint!(i8, i16);

impl_bigint!(u16, i32);
impl_bigint!(i16, i32);

impl_bigint!(u32, i64);
impl_bigint!(i32, i64);