//! CPU emulation core for the Motorola 68000.
//!
//! This module provides the main CPU emulation logic including:
//! - [`ConfiguredCore`] - The main CPU struct parameterized over memory and interrupt controller
//! - [`Core`] - Trait defining the CPU interface for instruction handlers
//! - [`Cycles`] - Wrapper type for cycle counting
//! - [`Exception`] - CPU exceptions (traps, interrupts, errors)
//! - [`ProcessingState`] - Current CPU execution state
//!
//! # Example
//!
//! ```rust
//! use r68k::cpu::ConfiguredCore;
//! use r68k::ram::PagedMem;
//! use r68k::interrupts::AutoInterruptController;
//!
//! let memory = PagedMem::new(0);
//! let mut cpu = ConfiguredCore::new_with(0, AutoInterruptController::new(), memory);
//! cpu.reset();
//! let cycles = cpu.execute(100);
//! ```

use std::result;

/// Result type for CPU operations that may raise exceptions.
pub type Result<T> = result::Result<T, Exception>;

use crate::interrupts::{InterruptController, AutoInterruptController, SPURIOUS_INTERRUPT};
use crate::ram::loggingmem::{LoggingMem, OpsLogger};

/// Test-only CPU configuration with logging memory. Not part of the public API.
#[cfg(test)]
pub type TestCore = ConfiguredCore<AutoInterruptController, LoggingMem<OpsLogger>>;

#[cfg(not(test))]
pub(crate) type TestCore = ConfiguredCore<AutoInterruptController, LoggingMem<OpsLogger>>;

/// Function signature for instruction handlers.
///
/// Each handler takes a mutable reference to a [`Core`] implementation
/// and returns the number of cycles consumed, or an exception.
pub type Handler<T> = fn(&mut T) -> Result<Cycles>;

/// The complete instruction set as a vector of 65536 handlers (one per opcode).
pub type InstructionSet<T> = Vec<Handler<T>>;

use crate::ram::{AddressBus, PagedMem, SUPERVISOR_PROGRAM, SUPERVISOR_DATA, USER_PROGRAM, USER_DATA};

/// A standard CPU configuration with paged memory and autovectored interrupts.
///
/// This is the most common configuration for basic 68000 emulation.
/// Use this type alias when you don't need custom memory or interrupt handling.
///
/// # Example
///
/// ```rust
/// use r68k::cpu::Cpu;
///
/// let mut cpu = Cpu::new(0);
/// cpu.reset();
/// cpu.execute(1000);
/// ```
pub type Cpu = ConfiguredCore<AutoInterruptController, PagedMem>;

impl Cpu {
    /// Creates a new CPU with paged memory and autovectored interrupts.
    ///
    /// This is a convenience constructor that creates a ready-to-use CPU
    /// with default memory (initialized to the given pattern) and
    /// standard autovectored interrupt handling.
    ///
    /// # Arguments
    ///
    /// * `initializer` - The 4-byte pattern for uninitialized memory (e.g., `0` or `0xDEADBEEF`)
    ///
    /// # Example
    ///
    /// ```rust
    /// use r68k::cpu::Cpu;
    ///
    /// // Create CPU with zeroed memory
    /// let mut cpu = Cpu::new(0);
    ///
    /// // Or with a debug pattern
    /// let mut debug_cpu = Cpu::new(0xDEADBEEF);
    ///
    /// cpu.reset();
    /// ```
    pub fn new(initializer: u32) -> Cpu {
        ConfiguredCore::new_with(0, AutoInterruptController::new(), PagedMem::new(initializer))
    }
}

pub mod ops;
mod effective_address;
mod operator;

/// Core CPU interface used by instruction handlers and exception callbacks.
///
/// This trait provides access to CPU registers, memory operations, and status flags.
/// It is implemented by [`ConfiguredCore`] and used internally by instruction handlers.
///
/// # For Callback Implementors
///
/// When implementing [`Callbacks`], you'll receive a `&mut impl Core`. The most useful
/// methods for callbacks are:
///
/// - **Register access**: [`pc()`](Self::pc), [`dar()`](Self::dar)
/// - **Status**: [`status_register()`](Self::status_register), [`condition_code_register()`](Self::condition_code_register)
/// - **Memory**: [`read_data_byte()`](Self::read_data_byte), [`write_data_byte()`](Self::write_data_byte), etc.
///
/// Most other methods are internal implementation details for instruction handlers.
///
/// # Register Layout
///
/// The 68000 has 8 data registers (D0-D7) and 8 address registers (A0-A7),
/// stored contiguously in the `dar` array:
/// - Indices 0-7: D0-D7 (data registers)
/// - Indices 8-15: A0-A7 (address registers)
/// - Index 15 (A7): Active stack pointer
///
/// # Memory Access
///
/// Memory operations are split by address space (program/data) and access mode (supervisor/user).
/// All memory reads return `u32` for uniformity; byte and word values are zero-extended.
pub trait Core {
    // === Primary methods for external use ===

    /// Returns a mutable reference to the program counter.
    fn pc(&mut self) -> &mut u32;

    /// Returns the current instruction register (opcode being executed).
    fn ir(&mut self) -> u16;

    /// Returns a mutable reference to all data and address registers.
    ///
    /// Layout: D0-D7 at indices 0-7, A0-A7 at indices 8-15.
    fn dar(&mut self) -> &mut [u32; 16];

    /// Returns the full 16-bit status register value.
    fn status_register(&self) -> u16;

    /// Returns the condition code register (lower 8 bits of SR).
    fn condition_code_register(&self) -> u16;

    /// Sets flags from a status register value.
    fn sr_to_flags(&mut self, sr: u16);

    /// Sets flags from a condition code register value.
    fn ccr_to_flags(&mut self, ccr: u16);

    // === Memory access methods ===

    /// Reads a byte from data space.
    fn read_data_byte(&mut self, address: u32) -> Result<u32>;
    /// Reads a word (16-bit) from data space.
    fn read_data_word(&mut self, address: u32) -> Result<u32>;
    /// Reads a long (32-bit) from data space.
    fn read_data_long(&mut self, address: u32) -> Result<u32>;
    /// Reads a byte from program space.
    fn read_program_byte(&mut self, address: u32) -> Result<u32>;
    /// Reads a word from program space.
    fn read_program_word(&mut self, address: u32) -> Result<u32>;
    /// Reads a long from program space.
    fn read_program_long(&mut self, address: u32) -> Result<u32>;
    /// Writes a byte to data space.
    fn write_data_byte(&mut self, address: u32, value: u32) -> Result<()>;
    /// Writes a word to data space.
    fn write_data_word(&mut self, address: u32, value: u32) -> Result<()>;
    /// Writes a long to data space.
    fn write_data_long(&mut self, address: u32, value: u32) -> Result<()>;
    /// Writes a byte to program space.
    fn write_program_byte(&mut self, address: u32, value: u32) -> Result<()>;
    /// Writes a word to program space.
    fn write_program_word(&mut self, address: u32, value: u32) -> Result<()>;
    /// Writes a long to program space.
    fn write_program_long(&mut self, address: u32, value: u32) -> Result<()>;

    // === Internal methods (used by instruction handlers) ===

