kr580 1.0.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
use super::{ExportFlag, ExportMemoryColumn, ExportRegister, ExportTab};
use crate::persistence::{ExportFlagKind, ExportRegisterKind};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ExportModalFocus {
    None,
    TabXlsx,
    TabText,
    Page,
    TargetDropdown,
    TargetAdd,
    TargetDelete,
    MemoryStart,
    MemoryEnd,
    ColumnAddress,
    ColumnValue,
    ColumnCommand,
    ColumnComment,
    RegisterAccumulator,
    RegisterW,
    RegisterZ,
    RegisterB,
    RegisterC,
    RegisterD,
    RegisterE,
    RegisterH,
    RegisterL,
    RegisterStackPointer,
    RegisterProgramCounter,
    RegisterCycles,
    FlagSign,
    FlagZero,
    FlagAuxiliaryCarry,
    FlagParity,
    FlagCarry,
    Cancel,
    Confirm,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ExportMemoryColumns {
    pub(crate) address: bool,
    pub(crate) value: bool,
    pub(crate) command: bool,
    pub(crate) comment: bool,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct ExportRegisterSelection {
    pub(crate) accumulator: bool,
    pub(crate) w: bool,
    pub(crate) z: bool,
    pub(crate) b: bool,
    pub(crate) c: bool,
    pub(crate) d: bool,
    pub(crate) e: bool,
    pub(crate) h: bool,
    pub(crate) l: bool,
    pub(crate) stack_pointer: bool,
    pub(crate) program_counter: bool,
    pub(crate) cycles: bool,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct ExportFlagSelection {
    pub(crate) sign: bool,
    pub(crate) zero: bool,
    pub(crate) auxiliary_carry: bool,
    pub(crate) parity: bool,
    pub(crate) carry: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ExportTargetSettings {
    pub(crate) memory_start_input: String,
    pub(crate) memory_end_input: String,
    pub(crate) columns: ExportMemoryColumns,
    pub(crate) registers: ExportRegisterSelection,
    pub(crate) flags: ExportFlagSelection,
}

impl Default for ExportMemoryColumns {
    fn default() -> Self {
        Self {
            address: true,
            value: true,
            command: true,
            comment: false,
        }
    }
}

impl Default for ExportTargetSettings {
    fn default() -> Self {
        Self {
            memory_start_input: "0000".to_owned(),
            memory_end_input: "FFFF".to_owned(),
            columns: ExportMemoryColumns::default(),
            registers: ExportRegisterSelection::default(),
            flags: ExportFlagSelection::default(),
        }
    }
}

impl ExportMemoryColumns {
    pub(crate) fn toggle(&mut self, column: ExportMemoryColumn) {
        match column {
            ExportMemoryColumn::Address => self.address = !self.address,
            ExportMemoryColumn::Value => self.value = !self.value,
            ExportMemoryColumn::Command => self.command = !self.command,
            ExportMemoryColumn::Comment => self.comment = !self.comment,
        }
    }
}

impl ExportRegisterSelection {
    pub(crate) fn toggle(&mut self, register: ExportRegister) {
        match register {
            ExportRegister::Accumulator => self.accumulator = !self.accumulator,
            ExportRegister::W => self.w = !self.w,
            ExportRegister::Z => self.z = !self.z,
            ExportRegister::B => self.b = !self.b,
            ExportRegister::C => self.c = !self.c,
            ExportRegister::D => self.d = !self.d,
            ExportRegister::E => self.e = !self.e,
            ExportRegister::H => self.h = !self.h,
            ExportRegister::L => self.l = !self.l,
            ExportRegister::StackPointer => self.stack_pointer = !self.stack_pointer,
            ExportRegister::ProgramCounter => self.program_counter = !self.program_counter,
            ExportRegister::Cycles => self.cycles = !self.cycles,
        }
    }

    pub(crate) fn selected(self) -> Vec<ExportRegisterKind> {
        let mut out = Vec::new();
        push_if(&mut out, self.accumulator, ExportRegisterKind::Accumulator);
        push_if(&mut out, self.w, ExportRegisterKind::W);
        push_if(&mut out, self.z, ExportRegisterKind::Z);
        push_if(&mut out, self.b, ExportRegisterKind::B);
        push_if(&mut out, self.c, ExportRegisterKind::C);
        push_if(&mut out, self.d, ExportRegisterKind::D);
        push_if(&mut out, self.e, ExportRegisterKind::E);
        push_if(&mut out, self.h, ExportRegisterKind::H);
        push_if(&mut out, self.l, ExportRegisterKind::L);
        push_if(
            &mut out,
            self.stack_pointer,
            ExportRegisterKind::StackPointer,
        );
        push_if(
            &mut out,
            self.program_counter,
            ExportRegisterKind::ProgramCounter,
        );
        push_if(&mut out, self.cycles, ExportRegisterKind::Cycles);
        out
    }
}

impl ExportFlagSelection {
    pub(crate) fn toggle(&mut self, flag: ExportFlag) {
        match flag {
            ExportFlag::Sign => self.sign = !self.sign,
            ExportFlag::Zero => self.zero = !self.zero,
            ExportFlag::AuxiliaryCarry => self.auxiliary_carry = !self.auxiliary_carry,
            ExportFlag::Parity => self.parity = !self.parity,
            ExportFlag::Carry => self.carry = !self.carry,
        }
    }

    pub(crate) fn selected(self) -> Vec<ExportFlagKind> {
        let mut out = Vec::new();
        push_flag_if(&mut out, self.zero, ExportFlagKind::Zero);
        push_flag_if(&mut out, self.sign, ExportFlagKind::Sign);
        push_flag_if(&mut out, self.parity, ExportFlagKind::Parity);
        push_flag_if(&mut out, self.carry, ExportFlagKind::Carry);
        push_flag_if(
            &mut out,
            self.auxiliary_carry,
            ExportFlagKind::AuxiliaryCarry,
        );
        out
    }
}

impl ExportModalFocus {
    const ORDER: [Self; 32] = [
        Self::None,
        Self::TabXlsx,
        Self::TabText,
        Self::Page,
        Self::TargetDropdown,
        Self::TargetAdd,
        Self::TargetDelete,
        Self::MemoryStart,
        Self::MemoryEnd,
        Self::ColumnAddress,
        Self::ColumnValue,
        Self::ColumnCommand,
        Self::ColumnComment,
        Self::RegisterAccumulator,
        Self::RegisterW,
        Self::RegisterZ,
        Self::RegisterB,
        Self::RegisterC,
        Self::RegisterD,
        Self::RegisterE,
        Self::RegisterH,
        Self::RegisterL,
        Self::RegisterStackPointer,
        Self::RegisterProgramCounter,
        Self::RegisterCycles,
        Self::FlagZero,
        Self::FlagSign,
        Self::FlagParity,
        Self::FlagCarry,
        Self::FlagAuxiliaryCarry,
        Self::Cancel,
        Self::Confirm,
    ];

    pub(crate) fn next(self) -> Self {
        let index = Self::ORDER
            .iter()
            .position(|focus| *focus == self)
            .unwrap_or(0);
        Self::ORDER[(index + 1) % Self::ORDER.len()]
    }

    pub(crate) fn previous(self) -> Self {
        let index = Self::ORDER
            .iter()
            .position(|focus| *focus == self)
            .unwrap_or(0);
        Self::ORDER[(index + Self::ORDER.len() - 1) % Self::ORDER.len()]
    }

    pub(crate) fn next_for_tab(self, tab: ExportTab) -> Self {
        let mut focus = self.next();
        while !focus.is_visible_for_tab(tab) {
            focus = focus.next();
        }
        focus
    }

    pub(crate) fn previous_for_tab(self, tab: ExportTab) -> Self {
        let mut focus = self.previous();
        while !focus.is_visible_for_tab(tab) {
            focus = focus.previous();
        }
        focus
    }

    pub(crate) fn is_visible_for_tab(self, tab: ExportTab) -> bool {
        !matches!(
            (tab, self),
            (_, Self::None) | (ExportTab::Text, Self::ColumnComment)
        )
    }

    pub(crate) fn tab(self) -> Option<ExportTab> {
        match self {
            Self::TabXlsx => Some(ExportTab::Xlsx),
            Self::TabText => Some(ExportTab::Text),
            _ => None,
        }
    }

    pub(crate) fn memory_column(self) -> Option<ExportMemoryColumn> {
        match self {
            Self::ColumnAddress => Some(ExportMemoryColumn::Address),
            Self::ColumnValue => Some(ExportMemoryColumn::Value),
            Self::ColumnCommand => Some(ExportMemoryColumn::Command),
            Self::ColumnComment => Some(ExportMemoryColumn::Comment),
            _ => None,
        }
    }

    pub(crate) fn register(self) -> Option<ExportRegister> {
        match self {
            Self::RegisterAccumulator => Some(ExportRegister::Accumulator),
            Self::RegisterW => Some(ExportRegister::W),
            Self::RegisterZ => Some(ExportRegister::Z),
            Self::RegisterB => Some(ExportRegister::B),
            Self::RegisterC => Some(ExportRegister::C),
            Self::RegisterD => Some(ExportRegister::D),
            Self::RegisterE => Some(ExportRegister::E),
            Self::RegisterH => Some(ExportRegister::H),
            Self::RegisterL => Some(ExportRegister::L),
            Self::RegisterStackPointer => Some(ExportRegister::StackPointer),
            Self::RegisterProgramCounter => Some(ExportRegister::ProgramCounter),
            Self::RegisterCycles => Some(ExportRegister::Cycles),
            _ => None,
        }
    }

    pub(crate) fn flag(self) -> Option<ExportFlag> {
        match self {
            Self::FlagSign => Some(ExportFlag::Sign),
            Self::FlagZero => Some(ExportFlag::Zero),
            Self::FlagAuxiliaryCarry => Some(ExportFlag::AuxiliaryCarry),
            Self::FlagParity => Some(ExportFlag::Parity),
            Self::FlagCarry => Some(ExportFlag::Carry),
            _ => None,
        }
    }

    pub(crate) fn for_column(column: ExportMemoryColumn) -> Self {
        match column {
            ExportMemoryColumn::Address => Self::ColumnAddress,
            ExportMemoryColumn::Value => Self::ColumnValue,
            ExportMemoryColumn::Command => Self::ColumnCommand,
            ExportMemoryColumn::Comment => Self::ColumnComment,
        }
    }

    pub(crate) fn for_register(register: ExportRegister) -> Self {
        match register {
            ExportRegister::Accumulator => Self::RegisterAccumulator,
            ExportRegister::W => Self::RegisterW,
            ExportRegister::Z => Self::RegisterZ,
            ExportRegister::B => Self::RegisterB,
            ExportRegister::C => Self::RegisterC,
            ExportRegister::D => Self::RegisterD,
            ExportRegister::E => Self::RegisterE,
            ExportRegister::H => Self::RegisterH,
            ExportRegister::L => Self::RegisterL,
            ExportRegister::StackPointer => Self::RegisterStackPointer,
            ExportRegister::ProgramCounter => Self::RegisterProgramCounter,
            ExportRegister::Cycles => Self::RegisterCycles,
        }
    }

    pub(crate) fn for_flag(flag: ExportFlag) -> Self {
        match flag {
            ExportFlag::Sign => Self::FlagSign,
            ExportFlag::Zero => Self::FlagZero,
            ExportFlag::AuxiliaryCarry => Self::FlagAuxiliaryCarry,
            ExportFlag::Parity => Self::FlagParity,
            ExportFlag::Carry => Self::FlagCarry,
        }
    }

    pub(crate) fn after_target_action(dropdown_open: bool) -> Self {
        if dropdown_open {
            Self::TargetDropdown
        } else {
            Self::Page
        }
    }

    pub(crate) fn clears_on_escape(self) -> bool {
        matches!(
            self,
            Self::Page | Self::TargetDropdown | Self::MemoryStart | Self::MemoryEnd
        ) || self.memory_column().is_some()
            || self.register().is_some()
            || self.flag().is_some()
    }
}

fn push_if(out: &mut Vec<ExportRegisterKind>, enabled: bool, register: ExportRegisterKind) {
    if enabled {
        out.push(register);
    }
}

fn push_flag_if(out: &mut Vec<ExportFlagKind>, enabled: bool, flag: ExportFlagKind) {
    if enabled {
        out.push(flag);
    }
}

pub(super) fn hex4_input(value: &str) -> String {
    value
        .chars()
        .filter(|ch| ch.is_ascii_hexdigit())
        .take(4)
        .map(|ch| ch.to_ascii_uppercase())
        .collect()
}

pub(super) fn parse_hex_u16_or(value: &str, fallback: u16) -> u16 {
    u16::from_str_radix(value.trim(), 16).unwrap_or(fallback)
}