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 mulu_internal_clocks(src: u16) -> u32 {
34 + 2 * src.count_ones()
}
#[inline]
fn muls_internal_clocks(src: u16) -> u32 {
34 + 2 * muls_transitions(src)
}
#[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 {
fn finish_m68000_mul<B: AddressBus>(&mut self, bus: &mut B, internal_clocks: u32) {
self.top_up_prefetch(bus);
self.internal_cycles(internal_clocks);
self.flush_sync(bus);
}
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;
if self.cpu_type == CpuType::M68000 {
self.finish_m68000_mul(bus, mulu_internal_clocks(src as u16));
}
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 & 0xFFFF).count_ones() as i32 + self.ea_source_cycles(mode, Size::Word)
} else {
42
}
}
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;
if self.cpu_type == CpuType::M68000 {
self.finish_m68000_mul(bus, muls_internal_clocks(src as u16));
}
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 + self.ea_source_cycles(mode, Size::Word)
} else {
42
}
}
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);
let m68000 = self.cpu_type == CpuType::M68000;
if src == 0 {
if m68000 {
self.internal_cycles(8);
}
return self.exception_zero_divide(bus);
}
let div_clocks = if m68000 {
divu_cycles(dst, src as u16)
} else {
140
};
let cycles = if m68000 {
div_clocks + self.ea_source_cycles(mode, Size::Word)
} else {
140
};
if m68000 {
self.top_up_prefetch(bus);
if div_clocks > 4 {
self.internal_cycles((div_clocks - 4) as u32);
self.flush_sync(bus);
}
}
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;
let m68000 = self.cpu_type == CpuType::M68000;
if src == 0 {
if m68000 {
self.internal_cycles(8);
}
return self.exception_zero_divide(bus);
}
let div_clocks = if m68000 {
divs_cycles(dst, src as i16)
} else {
158
};
let cycles = if m68000 {
div_clocks + self.ea_source_cycles(mode, Size::Word)
} else {
158
};
if m68000 {
self.top_up_prefetch(bus);
if div_clocks > 4 {
self.internal_cycles((div_clocks - 4) as u32);
self.flush_sync(bus);
}
}
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
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Eq)]
enum Event {
ReadWord(u32),
Sync(u32),
}
#[derive(Default)]
struct TraceBus {
events: Vec<Event>,
}
impl AddressBus for TraceBus {
fn read_byte(&mut self, _address: u32) -> u8 {
0
}
fn read_word(&mut self, address: u32) -> u16 {
self.events.push(Event::ReadWord(address));
0x4e71
}
fn read_long(&mut self, _address: u32) -> u32 {
0
}
fn write_byte(&mut self, _address: u32, _value: u8) {}
fn write_word(&mut self, _address: u32, _value: u16) {}
fn write_long(&mut self, _address: u32, _value: u32) {}
fn sync(&mut self, cpu_clocks: u32) {
self.events.push(Event::Sync(cpu_clocks));
}
}
fn m68000_cpu_with_one_prefetch_word() -> CpuCore {
let mut cpu = CpuCore::new();
cpu.set_cpu_type(CpuType::M68000);
cpu.pc = 0x2000;
cpu.prefetch_queue = [0x4e71, 0];
cpu.prefetch_count = 1;
cpu
}
#[test]
fn m68000_mulu_data_register_prefetches_before_multiplier_sync() {
let mut cpu = m68000_cpu_with_one_prefetch_word();
let mut bus = TraceBus::default();
cpu.dar[0] = 0x0000_0003;
cpu.dar[1] = 0x0000_0004;
let cycles = cpu.exec_mulu(&mut bus, AddressingMode::DataDirect(0), 1);
assert_eq!(cycles, 42);
assert_eq!(cpu.dar[1], 0x0000_000C);
assert_eq!(cpu.prefetch_count, 2);
assert_eq!(cpu.pending_sync_clocks, 0);
assert_eq!(bus.events, vec![Event::ReadWord(0x2002), Event::Sync(38)]);
}
#[test]
fn m68000_muls_data_register_prefetches_before_multiplier_sync() {
let mut cpu = m68000_cpu_with_one_prefetch_word();
let mut bus = TraceBus::default();
cpu.dar[0] = 0x0000_FFFF;
cpu.dar[1] = 0x0000_0002;
let cycles = cpu.exec_muls(&mut bus, AddressingMode::DataDirect(0), 1);
assert_eq!(cycles, 40);
assert_eq!(cpu.dar[1], 0xFFFF_FFFE);
assert_eq!(cpu.prefetch_count, 2);
assert_eq!(cpu.pending_sync_clocks, 0);
assert_eq!(bus.events, vec![Event::ReadWord(0x2002), Event::Sync(36)]);
}
}