    #[doc(hidden)]
    fn ax(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn ay(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn dx(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn dy(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn c_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn v_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn n_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn s_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn x_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn not_z_flag(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn x_flag_as_1(&self) -> u32;
    #[doc(hidden)]
    fn cond_t(&self) -> bool;
    #[doc(hidden)]
    fn cond_f(&self) -> bool;
    #[doc(hidden)]
    fn cond_hi(&self) -> bool;
    #[doc(hidden)]
    fn cond_ls(&self) -> bool;
    #[doc(hidden)]
    fn cond_cc(&self) -> bool;
    #[doc(hidden)]
    fn cond_cs(&self) -> bool;
    #[doc(hidden)]
    fn cond_ne(&self) -> bool;
    #[doc(hidden)]
    fn cond_eq(&self) -> bool;
    #[doc(hidden)]
    fn cond_vc(&self) -> bool;
    #[doc(hidden)]
    fn cond_vs(&self) -> bool;
    #[doc(hidden)]
    fn cond_pl(&self) -> bool;
    #[doc(hidden)]
    fn cond_mi(&self) -> bool;
    #[doc(hidden)]
    fn cond_ge(&self) -> bool;
    #[doc(hidden)]
    fn cond_lt(&self) -> bool;
    #[doc(hidden)]
    fn cond_gt(&self) -> bool;
    #[doc(hidden)]
    fn cond_le(&self) -> bool;
    #[doc(hidden)]
    fn branch_8(&mut self, offset: i8);
    #[doc(hidden)]
    fn branch_16(&mut self, offset: i16);
    #[doc(hidden)]
    fn read_imm_i16(&mut self) -> Result<i16>;
    #[doc(hidden)]
    fn read_imm_u16(&mut self) -> Result<u16>;
    #[doc(hidden)]
    fn read_imm_u32(&mut self) -> Result<u32>;
    #[doc(hidden)]
    fn jump(&mut self, pc: u32);
    #[doc(hidden)]
    fn push_32(&mut self, value: u32) -> u32;
    #[doc(hidden)]
    fn pop_32(&mut self) -> u32;
    #[doc(hidden)]
    fn push_16(&mut self, value: u16) -> u32;
    #[doc(hidden)]
    fn pop_16(&mut self) -> u16;
    #[doc(hidden)]
    fn push_sp(&mut self) -> u32;
    #[doc(hidden)]
    fn inactive_ssp(&self) -> u32;
    #[doc(hidden)]
    fn inactive_usp(&mut self) -> &mut u32;
    #[doc(hidden)]
    fn reset_external_devices(&mut self);
    #[doc(hidden)]
    fn resume_normal_processing(&mut self);
    #[doc(hidden)]
    fn stop_instruction_processing(&mut self);
    #[doc(hidden)]
    fn allow_tas_writeback(&mut self) -> bool;
}

/// The main 68000 CPU emulator, parameterized over memory and interrupt controller.
///
/// `ConfiguredCore` is the primary type for running 68000 code. It holds all CPU state
/// including registers, flags, and references to the memory system and interrupt controller.
///
/// # Type Parameters
///
/// - `T`: The interrupt controller type, implementing [`InterruptController`](crate::interrupts::InterruptController)
/// - `A`: The memory/address bus type, implementing [`AddressBus`](crate::ram::AddressBus)
///
/// # Example
///
/// ```rust
/// use r68k::cpu::ConfiguredCore;
/// use r68k::ram::PagedMem;
/// use r68k::interrupts::AutoInterruptController;
///
/// // Create CPU with paged memory and autovectored interrupts
/// let memory = PagedMem::new(0);
/// let mut cpu = ConfiguredCore::new_with(0, AutoInterruptController::new(), memory);
///
/// // Reset loads initial SSP and PC from vectors at address 0
/// cpu.reset();
///
/// // Execute for up to 1000 cycles
/// let cycles_used = cpu.execute(1000);
/// println!("Executed {} cycles, PC now at {:08X}", cycles_used.0, cpu.pc);
/// ```
///
/// # CPU State
///
/// The CPU state is exposed via public fields for inspection and modification:
/// - `pc`: Program counter
/// - `dar`: Data and address registers (D0-D7 at indices 0-7, A0-A7 at indices 8-15)
/// - `processing_state`: Current execution state
/// - `mem`: The memory system
/// - `int_ctrl`: The interrupt controller
pub struct ConfiguredCore<T: InterruptController, A: AddressBus> {
    /// Program counter - address of next instruction to fetch.
    pub pc: u32,
    /// Inactive supervisor stack pointer (stored when in user mode).
    pub inactive_ssp: u32,
    /// Inactive user stack pointer (stored when in supervisor mode).
    pub inactive_usp: u32,
    /// Instruction register - the currently executing opcode.
    pub ir: u16,
    /// Data and address registers. D0-D7 at indices 0-7, A0-A7 at indices 8-15.
    /// A7 (index 15) is the active stack pointer.
    pub dar: [u32; 16],
    instruction_set: InstructionSet<ConfiguredCore<T, A>>,
    /// Supervisor flag (non-zero = supervisor mode).
    pub s_flag: u32,
    /// Current interrupt request level being processed.
    pub irq_level: u8,
    /// Interrupt priority mask from status register.
    pub int_mask: u32,
    /// The interrupt controller.
    pub int_ctrl: T,
    /// Extend flag (used for multi-precision arithmetic).
    pub x_flag: u32,
    /// Carry flag.
    pub c_flag: u32,
    /// Overflow flag.
    pub v_flag: u32,
    /// Negative flag.
    pub n_flag: u32,
    /// Prefetch buffer address.
    pub prefetch_addr: u32,
    /// Prefetch buffer data.
    pub prefetch_data: u32,
    /// Zero flag (stored inverted: 0 means Z is set).
    pub not_z_flag: u32,
    /// Current processing state.
    pub processing_state: ProcessingState,
    /// The memory/address bus.
    pub mem: A,
    /// Cycle granularity for timing alignment.
    ///
    /// All cycle counts are rounded up to the nearest multiple of this value.
    /// Default is 1 (no rounding). Set to 4 for Atari ST compatibility where
    /// the 68000 bus is synchronized to a 4-cycle boundary.
    cycle_granularity: i32,
    /// Accumulated wait state cycles during current instruction execution.
    ///
    /// This is reset at the start of each instruction and added to the
    /// instruction's cycle count at the end.
    pending_wait_cycles: i32,
}
impl<T: InterruptController, A: AddressBus> Core for ConfiguredCore<T, A> {
    fn dar(&mut self) -> &mut [u32; 16] {
        &mut self.dar
    }
    fn pc(&mut self) -> &mut u32 {
        &mut self.pc
    }
    fn ir(&mut self) -> u16 {
        self.ir
    }
    fn ax(&mut self) -> &mut u32 {
        let ax = ir_ax!(self);
        &mut self.dar[ax]
    }
    fn ay(&mut self) -> &mut u32 {
        let ay = ir_ay!(self);
        &mut self.dar[ay]
    }
    fn dx(&mut self) -> &mut u32 {
        let dx = ir_dx!(self);
        &mut self.dar[dx]
    }
    fn dy(&mut self) -> &mut u32 {
        let dy = ir_dy!(self);
        &mut self.dar[dy]
    }
    fn c_flag(&mut self) -> &mut u32{
        &mut self.c_flag
    }
    fn v_flag(&mut self) -> &mut u32{
        &mut self.v_flag
    }
    fn n_flag(&mut self) -> &mut u32{
        &mut self.n_flag
    }
    fn s_flag(&mut self) -> &mut u32{
        &mut self.s_flag
    }
    fn x_flag(&mut self) -> &mut u32{
        &mut self.x_flag
    }
    fn not_z_flag(&mut self) -> &mut u32{
        &mut self.not_z_flag
    }
    fn x_flag_as_1(&self) -> u32 {
        self.x_flag_as_1()
    }
    fn read_data_byte(&mut self, address: u32) -> Result<u32> {
        self.read_data_byte(address)
    }
    fn read_data_word(&mut self, address: u32) -> Result<u32> {
        self.read_data_word(address)
    }
    fn read_data_long(&mut self, address: u32) -> Result<u32> {
        self.read_data_long(address)
    }
    fn read_program_byte(&mut self, address: u32) -> Result<u32> {
        self.read_program_byte(address)
    }
    fn read_program_word(&mut self, address: u32) -> Result<u32> {
        self.read_program_word(address)
    }
    fn read_program_long(&mut self, address: u32) -> Result<u32> {
        self.read_program_long(address)
    }
    fn write_data_byte(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_data_byte(address, value)
    }
    fn write_data_word(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_data_word(address, value)
    }
    fn write_data_long(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_data_long(address, value)
    }
    fn write_program_byte(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_program_byte(address, value)
    }
    fn write_program_word(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_program_word(address, value)
    }
    fn write_program_long(&mut self, address: u32, value: u32) -> Result<()> {
        self.write_program_long(address, value)
    }
    fn status_register(&self) -> u16 {
        self.status_register()
    }
    fn condition_code_register(&self) -> u16 {
        self.condition_code_register()
    }
    fn sr_to_flags(&mut self, sr: u16) {
        self.sr_to_flags(sr)
    }
    fn ccr_to_flags(&mut self, ccr: u16) {
        self.ccr_to_flags(ccr)
    }
    fn cond_t(&self) -> bool {
        true
    }
    fn cond_f(&self) -> bool {
        false
    }
    fn cond_hi(&self) -> bool {
        // high
        self.cond_cc() && self.cond_ne()
    }
    fn cond_ls(&self) -> bool {
        // low or same
        self.cond_cs() || self.cond_eq()
    }
    fn cond_cc(&self) -> bool {
        // carry clear (HI)
        self.c_flag & CFLAG_SET == 0
    }
    fn cond_cs(&self) -> bool {
        // carry set (LO)
        !self.cond_cc()
    }
    fn cond_eq(&self) -> bool {
        // equal
        self.not_z_flag == ZFLAG_SET
    }
    fn cond_ne(&self) -> bool {
        // not equal
        !self.cond_eq()
    }
    fn cond_vc(&self) -> bool {
        // overflow clear
        self.v_flag & VFLAG_SET == 0
    }
    fn cond_vs(&self) -> bool {
        // overflow set
        !self.cond_vc()
    }
    fn cond_pl(&self) -> bool {
        // plus
        self.n_flag & NFLAG_SET == 0
    }
    fn cond_mi(&self) -> bool {
        // minus
        !self.cond_pl()
    }
    fn cond_ge(&self) -> bool {
        // greater or equal
        self.cond_mi() && self.cond_vs() || self.cond_pl() && self.cond_vc()
    }
    fn cond_lt(&self) -> bool {
        // less than
        self.cond_mi() && self.cond_vc() || self.cond_pl() && self.cond_vs()
    }
    fn cond_gt(&self) -> bool {
        // greater than
        self.cond_ge() && self.cond_ne()
    }
    fn cond_le(&self) -> bool {
        // less or equal
        self.cond_lt() || self.cond_eq()
    }
    fn branch_8(&mut self, offset: i8) {
        self.pc = self.pc.wrapping_add(offset as u32);
    }
    fn branch_16(&mut self, offset: i16) {
        self.pc = self.pc.wrapping_add(offset as u32);
    }
    fn read_imm_i16(&mut self) -> Result<i16> {
        self.read_imm_i16()
    }
    fn read_imm_u16(&mut self) -> Result<u16> {
        self.read_imm_u16()
    }
    fn read_imm_u32(&mut self) -> Result<u32> {
        self.read_imm_u32()
    }
    fn jump(&mut self, pc: u32) {
        self.jump(pc)
    }
    fn push_32(&mut self, value: u32) -> u32 {
        self.push_32(value)
    }
    fn pop_32(&mut self) -> u32 {
        self.pop_32()
    }
    fn push_16(&mut self, value: u16) -> u32 {
        self.push_16(value)
    }
    fn pop_16(&mut self) -> u16 {
        self.pop_16()
    }
    fn push_sp(&mut self) -> u32 {
        self.push_sp()
    }
    fn inactive_ssp(&self) -> u32 {
        self.inactive_ssp
    }
    fn inactive_usp(&mut self) -> &mut u32 {
        &mut self.inactive_usp
    }
    fn reset_external_devices(&mut self) {
        self.int_ctrl.reset_external_devices();
        self.mem.reset_instruction();
    }
    fn resume_normal_processing(&mut self) {
        self.processing_state = ProcessingState::Normal;
    }
    fn stop_instruction_processing(&mut self) {
        self.processing_state = ProcessingState::Stopped;
    }
    fn allow_tas_writeback(&mut self) -> bool {
        true
    }
}
/// Index of the stack pointer register (A7) in the `dar` array.
pub const STACK_POINTER_REG: usize = 15;

/// Represents CPU cycle counts for timing-accurate emulation.
///
/// The 68000 executes instructions in a specific number of clock cycles.
/// This type wraps cycle counts and provides arithmetic operations.
///
/// # Example
///
/// ```rust
/// use r68k::cpu::Cycles;
///
/// let cycles = Cycles(100);
/// let remaining = cycles - Cycles(40);
/// assert_eq!(remaining.0, 60);
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cycles(pub i32);

use std::ops::Sub;
use std::ops::Add;
impl Sub for Cycles {
    type Output = Cycles;

    fn sub(self, _rhs: Cycles) -> Cycles {
        Cycles(self.0 - _rhs.0)
    }
}
impl Add for Cycles {
    type Output = Cycles;

    fn add(self, _rhs: Cycles) -> Cycles {
        Cycles(self.0 + _rhs.0)
    }
}
impl Cycles {
    fn any(self) -> bool {
        self.0 > 0
    }
}

/// Trait for intercepting CPU exceptions during execution.
///
/// Implement this trait to handle exceptions (traps, interrupts, errors) in custom ways.
/// This is useful for implementing OS traps, debuggers, or custom I/O handling.
///
/// # Example
///
/// ```rust
/// use r68k::cpu::{Callbacks, Core, Cycles, Exception, Result};
///
/// struct TrapHandler {
///     trap_count: usize,
/// }
///
/// impl Callbacks for TrapHandler {
///     fn exception_callback(&mut self, _core: &mut impl Core, ex: Exception) -> Result<Cycles> {
///         match ex {
///             Exception::Trap(num, _) => {
///                 self.trap_count += 1;
///                 // Handle the trap ourselves, consuming 40 cycles
///                 Ok(Cycles(40))
///             }
///             // Let the CPU handle all other exceptions normally
///             _ => Err(ex),
///         }
///     }
/// }
/// ```
pub trait Callbacks {
    /// Called when an exception occurs during instruction execution.
    ///
    /// Return `Ok(Cycles)` to handle the exception yourself (consuming the specified cycles),
    /// or `Err(ex)` to let the CPU handle it via its normal exception processing.
    fn exception_callback(&mut self, core: &mut impl Core, ex: Exception) -> Result<Cycles>;
}

struct EmulateAllExceptions;
impl Callbacks for EmulateAllExceptions {
    fn exception_callback(&mut self, _: &mut impl Core, ex: Exception) -> Result<Cycles> {
        Err(ex)
    }
}

/// The current execution state of the CPU.
///
/// The 68000 processes exceptions in groups with different priorities:
/// - **Group 0** (highest priority): Reset, Bus Error, Address Error
/// - **Group 1**: Trace, Interrupt, Illegal Instruction, Privilege Violation
/// - **Group 2**: TRAP, TRAPV, CHK, Zero Divide
///
/// The CPU can also be in special non-executing states (Stopped or Halted).
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ProcessingState {
    /// Normal instruction execution.
    Normal,
    /// Processing a Group 2 exception (TRAP, TRAPV, CHK, Zero Divide).
    Group2Exception,
    /// Processing a Group 1 exception (Trace, Interrupt, Illegal Instruction, Privilege Violation).
    Group1Exception,
    /// Processing a Group 0 exception (Address Error, Bus Error, Reset).
    Group0Exception,
    /// CPU is stopped via STOP instruction. Resumes on interrupt or reset.
    Stopped,
    /// CPU is halted due to double fault. Only external reset can resume.
    Halted,
}

impl ProcessingState {
    // The processor is processing an instruction if it is in the normal
    // state or processing a group 2 exception; the processor is not
    // processing an instruction if it is processing a group 0 or a group 1
    // exception. This info goes into a Group0 stack frame
    fn instruction_processing(self) -> bool {
        matches!(self, ProcessingState::Normal | ProcessingState::Group2Exception)
    }
    fn running(self) -> bool {
        !matches!(self, ProcessingState::Stopped | ProcessingState::Halted)
    }
}

/// Type of memory access that caused an exception.
#[derive(Clone, Copy, Debug)]
pub enum AccessType {
    /// Memory read operation.
    Read,
    /// Memory write operation.
    Write,
}
use crate::ram::AddressSpace;

/// CPU exceptions that can occur during instruction execution.
///
/// Exceptions are the 68000's mechanism for handling errors, traps, and interrupts.
/// When an exception occurs, the CPU saves state to the stack and vectors to a handler.
///
/// # Exception Vectors
///
/// Each exception type has an associated vector number. The vector address is
/// calculated as `vector_number * 4`, pointing to the exception handler address
/// in the vector table at the start of memory.
#[derive(Clone, Copy, Debug)]
pub enum Exception {
    /// Address error: word/long access at odd address.
    ///
    /// Contains the faulting address, access type, CPU state, and address space.
    AddressError {
        /// The odd address that caused the error.
        address: u32,
        /// Whether the access was a read or write.
        access_type: AccessType,
        /// CPU state when the error occurred.
        processing_state: ProcessingState,
        /// The address space (user/supervisor, program/data).
        address_space: AddressSpace,
    },
    /// Illegal instruction encountered.
    ///
    /// Fields: (instruction_register, program_counter)
    IllegalInstruction(u16, u32),
    /// TRAP instruction executed.
    ///
    /// Fields: (trap_vector, cycles_for_ea_calculation)
    Trap(u8, i32),
    /// Privileged instruction executed in user mode.
    ///
    /// Fields: (instruction_register, program_counter)
    PrivilegeViolation(u16, u32),
    /// Unimplemented instruction (A-line or F-line).
    ///
    /// Fields: (instruction_register, program_counter, vector_number)
    UnimplementedInstruction(u16, u32, u8),
    /// Hardware interrupt.
    ///
    /// Fields: (interrupt_level, vector_number)
    Interrupt(u8, u8),
}
use std::fmt;
impl fmt::Display for Exception {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Exception::AddressError {
                address, access_type, processing_state, address_space
                } => write!(f, "Address Error: {access_type:?} {address_space:?} at {address:08x} during {processing_state:?} processing"),
            Exception::IllegalInstruction(ir, pc) => write!(f, "Illegal Instruction {ir:04x} at {pc:08x}"),
            Exception::Trap(num, ea_cyc) => write!(f, "Trap: {num:02x} (ea cyc {ea_cyc})"),
            Exception::PrivilegeViolation(ir, pc) => write!(f, "Privilege Violation {ir:04x} at {pc:08x}"),
            Exception::UnimplementedInstruction(ir, pc, _) => write!(f, "Unimplemented Instruction {ir:04x} at {pc:08x}"),
            Exception::Interrupt(irq, vec) => write!(f, "Interrupt {irq:1x} (vector {vec:02x})"),
        }
    }
}
use std::error;
impl error::Error for Exception {
    fn description(&self) -> &str {
         match *self {
            Exception::AddressError{..} => "Address Error",
            Exception::IllegalInstruction(_, _) => "Illegal Instruction",
            Exception::Trap(_, _) => "Trap",
            Exception::PrivilegeViolation(_, _) => "PrivilegeViolation",
            Exception::UnimplementedInstruction(_, _, _) => "UnimplementedInstruction",
            Exception::Interrupt(_, _) => "Interrupt",
         }
    }
    fn cause(&self) -> Option<&dyn error::Error> {
        None
    }
}
use std::num::Wrapping;

// these values are borrowed from Musashi
// and not yet fully understood
const SFLAG_SET: u32 =  0x04;
const XFLAG_SET: u32 = 0x100;
const ZFLAG_SET: u32 = 0x00;
const NFLAG_SET: u32 =  0x80;
const VFLAG_SET: u32 =  0x80;
const CFLAG_SET: u32 = 0x100;
const CPU_SR_MASK: u16 = 0xa71f; /* T1 -- S  -- -- I2 I1 I0 -- -- -- X  N  Z  V  C  */
const CPU_SR_INT_MASK: u32 = 0x0700;

const VFLAG_CLEAR: u32 =  0x00;
const XFLAG_CLEAR: u32 =  0x00;
const NFLAG_CLEAR: u32 =  0x00;
const CFLAG_CLEAR: u32 =  0x00;
const SFLAG_CLEAR: u32 =  0x00;
const ZFLAG_CLEAR: u32 =  0xffff_ffff; // used as "non-z-flag"

// Exception Vectors
//pub const EXCEPTION_BUS_ERROR: u8               =  2;
pub const EXCEPTION_ADDRESS_ERROR: u8           =  3;
pub const EXCEPTION_ILLEGAL_INSTRUCTION: u8     =  4;
pub const EXCEPTION_ZERO_DIVIDE: u8             =  5;
pub const EXCEPTION_CHK: u8                     =  6;
pub const EXCEPTION_TRAPV: u8                   =  7;
pub const EXCEPTION_PRIVILEGE_VIOLATION: u8     =  8;
// pub const EXCEPTION_TRACE: u8                   =  9;
pub const EXCEPTION_UNIMPLEMENTED_1010: u8      = 10;
pub const EXCEPTION_UNIMPLEMENTED_1111: u8      = 11;
// pub const EXCEPTION_FORMAT_ERROR: u8            = 14;
// pub const EXCEPTION_UNINITIALIZED_INTERRUPT: u8 = 15;
// pub const EXCEPTION_SPURIOUS_INTERRUPT: u8      = 24;
// pub const EXCEPTION_INTERRUPT_AUTOVECTOR: u8    = 24;
pub const EXCEPTION_TRAP_BASE: u8               = 32;

impl TestCore {
    pub fn new(base: u32) -> TestCore {
        TestCore {
            pc: base, prefetch_addr: 0, prefetch_data: 0, inactive_ssp: 0, inactive_usp: 0, ir: 0, processing_state: ProcessingState::Group0Exception,
            dar: [0u32; 16], mem: LoggingMem::new(0xaaaa_aaaa, OpsLogger::new()), instruction_set: ops::instruction_set(),
            irq_level: 0, int_ctrl: AutoInterruptController::new(),
            s_flag: SFLAG_SET, int_mask: CPU_SR_INT_MASK, x_flag: 0, v_flag: 0, c_flag: 0, n_flag: 0, not_z_flag: 0xffff_ffff,
            cycle_granularity: 1, pending_wait_cycles: 0,
        }
    }
    pub fn new_auto() -> TestCore {
        TestCore::new_with(0, AutoInterruptController::new(), LoggingMem::new(0xaaaa_aaaa, OpsLogger::new()))
    }
    pub fn new_mem(base: u32, contents: &[u8]) -> TestCore {
        TestCore::new_mem_init(base, contents, 0xaaaa_aaaa)
    }
    pub fn new_mem_init(base: u32, contents: &[u8], initializer: u32) -> TestCore {
        let mut lm = LoggingMem::new(initializer, OpsLogger::new());
        for (offset, byte) in contents.iter().enumerate() {
            lm.write_u8(base + offset as u32, u32::from(*byte));
        }
        TestCore {
            pc: base, prefetch_addr: 0, prefetch_data: 0, inactive_ssp: 0, inactive_usp: 0, ir: 0, processing_state: ProcessingState::Normal,
            dar: [0u32; 16], mem: lm, instruction_set: ops::instruction_set(),
            irq_level: 0, int_ctrl: AutoInterruptController::new(),
            s_flag: SFLAG_SET, int_mask: CPU_SR_INT_MASK, x_flag: 0, v_flag: 0, c_flag: 0, n_flag: 0, not_z_flag: 0xffff_ffff,
            cycle_granularity: 1, pending_wait_cycles: 0,
        }
    }
}

impl<T: InterruptController, A: AddressBus> ConfiguredCore<T, A> {
    /// Creates a new CPU with the given interrupt controller and memory.
    ///
    /// The CPU starts in supervisor mode with an uninitialized state.
    /// Call [`reset()`](Self::reset) to properly initialize the CPU by loading
    /// the initial stack pointer and program counter from the reset vectors.
    ///
    /// # Arguments
    ///
    /// * `base` - Initial program counter value (typically 0, will be overwritten by reset)
    /// * `int_ctrl` - The interrupt controller to use
    /// * `memory` - The memory/address bus implementation
    ///
    /// # Example
    ///
    /// ```rust
    /// use r68k::cpu::ConfiguredCore;
    /// use r68k::ram::PagedMem;
    /// use r68k::interrupts::AutoInterruptController;
    ///
    /// let memory = PagedMem::new(0);
    /// let mut cpu = ConfiguredCore::new_with(0, AutoInterruptController::new(), memory);
    /// cpu.reset();
    /// ```
    pub fn new_with(base: u32, int_ctrl: T, memory: A) -> ConfiguredCore<T, A> {
        ConfiguredCore {
            pc: base, prefetch_addr: 0, prefetch_data: 0, inactive_ssp: 0, inactive_usp: 0, ir: 0, processing_state: ProcessingState::Group0Exception,
            dar: [0u32; 16], mem: memory, instruction_set: ops::instruction_set(),
            irq_level: 0, int_ctrl,
            s_flag: SFLAG_SET, int_mask: CPU_SR_INT_MASK, x_flag: 0, v_flag: 0, c_flag: 0, n_flag: 0, not_z_flag: 0xffff_ffff,
            cycle_granularity: 1, pending_wait_cycles: 0,
        }
    }

    /// Sets the cycle granularity for timing alignment.
    ///
    /// All instruction cycle counts will be rounded up to the nearest multiple
    /// of the granularity value. This is useful for emulating systems where the
    /// 68000 bus is synchronized to a specific clock boundary.
    ///
    /// # Arguments
    ///
    /// * `granularity` - The cycle alignment boundary (must be >= 1)
    ///
    /// # Common Values
    ///
    /// * `1` - No rounding (default, standard 68000 behavior)
    /// * `4` - Atari ST compatibility (bus synchronized to 4-cycle boundary)
    ///
    /// # Example
    ///
    /// ```rust
    /// use r68k::cpu::Cpu;
    ///
    /// let mut cpu = Cpu::new(0);
    ///
    /// // Configure for Atari ST timing
    /// cpu.set_cycle_granularity(4);
    ///
    /// cpu.reset();
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if `granularity` is less than 1.
    pub fn set_cycle_granularity(&mut self, granularity: i32) {
        assert!(granularity >= 1, "cycle granularity must be >= 1");
        self.cycle_granularity = granularity;
    }

    /// Returns the current cycle granularity setting.
    pub fn cycle_granularity(&self) -> i32 {
        self.cycle_granularity
    }

    /// Rounds a cycle count up to the configured granularity boundary.
    #[inline]
    fn align_cycles(&self, cycles: Cycles) -> Cycles {
        if self.cycle_granularity == 1 {
            cycles
        } else {
            // Round up: (n + g - 1) / g * g
            let n = cycles.0;
            let g = self.cycle_granularity;
            Cycles(((n + g - 1) / g) * g)
        }
    }

    /// Performs a CPU reset, loading initial SSP and PC from the vector table.
    ///
    /// This simulates a hardware reset:
    /// 1. Enters supervisor mode
    /// 2. Sets interrupt mask to highest priority
    /// 3. Reads initial SSP from address 0x000000
    /// 4. Reads initial PC from address 0x000004
    /// 5. Enters normal processing state
    ///
    /// The memory must contain valid reset vectors at addresses 0-7.
    pub fn reset(&mut self) {
        self.processing_state = ProcessingState::Group0Exception;
        self.s_flag = SFLAG_SET;
        self.int_mask = CPU_SR_INT_MASK;
        self.prefetch_addr = 1; // non-zero, or the prefetch won't kick in
        self.jump(0);
        // these reads cannot possibly cause AddressError, as we forced PC to 0
        self.dar[15] = self.read_imm_u32().unwrap();
        let new_pc = self.read_imm_u32().unwrap();
        self.jump(new_pc);
        self.processing_state = ProcessingState::Normal;
    }
    /// Returns the extend flag as 0 or 1.
    pub fn x_flag_as_1(&self) -> u32 {
        (self.x_flag>>8)&1
    }

    /// Returns the full 16-bit status register value.
    ///
    /// The status register contains:
    /// - Bits 15-13: Trace mode (T1, T0) and reserved
    /// - Bit 13: Supervisor mode (S)
    /// - Bits 10-8: Interrupt priority mask (I2, I1, I0)
    /// - Bit 4: Extend flag (X)
    /// - Bit 3: Negative flag (N)
    /// - Bit 2: Zero flag (Z)
    /// - Bit 1: Overflow flag (V)
    /// - Bit 0: Carry flag (C)
    pub fn status_register(&self) -> u16 {
        ((self.s_flag << 11)                |
        self.int_mask                        |
        ((self.x_flag & XFLAG_SET) >> 4)    |
        ((self.n_flag & NFLAG_SET) >> 4)    |
        ((not1!(self.not_z_flag))  << 2)    |
        ((self.v_flag & VFLAG_SET) >> 6)    |
        ((self.c_flag & CFLAG_SET) >> 8)) as u16
    }
    /// Returns the condition code register (lower 8 bits of status register).
    ///
    /// Contains: X, N, Z, V, C flags.
    pub fn condition_code_register(&self) -> u16 {
        self.status_register() & 0xff
    }

    /// Returns the current user stack pointer value.
    ///
    /// In supervisor mode, returns the saved USP. In user mode, returns A7.
    pub fn usp(&self) -> u32 {
        if self.s_flag > 0 {
            self.inactive_usp
        } else {
            self.dar[15]
        }
    }

    /// Returns the current supervisor stack pointer value.
    ///
    /// In supervisor mode, returns A7. In user mode, returns the saved SSP.
    pub fn ssp(&self) -> u32 {
        if self.s_flag > 0 {
            self.dar[15]
        } else {
            self.inactive_ssp
        }
    }

    /// Sets CPU flags from a status register value.
    ///
    /// This updates all flags and the interrupt mask from the given SR value.
    /// If the supervisor bit changes, the stack pointers are swapped.
    pub fn sr_to_flags(&mut self, sr: u16) {
        let sr = u32::from(sr & CPU_SR_MASK);
        let old_sflag = self.s_flag;
        self.int_mask = sr & CPU_SR_INT_MASK;
        self.s_flag =           (sr >> 11) & SFLAG_SET;
        self.x_flag =            (sr <<  4) & XFLAG_SET;
        self.n_flag =            (sr <<  4) & NFLAG_SET;
        self.not_z_flag = not1!(sr & 0b00100);
        self.v_flag =            (sr <<  6) & VFLAG_SET;
        self.c_flag =            (sr <<  8) & CFLAG_SET;
        if old_sflag != self.s_flag {
            if self.s_flag == SFLAG_SET {
                self.inactive_usp = self.dar[15];
                self.dar[15] = self.inactive_ssp;
            } else {
                self.inactive_ssp = self.dar[15];
                self.dar[15] = self.inactive_usp;
            }
        }
        // println!("{} {:016b} {} {}", self.flags(), sr, self.not_z_flag, sr & 0b00100);
    }
    /// Sets CPU flags from a condition code register value.
    ///
    /// Only updates the lower 8 bits (X, N, Z, V, C), preserving the upper byte.
    pub fn ccr_to_flags(&mut self, ccr: u16) {
        let sr = self.status_register();
        self.sr_to_flags((sr & 0xff00) | (ccr & 0xff));
    }

    /// Returns a human-readable string representation of the current flags.
    ///
    /// Format: `-S7XNZVC` where:
    /// - S/U: Supervisor/User mode
    /// - 0-7: Interrupt priority mask
    /// - X, N, Z, V, C: Flag letters if set, `-` if clear
    ///
    /// Example: `-S7X-Z--` means supervisor mode, mask 7, X and Z flags set.
    pub fn flags(&self) -> String {
        let sr = self.status_register();
        let supervisor = (sr >> 13) & 1;
        let irq_mask = (0x700 & sr) >> 8;

        format!("-{}{}{}{}{}{}{}",
        if supervisor > 0 {'S'} else {'U'},
        irq_mask,
        if 0 < (sr >> 4) & 1 {'X'} else {'-'},
        if 0 < (sr >> 3) & 1 {'N'} else {'-'},
        if 0 < (sr >> 2) & 1 {'Z'} else {'-'},
        if 0 < (sr >> 1) & 1 {'V'} else {'-'},
        if 0 < (sr     ) & 1 {'C'} else {'-'})
    }
    fn prefetch_if_needed(&mut self) -> bool {
        // does current PC overlap with fetched data
        let fetched = if self.pc & !3 != self.prefetch_addr {
            self.prefetch_addr = self.pc & !3;
            let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
            self.pending_wait_cycles += self.mem.wait_cycles(self.prefetch_addr, 4, false);
            self.prefetch_data = self.mem.read_long(address_space, self.prefetch_addr);
            true
        } else {
            false
        };
        self.pc = self.pc.wrapping_add(2);
        fetched
    }
    pub fn read_imm_u32(&mut self) -> Result<u32> {
        if self.pc & 1 > 0 {
            let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
            return Err(Exception::AddressError{address: self.pc, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        }
        self.prefetch_if_needed();
        let prev_prefetch_data = self.prefetch_data;
        Ok(if self.prefetch_if_needed() {
            (prev_prefetch_data << 16) | (self.prefetch_data >> 16)
        } else {
            prev_prefetch_data
        })
    }
    pub fn read_imm_i16(&mut self) -> Result<i16> {
        self.read_imm_u16().map(|val| val as i16)
    }
    pub fn read_imm_u16(&mut self) -> Result<u16> {
        // the Musashi read_imm_16 calls cpu_read_long as part of prefetch
        if self.pc & 1 > 0 {
            let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
            return Err(Exception::AddressError{address: self.pc, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        }
        self.prefetch_if_needed();
        Ok(((self.prefetch_data >> ((2 - ((self.pc.wrapping_sub(2)) & 2))<<3)) & 0xffff) as u16)
    }
    pub fn push_sp(&mut self) -> u32 {
         let new_sp = (Wrapping(self.dar[15]) - Wrapping(4)).0;
         self.dar[15] = new_sp;
         self.write_data_long(new_sp, new_sp).unwrap();
         new_sp
    }
    pub fn push_32(&mut self, value: u32) -> u32 {
         let new_sp = (Wrapping(self.dar[15]) - Wrapping(4)).0;
         self.dar[15] = new_sp;
         self.write_data_long(new_sp, value).unwrap();
         new_sp
    }
    pub fn pop_32(&mut self) -> u32 {
        let sp = self.dar[15];
        let data = self.read_data_long(sp).unwrap();
        self.dar[15] = sp.wrapping_add(4);
        data
    }
    pub fn push_16(&mut self, value: u16) -> u32 {
         let new_sp = (Wrapping(self.dar[15]) - Wrapping(2)).0;
         self.dar[15] = new_sp;
         self.write_data_word(new_sp, u32::from(value)).unwrap();
         new_sp
    }
    pub fn pop_16(&mut self) -> u16 {
        let sp = self.dar[15];
        let data = self.read_data_word(sp).unwrap() as u16;
        self.dar[15] = sp.wrapping_add(2);
        data
    }
    pub fn read_data_byte(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        self.pending_wait_cycles += self.mem.wait_cycles(address, 1, false);
        Ok(self.mem.read_byte(address_space, address))
    }
    pub fn read_program_byte(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        self.pending_wait_cycles += self.mem.wait_cycles(address, 1, false);
        Ok(self.mem.read_byte(address_space, address))
    }
    pub fn write_data_byte(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        self.pending_wait_cycles += self.mem.wait_cycles(address, 1, true);
        self.mem.write_byte(address_space, address, value);
        Ok(())
    }
    pub fn write_program_byte(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        self.pending_wait_cycles += self.mem.wait_cycles(address, 1, true);
        self.mem.write_byte(address_space, address, value);
        Ok(())
    }
    pub fn read_data_word(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 2, false);
            Ok(self.mem.read_word(address_space, address))
        }
    }
    pub fn read_program_word(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        if address & 1 > 0 {
            Err(Exception::AddressError {address, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 2, false);
            Ok(self.mem.read_word(address_space, address))
        }
    }
    pub fn write_data_word(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Write, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 2, true);
            self.mem.write_word(address_space, address, value);
            Ok(())
        }
    }
    pub fn write_program_word(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Write, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 2, true);
            self.mem.write_word(address_space, address, value);
            Ok(())
        }
    }
    pub fn read_data_long(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 4, false);
            Ok(self.mem.read_long(address_space, address))
        }
    }
    pub fn read_program_long(&mut self, address: u32) -> Result<u32> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Read, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 4, false);
            Ok(self.mem.read_long(address_space, address))
        }
    }
    pub fn write_data_long(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_DATA} else {USER_DATA};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Write, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 4, true);
            self.mem.write_long(address_space, address, value);
            Ok(())
        }
    }
    pub fn write_program_long(&mut self, address: u32, value: u32) -> Result<()> {
        let address_space = if self.s_flag != 0 {SUPERVISOR_PROGRAM} else {USER_PROGRAM};
        if address & 1 > 0 {
            Err(Exception::AddressError{address, access_type: AccessType::Write, address_space, processing_state: self.processing_state})
        } else {
            self.pending_wait_cycles += self.mem.wait_cycles(address, 4, true);
            self.mem.write_long(address_space, address, value);
            Ok(())
        }
    }
    pub fn jump(&mut self, pc: u32) {
        self.pc = pc;
    }
    pub fn jump_vector(&mut self, vector: u8) {
        let vector_address = u32::from(vector) << 2;
        self.pc = self.read_data_long(vector_address).unwrap();
    }
    pub fn ensure_supervisor_mode(&mut self) -> u16 {
        let backup_sr = self.status_register();
        // if in user mode, swap stack pointers!
        if self.s_flag == SFLAG_CLEAR {
            self.inactive_usp = self.dar[15];
            self.dar[15] = self.inactive_ssp;
        }
        // enter supervisor mode
        self.s_flag = SFLAG_SET;
        backup_sr
    }
    pub fn handle_address_error(&mut self, bad_address: u32, access_type: AccessType, processing_state: ProcessingState, address_space: AddressSpace) -> Cycles
    {
        if processing_state == ProcessingState::Group0Exception {
            self.processing_state = ProcessingState::Halted;
            return Cycles(0);
        }
        self.processing_state = ProcessingState::Group0Exception;
        let backup_sr = self.ensure_supervisor_mode();

        // Bus error stack frame (68000 only).
        let (pc, ir) = (self.pc, self.ir);
        self.push_32(pc);
        self.push_16(backup_sr);
        self.push_16(ir);
        self.push_32(bad_address);    /* access address */
        /* 0 0 0 0 0 0 0 0 0 0 0 R/W I/N FC
         * R/W  0 = write, 1 = read
         * I/N  0 = instruction, 1 = not
         * FC   3-bit function code
         */
        let access_info = match access_type {AccessType::Read => 0b10000, _ => 0 } |
            if processing_state.instruction_processing() { 0 } else { 0b01000 } |
            (address_space.fc() as u16);
        self.push_16(access_info);
        self.jump_vector(EXCEPTION_ADDRESS_ERROR);
        Cycles(50)
    }
    pub fn handle_unimplemented_instruction(&mut self, pc: u32, vector: u8) -> Cycles {
        // somewhat unclear if the unimplemented instruction exceptions
        // are Group 1 or 2 exceptions. They are mentioned together with
        // "illegal instruction" which is clearly defined as a group 1
        // exception,  but the text ("6.3.6 Illegal and Unimplemented
        // Instructions" in the M68000UM) mentions that illegal
        // instructions push a group 2 stack frame. On the 68000 G1 and
        // G2 exception stack frames are identical, so maybe it doesn't
        // really matter. EASy68k considers them group 2 exceptions. For
        // the time being, we do too.
        self.handle_exception(ProcessingState::Group2Exception, pc, vector, 34)
    }
    pub fn handle_illegal_instruction(&mut self, pc: u32) -> Cycles {
        self.handle_exception(ProcessingState::Group1Exception, pc, EXCEPTION_ILLEGAL_INSTRUCTION, 34)
    }
    pub fn handle_privilege_violation(&mut self, pc: u32) -> Cycles {
        self.handle_exception(ProcessingState::Group1Exception, pc, EXCEPTION_PRIVILEGE_VIOLATION, 34)
    }
    pub fn handle_trap(&mut self, trap: u8, cycles: i32) -> Cycles {
        let pc = self.pc;
        self.handle_exception(ProcessingState::Group2Exception, pc, trap, cycles)
    }

    pub fn handle_exception(&mut self, new_state: ProcessingState, pc: u32, vector: u8, cycles: i32) -> Cycles {
        self.processing_state = new_state;
        let backup_sr = self.ensure_supervisor_mode();

        // Group 1 and 2 stack frame (68000 only).
        self.push_32(pc);
        self.push_16(backup_sr);

        self.jump_vector(vector);
        Cycles(cycles)
    }

    pub fn handle_interrupt(&mut self, irq_level: u8, vector: u8) -> Cycles {
        let pc = self.pc;
        self.processing_state = ProcessingState::Group1Exception;
        let backup_sr = self.ensure_supervisor_mode();
        // new mask set here, in order to exclude from backup_sr
        self.int_mask = u32::from(irq_level) << 8;
        self.irq_level = irq_level;

        // Musashi jumps first, and stacks later for interrupts,
        // but the other way around for exceptions
        self.jump_vector(vector);

        // Group 1 and 2 stack frame (68000 only).
        self.push_32(pc);
        self.push_16(backup_sr);

        // 44 cycles for an interrupt according to MC68000UM, Table 8-14
        // The interrupt acknowledge cycle is assumed to take four clock periods
        Cycles(44)
    }
    fn stopped_with_pending_interrups(&mut self) -> bool {
        self.processing_state == ProcessingState::Stopped && self.pending_interrupt().is_some()
    }
    fn can_execute(&mut self) -> bool {
        self.processing_state.running() || self.stopped_with_pending_interrups()
    }
    fn pending_interrupt(&self) -> Option<u8> {
        let old_level = self.irq_level;
        let new_level = self.int_ctrl.highest_priority();
        let edge_triggered_nmi = old_level != 7 && new_level == 7;
        if u32::from(new_level) << 8 > self.int_mask || edge_triggered_nmi {
            Some(new_level)
        } else {
            None
        }
    }
    pub fn read_instruction(&mut self) -> Result<u16> {
        // first check for interrupts
        if let Some(irq) = self.pending_interrupt() {
            let vector = self.int_ctrl.acknowledge_interrupt(irq).unwrap_or(SPURIOUS_INTERRUPT);
            Err(Exception::Interrupt(irq, vector))
        } else {
            // not interrupted, read instruction from PC
            self.read_imm_u16()
        }
    }
    /// Executes a single instruction.
    ///
    /// This is a convenience method equivalent to `execute(1)`.
    /// Returns the number of cycles consumed by the instruction.
    pub fn execute1(&mut self) -> Cycles {
        self.execute(1)
    }

    /// Executes instructions for up to the specified number of cycles.
    ///
    /// The CPU will execute instructions until the cycle budget is exhausted
    /// or the CPU enters a non-running state (Stopped or Halted).
    ///
    /// # Arguments
    ///
    /// * `cycles` - Maximum number of cycles to execute
    ///
    /// # Returns
    ///
    /// The actual number of cycles consumed. This may exceed the requested
    /// amount if the last instruction takes more cycles than remaining.
    ///
    /// # Example
    ///
    /// ```rust
    /// use r68k::cpu::ConfiguredCore;
    /// use r68k::ram::PagedMem;
    /// use r68k::interrupts::AutoInterruptController;
    ///
    /// let memory = PagedMem::new(0);
    /// let mut cpu = ConfiguredCore::new_with(0, AutoInterruptController::new(), memory);
    /// cpu.reset();
    ///
    /// // Execute for approximately 1000 cycles
    /// let actual_cycles = cpu.execute(1000);
    /// println!("Consumed {} cycles", actual_cycles.0);
    /// ```
    pub fn execute(&mut self, cycles: i32) -> Cycles {
        self.execute_with_state(cycles, &mut EmulateAllExceptions)
    }

    /// Executes instructions with custom exception handling.
    ///
    /// Like [`execute()`](Self::execute), but allows intercepting exceptions
    /// via a [`Callbacks`] implementation. This is useful for implementing
    /// OS traps, debuggers, or custom I/O.
    ///
    /// # Arguments
    ///
    /// * `cycles` - Maximum number of cycles to execute
    /// * `state` - A [`Callbacks`] implementation for exception handling
    ///
    /// # Example
    ///
    /// ```rust
    /// use r68k::cpu::{ConfiguredCore, Callbacks, Core, Cycles, Exception, Result};
    /// use r68k::ram::PagedMem;
    /// use r68k::interrupts::AutoInterruptController;
    ///
    /// struct MyHandler;
    /// impl Callbacks for MyHandler {
    ///     fn exception_callback(&mut self, _: &mut impl Core, ex: Exception) -> Result<Cycles> {
    ///         Err(ex) // Let CPU handle all exceptions
    ///     }
    /// }
    ///
    /// let memory = PagedMem::new(0);
    /// let mut cpu = ConfiguredCore::new_with(0, AutoInterruptController::new(), memory);
    /// cpu.reset();
    ///
    /// let mut handler = MyHandler;
    /// cpu.execute_with_state(1000, &mut handler);
    /// ```
    pub fn execute_with_state<S: Callbacks>(&mut self, cycles: i32, state: &mut S) -> Cycles {
        let cycles = Cycles(cycles);
        let mut remaining_cycles = cycles;
        while remaining_cycles.any() && self.can_execute() {
            // Reset wait state accumulator for this instruction
            self.pending_wait_cycles = 0;
            // Read an instruction from PC (increments PC by 2)
            let result = self.read_instruction().and_then(|opcode| {
                    self.ir = opcode;
                    // Call instruction handler to mutate Core accordingly
                    self.instruction_set[opcode as usize](self)
                });
            let cycles_used = match result {
                Ok(cycles_used) => cycles_used,
                Err(ex) => {
                    match state.exception_callback(self, ex) {
                        Ok(cycles_used) => cycles_used,
                        Err(Exception::AddressError { address, access_type, processing_state, address_space }) =>
                            self.handle_address_error(address, access_type, processing_state, address_space),
                        Err(Exception::IllegalInstruction(_, pc)) =>
                            self.handle_illegal_instruction(pc),
                        Err(Exception::UnimplementedInstruction(_, pc, vector)) =>
                            self.handle_unimplemented_instruction(pc, vector),
                        Err(Exception::Trap(num, ea_calculation_cycles)) =>
                            self.handle_trap(num, ea_calculation_cycles),
                        Err(Exception::PrivilegeViolation(_, pc)) =>
                            self.handle_privilege_violation(pc),
                        Err(Exception::Interrupt(irq, vec)) =>
                            self.handle_interrupt(irq, vec),
                    }
                }
            };
            // Add accumulated wait state cycles and apply granularity alignment
            let total_cycles = Cycles(cycles_used.0 + self.pending_wait_cycles);
            remaining_cycles = remaining_cycles - self.align_cycles(total_cycles);
        }
        if self.processing_state.running() {
            cycles - remaining_cycles
        } else {
            // if not running, consume all available cycles
            // including overconsumed cycles
            let adjust = if remaining_cycles.0 < 0 { remaining_cycles } else { Cycles(0) };
            cycles - adjust
        }
    }
}

