use crate::core::cpu::CpuCore;
use crate::core::ea::AddressingMode;
use crate::core::execute::RUN_MODE_BERR_AERR_RESET;
use crate::core::memory::AddressBus;
use crate::core::types::{CpuType, Size};
#[inline]
fn muls_transitions(src: u16) -> u32 {
let s = (src as u32) << 1; ((s ^ (s >> 1)) & 0xFFFF).count_ones()
}
#[inline]
fn divu_cycles(dividend: u32, divisor: u16) -> i32 {
let div = divisor as u32;
if (dividend >> 16) >= div {
return 10;
}
let hdivisor = div << 16;
let mut mcycles: i32 = 38;
let mut dividend = dividend;
for _ in 0..15 {
let temp = dividend;
dividend <<= 1;
if (temp as i32) < 0 {
dividend = dividend.wrapping_sub(hdivisor);
} else {
mcycles += 2;
if dividend >= hdivisor {
dividend = dividend.wrapping_sub(hdivisor);
mcycles -= 1;
}
}
}
mcycles * 2
}
#[inline]
fn divs_cycles(dividend: i32, divisor: i16) -> i32 {
let mut mcycles: i32 = 6;
if dividend < 0 {
mcycles += 1;
}
let adivisor = (divisor as i32).unsigned_abs();
let adividend = (dividend as i64).unsigned_abs() as u32;
if (adividend >> 16) >= adivisor {
return (mcycles + 2) * 2;
}
mcycles += 55;
if divisor >= 0 {
if dividend >= 0 {
mcycles -= 1;
} else {
mcycles += 1;
}
}
let aquotient = adividend / adivisor;
let mut q = (aquotient & 0xFFFF) as u16;
for _ in 0..15 {
if (q as i16) >= 0 {
mcycles += 1;
}
q <<= 1;
}
mcycles * 2
}
impl CpuCore {
pub fn exec_mulu<B: AddressBus>(
&mut self,
bus: &mut B,
mode: AddressingMode,
dst_reg: usize,
) -> i32 {
let src = self.read_ea(bus, mode, Size::Word) & 0xFFFF;
if self.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = self.d(dst_reg) & 0xFFFF;
let result = src * dst;
self.set_d(dst_reg, result);
self.not_z_flag = result;
self.n_flag = if result & 0x80000000 != 0 { 0x80 } else { 0 };
self.v_flag = 0;
self.c_flag = 0;
if self.cpu_type == CpuType::M68000 {
38 + 2 * src.count_ones() as i32
} else {
38
}
}
pub fn exec_muls<B: AddressBus>(
&mut self,
bus: &mut B,
mode: AddressingMode,
dst_reg: usize,
) -> i32 {
let src = self.read_ea(bus, mode, Size::Word) as i16 as i32;
if self.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = self.d(dst_reg) as i16 as i32;
let result = (src * dst) as u32;
self.set_d(dst_reg, result);
self.not_z_flag = result;
self.n_flag = if result & 0x80000000 != 0 { 0x80 } else { 0 };
self.v_flag = 0;
self.c_flag = 0;
if self.cpu_type == CpuType::M68000 {
38 + 2 * muls_transitions(src as u16) as i32
} else {
38
}
}
pub fn exec_divu<B: AddressBus>(
&mut self,
bus: &mut B,
mode: AddressingMode,
dst_reg: usize,
) -> i32 {
let src = self.read_ea(bus, mode, Size::Word) & 0xFFFF;
if self.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = self.d(dst_reg);
if src == 0 {
return self.exception_zero_divide(bus);
}
let cycles = if self.cpu_type == CpuType::M68000 {
divu_cycles(dst, src as u16)
} else {
140
};
let quotient = dst / src;
let remainder = dst % src;
if quotient >= 0x10000 {
self.v_flag = 0x80;
if self.sst_m68000_compat {
self.n_flag = 0x80;
self.not_z_flag = 1; self.c_flag = 0;
}
return cycles;
}
self.set_d(dst_reg, (remainder << 16) | (quotient & 0xFFFF));
self.not_z_flag = quotient;
self.n_flag = if quotient & 0x8000 != 0 { 0x80 } else { 0 };
self.v_flag = 0;
self.c_flag = 0;
cycles
}
pub fn exec_divs<B: AddressBus>(
&mut self,
bus: &mut B,
mode: AddressingMode,
dst_reg: usize,
) -> i32 {
let src = self.read_ea(bus, mode, Size::Word) as i16 as i32;
if self.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = self.d(dst_reg) as i32;
if src == 0 {
return self.exception_zero_divide(bus);
}
let cycles = if self.cpu_type == CpuType::M68000 {
divs_cycles(dst, src as i16)
} else {
158
};
if dst == i32::MIN && src == -1 {
self.set_d(dst_reg, 0);
self.not_z_flag = 0;
self.n_flag = 0;
self.v_flag = 0;
self.c_flag = 0;
return cycles;
}
let quotient = dst / src;
let remainder = dst % src;
if !(-32768..=32767).contains("ient) {
self.v_flag = 0x80;
if self.sst_m68000_compat {
self.n_flag = 0x80;
self.not_z_flag = 1; self.c_flag = 0;
}
return cycles;
}
let quotient_u16 = quotient as i16 as u16 as u32;
let remainder_u16 = remainder as i16 as u16 as u32;
self.set_d(dst_reg, (remainder_u16 << 16) | quotient_u16);
self.not_z_flag = quotient_u16;
self.n_flag = if quotient_u16 & 0x8000 != 0 { 0x80 } else { 0 };
self.v_flag = 0;
self.c_flag = 0;
cycles
}
}