sim86 0.1.0

An 8086/8088 emulator with full support for 16-bit x86.
Documentation
use std::io::Write;

use crate::model::{
    ByteOrWord, ByteReg, CompleteInstruction, Instruction, ModRM, Reg, SegmentReg, WordReg,
};

#[derive(Clone, Debug, Default)]
pub struct MachineState {
    pub gprs: [u16; 8],
    pub srs: [u16; 4],
}

impl MachineState {
    pub fn execute_instruction(&mut self, complete_instruction: CompleteInstruction) {
        let CompleteInstruction(instruction, _) = complete_instruction;
        match instruction {
            Instruction::MovRmToFromReg {
                is_reg_dst,
                reg,
                rm,
            } => {
                if is_reg_dst {
                    match rm {
                        ModRM::Reg(src_reg) => self.mov_reg_to_reg(reg, src_reg),
                        _ => todo!(),
                    }
                } else {
                    match rm {
                        ModRM::Reg(dst_reg) => self.mov_reg_to_reg(dst_reg, reg),
                        _ => todo!(),
                    }
                }
            }
            Instruction::MovImmediateToRm { rm, immediate } => match rm {
                ModRM::Reg(dst_reg) => match (dst_reg, immediate) {
                    (Reg::ByteReg(byte_reg), ByteOrWord::Byte(byte)) => {
                        self.write_byte_to_byte_reg(byte_reg, byte);
                    }
                    (Reg::WordReg(word_reg), ByteOrWord::Word(word)) => {
                        self.gprs[word_reg as usize] = word;
                    }
                    (Reg::SegmentReg(_), _) => unreachable!(),
                    (Reg::ByteReg(_), ByteOrWord::Word(_)) => unreachable!(),
                    (Reg::WordReg(_), ByteOrWord::Byte(_)) => unreachable!(),
                },
                _ => todo!(),
            },
            _ => todo!(),
        }
    }

    pub fn read_byte_reg(&self, byte_reg: ByteReg) -> u8 {
        let byte_reg: u8 = byte_reg as u8;
        let gpr = byte_reg & 0b11;
        let is_high = (byte_reg & 0b100) != 0;
        if is_high {
            (self.gprs[gpr as usize] >> 8) as u8
        } else {
            (self.gprs[gpr as usize]) as u8
        }
    }

    pub fn write_byte_to_byte_reg(&mut self, byte_reg: ByteReg, byte: u8) {
        let byte_reg: u8 = byte_reg as u8;
        let gpr = byte_reg & 0b11;
        let is_high = (byte_reg & 0b100) != 0;
        if is_high {
            let high_bits_cleared = self.gprs[gpr as usize] & 0x00FFu16;
            self.gprs[gpr as usize] = high_bits_cleared | ((byte as u16) << 8);
        } else {
            let low_bits_cleared = self.gprs[gpr as usize] & 0xFF00u16;
            self.gprs[gpr as usize] = low_bits_cleared | (byte as u16);
        }
    }

    pub fn mov_reg_to_reg(&mut self, dst_reg: Reg, src_reg: Reg) {
        match (dst_reg, src_reg) {
            (Reg::ByteReg(dst_byte_reg), Reg::ByteReg(src_byte_reg)) => {
                self.write_byte_to_byte_reg(dst_byte_reg, self.read_byte_reg(src_byte_reg));
            }
            (Reg::WordReg(dst_word_reg), Reg::WordReg(src_word_reg)) => {
                self.gprs[dst_word_reg as usize] = self.gprs[src_word_reg as usize];
            }

            (Reg::SegmentReg(segment_reg), Reg::WordReg(word_reg)) => {
                self.srs[segment_reg as usize] = self.gprs[word_reg as usize];
            }
            (Reg::WordReg(word_reg), Reg::SegmentReg(segment_reg)) => {
                self.gprs[word_reg as usize] = self.srs[segment_reg as usize];
            }

            (Reg::ByteReg(_), Reg::WordReg(_)) => unreachable!(),
            (Reg::ByteReg(_), Reg::SegmentReg(_)) => unreachable!(),
            (Reg::WordReg(_), Reg::ByteReg(_)) => unreachable!(),
            (Reg::SegmentReg(_), Reg::ByteReg(_)) => unreachable!(),
            (Reg::SegmentReg(_), Reg::SegmentReg(_)) => unreachable!(),
        };
    }
}

impl MachineState {
    pub fn print_registers<W: Write>(
        &self,
        prefix: &str,
        registers: &[Reg],
        output: &mut W,
    ) -> Result<(), anyhow::Error> {
        for reg in registers {
            match reg {
                Reg::ByteReg(byte_reg) => {
                    let value = self.read_byte_reg(*byte_reg);
                    writeln!(
                        output,
                        "{} {}: {:#06x} ({})",
                        prefix, byte_reg, value, value
                    )?;
                }
                Reg::WordReg(word_reg) => {
                    let value = self.gprs[*word_reg as usize];
                    writeln!(
                        output,
                        "{} {}: {:#06x} ({})",
                        prefix, word_reg, value, value
                    )?;
                }
                Reg::SegmentReg(segment_reg) => {
                    let value = self.srs[*segment_reg as usize];
                    writeln!(
                        output,
                        "{} {}: {:#06x} ({})",
                        prefix, segment_reg, value, value
                    )?;
                }
            }
        }
        Ok(())
    }

    pub fn print_all_registers<W: Write>(&self, output: &mut W) -> Result<(), anyhow::Error> {
        for gpr in 0b000u8..=0b111u8 {
            let gpr: WordReg = WordReg::try_from(gpr).unwrap();
            let value = self.gprs[gpr as usize];
            writeln!(output, "{}: {:#06x} ({})", gpr, value, value)?;
        }
        for sr in 0b00u8..=0b11u8 {
            let sr: SegmentReg = SegmentReg::try_from(sr).unwrap();
            let value = self.srs[sr as usize];
            writeln!(output, "{}: {:#06x} ({})", sr, value, value)?;
        }
        Ok(())
    }
}

#[derive(Clone, Debug)]
pub enum MachineStateDiff {
    Gpr(WordReg, u16, u16),
    Sr(SegmentReg, u16, u16),
}

impl ::std::fmt::Display for MachineStateDiff {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MachineStateDiff::Gpr(gpr, prev, next) => write!(f, "{}:{:#x}->{:#x}", gpr, prev, next),
            MachineStateDiff::Sr(sr, prev, next) => write!(f, "{}:{:#x}->{:#x}", sr, prev, next),
        }
    }
}

impl MachineStateDiff {
    pub fn diff<F: FnMut(MachineStateDiff) -> ()>(
        prev_state: &MachineState,
        next_state: &MachineState,
        mut process_diff: F,
    ) {
        for gpr in 0b000u8..=0b111u8 {
            let gpr: WordReg = WordReg::try_from(gpr).unwrap();
            let prev = prev_state.gprs[gpr as usize];
            let next = next_state.gprs[gpr as usize];
            if prev != next {
                let diff = MachineStateDiff::Gpr(gpr, prev, next);
                process_diff(diff);
            }
        }
        for sr in 0b00u8..=0b11u8 {
            let sr: SegmentReg = SegmentReg::try_from(sr).unwrap();
            let prev = prev_state.srs[sr as usize];
            let next = next_state.srs[sr as usize];
            if prev != next {
                let diff = MachineStateDiff::Sr(sr, prev, next);
                process_diff(diff);
            }
        }
    }
}