impl Clone for TestCore {
    fn clone(&self) -> Self {
        let mut lm = LoggingMem::new(self.mem.initializer, OpsLogger::new());
        lm.copy_from(&self.mem);
        assert_eq!(0, lm.logger.len());
        TestCore {
            pc: self.pc, prefetch_addr: 0, prefetch_data: 0, inactive_ssp: self.inactive_ssp, inactive_usp: self.inactive_usp, ir: self.ir, processing_state: self.processing_state,
            dar: self.dar, mem: lm, instruction_set: ops::instruction_set(),
            irq_level: 0, int_ctrl: AutoInterruptController::new(),
            s_flag: self.s_flag, int_mask: self.int_mask, x_flag: self.x_flag, v_flag: self.v_flag, c_flag: self.c_flag, n_flag: self.n_flag, not_z_flag: self.not_z_flag,
            cycle_granularity: self.cycle_granularity, pending_wait_cycles: 0,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{TestCore, Cycles};
    use super::ops; //::instruction_set;
    use crate::ram::{AddressBus, SUPERVISOR_PROGRAM, USER_PROGRAM, USER_DATA};
    use crate::ram::loggingmem::Operation;
    use crate::cpu::ops::opcodes;

    #[test]
    fn new_sets_pc() {
        let cpu = TestCore::new(256);
        assert_eq!(256, cpu.pc);
    }

    #[test]
    fn new_mem_sets_pc_and_mem() {
        let base = 128;
        let cpu = TestCore::new_mem(base, &[1u8, 2u8, 3u8, 4u8, 5u8, 6u8]);
        assert_eq!(128, cpu.pc);
        assert_eq!(1, cpu.mem.read_byte(SUPERVISOR_PROGRAM, 128));
        assert_eq!(2, cpu.mem.read_byte(SUPERVISOR_PROGRAM, 129));
    }

    #[test]
    fn a_jump_changes_pc() {
        let mut cpu = TestCore::new(0);
        cpu.jump(128);
        assert_eq!(128, cpu.pc);
    }

    #[test]
    #[allow(unused_must_use)]
    fn a_read_imm_u32_changes_pc() {
        let base = 128;
        let mut cpu = TestCore::new(base);
        cpu.read_imm_u32();
        assert_eq!(base+4, cpu.pc);
    }

    #[test]
    fn a_read_imm_u32_reads_from_pc() {
        let base = 128;
        let mut cpu = TestCore::new_mem(base, &[2u8, 1u8, 3u8, 4u8]);
        let val = cpu.read_imm_u32().unwrap();
        assert_eq!((2<<24)+(1<<16)+(3<<8)+4, val);
    }


    #[test]
    #[allow(unused_must_use)]
    fn a_read_imm_u16_changes_pc() {
        let base = 128;
        let mut cpu = TestCore::new(base);
        cpu.read_imm_u16();
        assert_eq!(base+2, cpu.pc);
    }

    #[test]
    fn a_read_imm_u16_reads_from_pc() {
        let base = 128;
        let mut cpu = TestCore::new_mem(base, &[2u8, 1u8, 3u8, 4u8]);
        assert_eq!("-S7-----", cpu.flags());

        let val = cpu.read_imm_u16().unwrap();
        assert_eq!((2<<8)+(1<<0), val);
        assert_eq!(Operation::ReadLong(SUPERVISOR_PROGRAM, base, 0x02010304), cpu.mem.logger.ops()[0]);
    }

    #[test]
    fn an_user_mode_read_imm_u16_is_reflected_in_mem_ops() {
        let base = 128;
        let mut cpu = TestCore::new_mem(base, &[2u8, 1u8, 3u8, 4u8]);
        cpu.s_flag = 0;
        assert_eq!("-U7-----", cpu.flags());

        let val = cpu.read_imm_u16().unwrap();
        assert_eq!((2<<8)+(1<<0), val);
        assert_eq!(Operation::ReadLong(USER_PROGRAM, base, 0x02010304), cpu.mem.logger.ops()[0]);
    }

    #[test]
    fn a_reset_reads_sp_and_pc_from_0() {
        let mut cpu = TestCore::new_mem(0, &[0u8,0u8,1u8,0u8, 0u8,0u8,0u8,128u8]);
        cpu.reset();
        assert_eq!(256, cpu.dar[15]);
        assert_eq!(128, cpu.pc);
        assert_eq!("-S7-----", cpu.flags());
        assert_eq!(Operation::ReadLong(SUPERVISOR_PROGRAM, 0, 0x100), cpu.mem.logger.ops()[0]);
    }

    #[test]
    fn execute_reads_from_pc_and_does_not_panic_on_illegal_instruction() {
        let mut cpu = TestCore::new_mem(0xba, &[0xba,0xd1,1u8,0u8, 0u8,0u8,0u8,128u8]);
        cpu.execute1();
    }
    #[test]
    fn execute_does_not_panic_on_odd_pc() {
        let mut cpu = TestCore::new_mem(0xbd, &[0x00, 0x0a, 0x00, 0x00]);
        cpu.execute1();
    }

    #[test]
    fn execute_can_execute_instruction_handler_0a() {
        let mut cpu = TestCore::new_mem(0xba, &[0x00, 0x0A, 1u8,0u8, 0u8,0u8,0u8,128u8]);
        cpu.instruction_set = ops::fake::instruction_set();
        cpu.execute1();
        assert_eq!(0xabcd, cpu.dar[0]);
        assert_eq!(0x0000, cpu.dar[1]);
    }

    #[test]
    fn execute_can_execute_instruction_handler_0b() {
        let mut cpu = TestCore::new_mem(0xba, &[0x00, 0x0B, 1u8,0u8, 0u8,0u8,0u8,128u8]);
        cpu.instruction_set = ops::fake::instruction_set();
        cpu.execute1();
        assert_eq!(0x0000, cpu.dar[0]);
        assert_eq!(0xbcde, cpu.dar[1]);
    }


    #[test]
    fn execute_can_execute_set_dx() {
        // first byte 40 is register D0
        // 42 == D1
        // 44 == D2
        // 46 == D3
        // 48 == D4
        // 4a == D5
        // 4c == D6
        // 4e == D7
        let mut cpu = TestCore::new_mem(0x40, &[0x4c, 0x00, 1u8, 0u8]);
        cpu.instruction_set = ops::fake::instruction_set();
        cpu.execute1();
        assert_eq!(0xcdef, cpu.dar[6]);
    }

    #[test]
    fn array_elems() {
        let mut arr = [1, 2, 3, 4];
        let marr = &mut arr;
        let elem: &mut i32 = &mut (marr[1]);
        // let mut elem2: &mut i32 = &mut (arr[2]);
        assert_eq!(2, *elem);
        *elem = 200;
        assert_eq!(200, *elem);
        // assert_eq!(200, &mut marr[1]);
    }
    #[test]
    fn cycle_counting() {
        // 0xc308 = abcd_8_mm taking 18 cycles
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x08]);
        let Cycles(count) = cpu.execute1();
        assert_eq!(18, count);
    }
    #[test]
    fn cycle_counting_exec2() {
        // 0xc308 = abcd_8_mm taking 18 cycles
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x08, 0xc3, 0x08]);
        let Cycles(count) = cpu.execute(20);
        assert_eq!(18*2, count);
    }

    #[test]
    fn cycle_granularity_rounds_up_to_boundary() {
        // 0xc308 = abcd_8_mm taking 18 cycles
        // With granularity 4, should round up to 20
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x08]);
        cpu.set_cycle_granularity(4);
        let Cycles(count) = cpu.execute1();
        assert_eq!(20, count); // 18 rounded up to next multiple of 4
    }

    #[test]
    fn cycle_granularity_no_change_when_already_aligned() {
        // 0xc300 = abcd_8_rr taking 6 cycles (already aligned to 4)
        // NOP = 0x4e71 taking 4 cycles (already aligned)
        let mut cpu = TestCore::new_mem(0x40, &[0x4e, 0x71]);
        cpu.set_cycle_granularity(4);
        let Cycles(count) = cpu.execute1();
        assert_eq!(4, count); // Already a multiple of 4
    }

    #[test]
    fn cycle_granularity_atari_st_mode() {
        // Simulate Atari ST timing with two instructions
        // 0xc308 = abcd_8_mm taking 18 cycles -> rounds to 20
        // 0xc300 = abcd_8_rr taking 6 cycles -> rounds to 8
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x08, 0xc3, 0x00]);
        cpu.set_cycle_granularity(4);

        // Execute first instruction - takes 18 cycles, aligned to 20
        let Cycles(count) = cpu.execute1();
        assert_eq!(20, count);

        // Execute second instruction - takes 6 cycles, aligned to 8
        let Cycles(count2) = cpu.execute1();
        assert_eq!(8, count2);

        // Total: 28 cycles aligned (vs 24 raw) - matching Atari ST bus behavior
        assert_eq!(28, count + count2);
    }

    #[test]
    fn abcd_8_rr() {
        // opcodes c100 - c107, c300 - c307, etc.
        // or more generally c[13579bdf]0[0-7]
        // where [13579bdf] is DX (dest regno) and [0-7] is DY (src regno)
        // so c300 means D1 = D0 + D1 in BCD
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x00]);

        cpu.dar[0] = 0x16;
        cpu.dar[1] = 0x26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(0x42, cpu.dar[1]);
    }
    #[test]
    fn abcd_8_mm() {
        // opcodes c108 - c10f, c308 - c30f, etc.
        // or more generally c[13579bdf]0[8-f]
        // where [13579bdf] is AX (dest regno) and [8-f] is AY (src regno)
        // so c308 means A1 = A0 + A1 in BCD
        let mut cpu = TestCore::new_mem(0x40, &[0xc3, 0x08]);

        cpu.dar[8+0] = 0x160+1;
        cpu.dar[8+1] = 0x260+1;
        cpu.mem.write_byte(USER_DATA, 0x160, 0x16);
        cpu.mem.write_byte(USER_DATA, 0x260, 0x26);
        cpu.execute1();
        let res = cpu.mem.read_byte(USER_DATA, 0x260);

        // 16 + 26 is 42
        assert_eq!(0x42, res);
    }

    #[test]
    fn add_8_er_d() {
        // opcodes d000 - d007, d200 - d207, etc.
        // or more generally d[02468ace]0[0-7]
        // where [02468ace] is DX (dest regno) and [0-7] is DY (src regno)

        // opcodes d200 is ADD.B    D0, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x00]);

        cpu.dar[0] = 16;
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }

    #[test]
    fn add_8_er_pi() {
        // opcodes d018 - d01f, d218 - d21f, etc.
        // or more generally d[02468ace]1[8-f]
        // where [02468ace] is DX (dest regno) and [8-f] is AY (src regno)

        // opcodes d218 is ADD.B    (A0)+, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x18]);
        let addr = 0x100;
        cpu.dar[8+0] = addr;
        cpu.mem.write_byte(USER_DATA, addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr+1, cpu.dar[8+0]);
    }

    #[test]
    fn add_8_er_pd() {
        // opcodes d020 - d027, d220 - d227, etc.
        // or more generally d[02468ace]2[0-7]
        // where [02468ace] is DX (dest regno) and [0-7] is AY (src regno)

        // opcodes d220 is ADD.B    -(A0), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x20]);
        let addr = 0x100;
        cpu.dar[8+0] = addr;
        cpu.mem.write_byte(USER_DATA, addr-1, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr-1, cpu.dar[8+0]);
    }

    #[test]
    fn add_8_er_ai() {
        // opcodes d010 - d017, d210 - d217, etc.
        // or more generally d[02468ace]1[0-7]
        // where [02468ace] is DX (dest regno) and [0-7] is AY (src regno)

        // opcodes d210 is ADD.B    (A0), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x10]);

        let addr = 0x100;
        cpu.dar[8+0] = addr;
        cpu.mem.write_byte(USER_DATA, addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr, cpu.dar[8+0]);
    }
    #[test]
    fn add_8_er_di_with_positive_displacement() {
        // opcodes d028 - d02f, d228 - d22f, etc.
        // or more generally d[02468ace]2[8-f]
        // where [02468ace] is DX (dest regno) and [8-f] is AY (src regno)

        // opcodes d228,0108 is ADD.B    (0x108, A0), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x28, 0x01, 0x08]);

        let addr = 0x100;
        cpu.dar[8+0] = addr;
        let displaced_addr = addr + 0x108;
        cpu.mem.write_byte(USER_DATA, displaced_addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr, cpu.dar[8+0]);
    }
    #[test]
    fn add_8_er_di_with_negative_displacement() {
        // opcodes d028 - d02f, d228 - d22f, etc. followed by an extension word
        // or more generally d[02468ace]2[8-f]
        // where [02468ace] is DX (dest regno) and [8-f] is AY (src regno)

        // opcodes d228,FFFE is ADD.B    (-2, A0), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x28, 0xFF, 0xFE]);

        let addr = 0x100;
        cpu.dar[8+0] = addr;
        let displaced_addr = addr - 2;
        cpu.mem.write_byte(USER_DATA, displaced_addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr, cpu.dar[8+0]);
    }
    #[test]
    fn add_8_er_ix_with_positive_displacement() {
        // opcodes d030 - d037, d230 - d237, etc. followed by an extension word
        // or more generally d[02468ace]3[0-7]
        // where [02468ace] is DX (dest regno) and [0-7] is AY (src regno)

        // opcodes d230,9002 is ADD.B    (2, A0, A1), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x30, 0x90, 0x02]);

        let addr = 0x100;
        let index = 0x10;
        let displacement = 2;
        cpu.dar[8+0] = addr;
        cpu.dar[8+1] = index;
        let effective_addr = addr + index + displacement;
        cpu.mem.write_byte(USER_DATA, effective_addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr, cpu.dar[8+0]);
    }
    #[test]
    fn add_8_er_ix_with_negative_displacement() {
        // opcodes d030 - d037, d230 - d237, etc. followed by an extension word
        // or more generally d[02468ace]3[0-7]
        // where [02468ace] is DX (dest regno) and [0-7] is AY (src regno)

        // opcodes d230,90FE is ADD.B    (-2, A0, A1), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x30, 0x90, 0xFE]);

        let addr = 0x100;
        let index = 0x10;
        let displacement = 2;
        cpu.dar[8+0] = addr;
        cpu.dar[8+1] = index;
        let effective_addr = addr + index - displacement;
        cpu.mem.write_byte(USER_DATA, effective_addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
        assert_eq!(addr, cpu.dar[8+0]);
    }
    #[test]
    fn add_8_er_aw() {
        // opcodes d038, d238, d438, etc. followed by an extension word
        // or more generally d[02468ace]38
        // where [02468ace] is DX (dest regno) and the extension word is
        // the 16-bit absolute address

        // opcodes d238,0108 is ADD.B    $0108, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x38, 0x01, 0x08]);
        cpu.mem.write_byte(USER_DATA, 0x108, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }
    #[test]
    fn add_8_er_al() {
        // opcodes d039, d239, d439, etc. followed by two extension words
        // or more generally d[02468ace]39

        // where [02468ace] is DX (dest regno) and the first extension
        // word is the high order word of the 32-bit absolute address,
        // and the second extension word is the low order word.

        // opcodes d239,0009,0000 is ADD.B    $90000, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x39, 0x00, 0x09, 0x00, 0x00]);
        cpu.mem.write_byte(USER_DATA, 0x90000, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }
    #[test]
    fn add_8_er_pcdi() {
        // opcodes d03a, d23a, d43a, etc. followed by an extension word
        // or more generally d[02468ace]3a

        // where [02468ace] is DX (dest regno)
        // opcodes d23a,0108 is ADD.B    ($0108, PC), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x3a, 0x01, 0x08]);
        let addr = 0x40+2+0x0108;
        cpu.mem.write_byte(USER_DATA, addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }
    #[test]
    fn add_8_er_pcix() {
        // opcodes d03b, d23b, d43b, etc. followed by an extension word
        // or more generally d[02468ace]3b

        // where [02468ace] is DX (dest regno)
        // opcodes d23b,9002 is ADD.B    (2, PC, A1), D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x3b, 0x90, 0x02]);
        let addr = cpu.pc + 2; // will be +2 after reading instruction word
        let index = 0x10;
        let displacement = 2;
        cpu.dar[8+1] = index;
        let effective_addr = addr + index + displacement;
        cpu.mem.write_byte(USER_DATA, effective_addr, 16);
        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }
    #[test]
    fn add_8_er_imm() {
        // opcodes d03c, d23c, d43c, etc. followed by an extension word
        // or more generally d[02468ace]3c
        // where [02468ace] is DX (dest regno)

        // opcodes d23c,0010 is ADD.B    #16, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x3c, 0x00, 0x10]);

        cpu.dar[1] = 26;
        cpu.execute1();

        // 16 + 26 is 42
        assert_eq!(42, cpu.dar[1]);
    }
    #[test]
    fn add_16_re_pi() { //0xD400, 0xD700
        let mut cpu = TestCore::new_mem(0x40, &[0xd3, 0x58]);
        cpu.dar[8+0] = 0x40;
        cpu.dar[1] = 0xa8;

        cpu.execute1();
        for op in cpu.mem.logger.ops() {
            println!("{:?}", op);
        }
        let word = cpu.read_data_word(64).unwrap();
        assert_eq!(0xd358 + 0xa8, word);
    }
    #[test]
    fn op_with_extension_word_moves_pc_past_extension_word() {
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x30, 0x90, 0xFE]);
        cpu.execute1();
        assert_eq!(0x44, cpu.pc);
    }

    #[test]
    fn status_register_roundtrip(){
        let mut core = TestCore::new(0x40);
        //Status register bits are:
        //      TTSM_0iii_000X_NZVC;
        let f=0b0000_1000_1110_0000; // these bits should always be zero
        let s=0b0010_0000_0000_0000;
        let i=0b0000_0111_0000_0000;
        let x=0b0000_0000_0001_0000;
        let n=0b0000_0000_0000_1000;
        let z=0b0000_0000_0000_0100;
        let v=0b0000_0000_0000_0010;
        let c=0b0000_0000_0000_0001;
        let flags = vec![x,n,z,v,c,f,s,i,0];
        for sf in flags {
            core.sr_to_flags(sf);
            let sr = core.status_register();
            let expected = if sf == f {0} else {sf};
            assert_eq!(expected, sr);
        }
    }
    #[test]
    fn clones_have_independent_registers() {
        let mut core = TestCore::new(0x40);
        core.dar[1] = 0x16;
        let mut clone = core.clone();
        assert_eq!(0x16, core.dar[1]);
        assert_eq!(0x16, clone.dar[1]);
        clone.dar[1] = 0x32;
        assert_eq!(0x16, core.dar[1]);
        assert_eq!(0x32, clone.dar[1]);
    }

    #[test]
    fn user_mode_chk_16_pd_with_trap_uses_sp_correctly() {
        let mut cpu = TestCore::new_mem(0x40, &[0x41, 0xa7]); // 0x41a7 CHK.W -(A7), D0
        cpu.write_data_long(super::EXCEPTION_CHK as u32 * 4, 0x1010).unwrap(); // set up exception vector 6
        cpu.s_flag = super::SFLAG_CLEAR; // user mode
        cpu.inactive_ssp = 0x200; // Supervisor stack at 0x200
        cpu.dar[15] = 0x100; // User stack at 0x100
        cpu.dar[0] = 0xF123; // negative, will cause a trap (vector 6) and enter supervisor mode

        cpu.execute1();
        assert_eq!(0x1010, cpu.pc);
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
        assert_eq!(0x100-2, cpu.inactive_usp); // check USP, decremented by A7 PD
        assert_eq!(0x200-6, cpu.dar[15]); // check SSP
    }

    #[test]
    fn sr_to_flags_can_enter_user_mode_and_swap_stackpointers() {
        let mut cpu = TestCore::new_mem(0x40, &[0x41, 0xa7]); // 0x41a7 CHK.W -(A7), D0
        cpu.s_flag = super::SFLAG_SET;
        cpu.inactive_usp = 0x1000;
        cpu.dar[15] = 0x2000;
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
        assert_eq!(0x2000, cpu.ssp());
        cpu.sr_to_flags(0); // User mode
        assert_eq!(0x1000, cpu.usp());
        assert_eq!(0x1000, cpu.dar[15]);
        assert_eq!(super::SFLAG_CLEAR, cpu.s_flag);
    }

    #[test]
    fn sr_to_flags_can_enter_supervisor_mode_and_swap_stackpointers() {
        let mut cpu = TestCore::new_mem(0x40, &[0x41, 0xa7]); // 0x41a7 CHK.W -(A7), D0
        cpu.s_flag = super::SFLAG_CLEAR;
        cpu.inactive_ssp = 0x1000;
        cpu.dar[15] = 0x2000;
        assert_eq!(super::SFLAG_CLEAR, cpu.s_flag);
        assert_eq!(0x2000, cpu.usp());
        cpu.sr_to_flags(0xffff); // Supa mode
        assert_eq!(0x1000, cpu.ssp());
        assert_eq!(0x1000, cpu.dar[15]);
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
    }

    #[test]
    fn core_can_stop() {
        let initial_pc = 0x40;
        let mut cpu = TestCore::new_mem_init(initial_pc, &[0x4e, 0x72, 0x00, 0x00], opcodes::OP_NOP);
        cpu.sr_to_flags(0xffff); // Supa mode
        cpu.execute1();
        assert_eq!(0x0000, cpu.status_register());
        let next_instruction = initial_pc + 2 + 2; // 40 + instruction word + immediate word
        assert_eq!(next_instruction, cpu.pc);
        // continue executing some cycles
        let chunk_of_cycles = 400;
        let Cycles(consumed) = cpu.execute(chunk_of_cycles);
        assert_eq!(chunk_of_cycles, consumed);
        // but as the cpu is stopped, pc should still point
        // to the next instruction
        assert_eq!(next_instruction, cpu.pc);
    }

    use crate::interrupts::InterruptController;
    #[test]
    fn reset_calls_interrupt_controller() {
        let mut cpu = TestCore::new_mem(0x40, &[0x4e, 0x70]); // 0x4e70 RESET
        cpu.int_ctrl.request_interrupt(5);
        cpu.execute1(); // will execute RESET, which will reset all IRQs
        assert_eq!(0, cpu.int_ctrl.highest_priority());
    }

    #[test]
    fn processing_state_is_known_in_g2_exception_handler() {
        let mut cpu = TestCore::new_mem(0x40, &[0x41, 0x90]); // 0x4190 CHK.W (A0), D0
        cpu.write_data_long(super::EXCEPTION_CHK as u32 * 4, 0x1010).unwrap(); // set up exception vector 6
        cpu.write_data_word(0x1010, opcodes::OP_RTE_32).unwrap(); // handler is just RTE
        cpu.s_flag = super::SFLAG_CLEAR; // user mode
        cpu.inactive_ssp = 0x200; // Supervisor stack at 0x200
        cpu.dar[15] = 0x100; // User stack at 0x100
        cpu.dar[0] = 0xF123; // negative, will cause a trap (vector 6) and enter the handler in supervisor mode

        cpu.execute1();
        assert_eq!(0x1010, cpu.pc);
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
        assert_eq!(super::ProcessingState::Group2Exception, cpu.processing_state);
        assert_eq!(0x200-6, cpu.dar[15]); // check SSP

        cpu.execute1(); // will execute RTE
        // which should return to user mode, instruction after CHK, normal state
        assert_eq!(0x42, cpu.pc);
        assert_eq!(super::SFLAG_CLEAR, cpu.s_flag);
        assert_eq!(super::ProcessingState::Normal, cpu.processing_state);
    }

    #[test]
    fn processing_state_is_known_in_g1_exception_handler() {
        // real illegal instruction = 0x4afc, but any illegal instruction should work
        let mut cpu = TestCore::new_mem(0x40, &[0x4a, 0xfc]);
        cpu.write_data_long(super::EXCEPTION_ILLEGAL_INSTRUCTION as u32 * 4, 0x1010).unwrap(); // set up exception vector
        cpu.write_data_word(0x1010, opcodes::OP_RTE_32).unwrap(); // handler is just RTE
        cpu.s_flag = super::SFLAG_CLEAR; // user mode
        cpu.inactive_ssp = 0x200; // Supervisor stack at 0x200
        cpu.dar[15] = 0x100; // User stack at 0x100

        cpu.execute1(); // will execute the illegal instruction and enter the handler in supervisor mode
        assert_eq!(0x1010, cpu.pc);
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
        assert_eq!(super::ProcessingState::Group1Exception, cpu.processing_state);

        cpu.execute1(); // will execute RTE
        // which should return to user mode, faulting instruction, normal state
        assert_eq!(0x40, cpu.pc);
        assert_eq!(super::SFLAG_CLEAR, cpu.s_flag);
        assert_eq!(super::ProcessingState::Normal, cpu.processing_state);
    }

    #[test]
    fn processing_state_is_known_in_g0_exception_handler() {
        let mut cpu = TestCore::new_mem(0x40, &[0x41, 0xa0]); // 0x41a0 CHK.W -(A0), D0
        cpu.write_data_long(super::EXCEPTION_ADDRESS_ERROR as u32 * 4, 0x1010).unwrap(); // set up exception vector
        cpu.write_data_word(0x1010, 0x504F).unwrap(); // handler is ADD #8, A7
        cpu.write_data_word(0x1012, opcodes::OP_RTE_32).unwrap(); // followed by RTE

        cpu.s_flag = super::SFLAG_CLEAR; // user mode
        cpu.inactive_ssp = 0x200; // Supervisor stack at 0x200
        cpu.dar[15] = 0x100; // User stack at 0x100
        cpu.dar[8] = 0x0023; // odd, will cause an address error exception and enter the handler in supervisor mode

        cpu.execute1();
        assert_eq!(0x1010, cpu.pc);
        assert_eq!(0x200-14, cpu.dar[15]);
        assert_eq!(super::SFLAG_SET, cpu.s_flag);
        assert_eq!(super::ProcessingState::Group0Exception, cpu.processing_state);

        cpu.execute1(); // will execute ADD #8, A7 (to skip g0 stuff in stack frame)
        assert_eq!(0x1012, cpu.pc);
        assert_eq!(0x200-6, cpu.dar[15]);

        cpu.execute1(); // will execute RTE
        // which should return to user mode, after faulted instruction, normal state
        assert_eq!(0x42, cpu.pc); // this is what address errors stack in Musashi
        assert_eq!(super::SFLAG_CLEAR, cpu.s_flag);
        assert_eq!(super::ProcessingState::Normal, cpu.processing_state);
    }

    #[test]
    fn stop_changes_processing_state() {
        let mut cpu = TestCore::new_mem(0x40, &[0x4e, 0x72]); // 0x4e72 STOP
        cpu.execute1(); // will execute STOP
        assert_eq!(super::ProcessingState::Stopped, cpu.processing_state);
    }

    #[test]
    fn interrupt_can_trigger_from_stopped_state() {
        let mut cpu = TestCore::new_mem(0x40, &[0x4e, 0x72]); // 0x4e72 STOP
        let supervisor_bit = 1 << 13;
        let irq_mask = 0;
        cpu.sr_to_flags(supervisor_bit | irq_mask);
        let vec4handler = 0x2F0000;
        let autovector_base = 24;
        let irq = 5;
        cpu.mem.write_long(SUPERVISOR_PROGRAM, (autovector_base + irq) * 4 , vec4handler);
        // opcodes d278,0108 is ADD.W    $0108, D1
        cpu.mem.write_long(SUPERVISOR_PROGRAM, vec4handler, 0xd2780108);
        cpu.execute1(); // will execute STOP
        assert_eq!(super::ProcessingState::Stopped, cpu.processing_state);
        cpu.int_ctrl.request_interrupt(irq as u8);
        cpu.execute1(); // will trigger interrupt handler
        assert_eq!(super::ProcessingState::Group1Exception, cpu.processing_state);
        assert_eq!(vec4handler, cpu.pc);
        cpu.execute1(); // should execute first instruction of handler, which is 4 bytes
        assert_eq!(vec4handler + 4, cpu.pc);
    }

    #[test]
    fn pending_interrupt_check_does_not_change_state() {
        // opcodes d200 is ADD.B    D0, D1
        let mut cpu = TestCore::new_mem(0x40, &[0xd2, 0x00]);
        let supervisor_bit = 1 << 13;
        let irq_mask = 0;
        cpu.sr_to_flags(supervisor_bit | irq_mask);
        cpu.int_ctrl.request_interrupt(4);
        assert_eq!(Some(4), cpu.pending_interrupt());
        assert_eq!(Some(4), cpu.pending_interrupt());
        // but is cleared after initiating interrupt handling
        assert_eq!(Cycles(44), cpu.execute1());
        assert_eq!(None, cpu.pending_interrupt());
    }

    #[test]
    fn can_enter_halted_state() {
        // halted state is entered when a second group 0 exception
        // (except external reset) occurs in a group 0 exception handler
        let mut cpu = TestCore::new_mem(0x41, &[0xd2, 0x00]); // d200 is ADD.B D0, D1
        let address_error_handler = 0x2F0001;
        cpu.mem.write_long(SUPERVISOR_PROGRAM, super::EXCEPTION_ADDRESS_ERROR as u32 * 4, address_error_handler);
        // opcodes d278,0108 is ADD.W    $0108, D1
        cpu.execute1(); // will execute at an odd address, invoking the address error exception handler
        assert_eq!(super::ProcessingState::Group0Exception, cpu.processing_state);
        cpu.execute1(); // will execute the handler at an odd address,
        // this should trigger a second address error, which should halt the processor
        assert_eq!(super::ProcessingState::Halted, cpu.processing_state);
        // in the halted state, the only way to win, is not to play.
        // An external reset is needed.
    }

    #[test]
    fn nmi_has_no_effect_in_halted_state() {
        let mut cpu = TestCore::new_mem(0x41, &[0x4e, 0x72]); // 0x4e72 STOP
        cpu.processing_state = super::ProcessingState::Halted;

        // Normally this would trigger a NMI, but should not in halted state
        cpu.int_ctrl.request_interrupt(7);
        cpu.execute1(); // will do nothing
        assert_eq!(super::ProcessingState::Halted, cpu.processing_state);
    }

    use crate::cpu::{Result, Callbacks, Exception, Core};

    struct CustomExceptionHandler
    {
        suppress: bool,
        count: isize,
        ex: Option<Exception>
    }

    impl Callbacks for CustomExceptionHandler {
        fn exception_callback(&mut self, core: &mut impl Core, ex: Exception) -> Result<Cycles> {
            self.count += 1;
            self.ex = Some(ex);

            if !self.suppress {
                Err(ex)
            } else {
                // correct an odd PC
                if *core.pc() % 2 == 1 {
                    *core.pc() += 1;
                }
                Ok(Cycles(1000))
            }
        }
    }

    #[test]
    fn can_execute_with_state() {
        let odd_initial_address = 0x41;
        let odd_subsequent_address = 0x441;
        let address_error_handler = 0x2F0000;
        // setup an odd PC, in order to cause an Address Error
        let mut cpu = TestCore::new_mem(odd_initial_address, &[0xd2, 0x00]); // d200 is ADD.B D0, D1
        cpu.write_data_long(super::EXCEPTION_ADDRESS_ERROR as u32 * 4, address_error_handler).unwrap();

        let mut handler = CustomExceptionHandler { suppress: false, count: 0, ex: None };
        let cycles = cpu.execute_with_state(1, &mut handler);
        assert_eq!(1, handler.count);
        assert_eq!(Cycles(50), cycles); // expected number of cycles for initiating an AddressError exception
        assert_eq!(address_error_handler, cpu.pc);
        if let Some(Exception::AddressError { address, .. }) = handler.ex {
            assert_eq!(odd_initial_address, address);
        } else {
            assert!(false);
        }
        // now the exception_callback will not pass through subsequent
        // exceptions, causing normal exception handling to be
        // suppressed. The exception_callback then acts as if some handler
        // took 1000 cycles, and returned to the next instruction
        handler.suppress = true;
        // setup an odd PC, in order to cause another Address Error
        cpu.pc = odd_subsequent_address;
        let cycles = cpu.execute_with_state(1, &mut handler);
        assert_eq!(2, handler.count);
        assert_eq!(Cycles(1000), cycles); // expected number of cycles we faked in our exception_callback
        assert_eq!(odd_subsequent_address + 1, cpu.pc); // our exception_callback jumps to the next even address
        if let Some(Exception::AddressError { address, .. }) = handler.ex {
            assert_eq!(odd_subsequent_address, address);
        } else {
            assert!(false);
        }
    }
}
