phakebit 0.1.4

A 6502 CPU emulator
Documentation
//! Contains the `Trace` struct and functions for printing it to stdout.

use crate::instruction;
use crate::instruction::AddressingMode;
use crate::instruction::Instruction;

/// Represents the state of the CPU after executing an instruction.
pub struct Trace {
    /// Address of the instruction that was just executed.
    pub pc: u16,
    /// A register value
    pub a: u8,
    /// X register value
    pub x: u8,
    /// Y register value
    pub y: u8,
    /// Stack pointer value
    pub sp: u8,
    /// Status register value
    pub sr: u8,
    /// The instruction that was executed
    pub instruction: Instruction,
    /// Possible operand of the instruction
    pub operand: Option<u16>,
}

impl Trace {
    pub fn new(
        pc: u16,
        a: u8,
        x: u8,
        y: u8,
        sp: u8,
        sr: u8,
        instruction: Instruction,
        operand: Option<u16>,
    ) -> Trace {
        Trace {
            pc: pc,
            a: a,
            x: x,
            y: y,
            sp: sp,
            sr: sr,
            instruction: instruction,
            operand: operand,
        }
    }

    /// Prints the trace to stdout in the format of
    /// ```text
    /// PC   Op Oper   Disassembly   |A  X  Y  SP|NVDIZC|C
    /// ```
    pub fn print(&self) {
        let n_flag = (self.sr >> 7) & 1;
        let v_flag = (self.sr >> 6) & 1;
        let d_flag = (self.sr >> 3) & 1;
        let i_flag = (self.sr >> 2) & 1;
        let z_flag = (self.sr >> 1) & 1;
        let c_flag = self.sr & 1;

        let name: String = format_operation(self.instruction.operation);
        let operand: String = match self.operand {
            Some(operand) => format_operand(operand, self.pc, self.instruction.mode),
            None => "".to_string(),
        };

        let operand_asm: String = match self.operand {
            Some(operand) => {
                if operand > 0xFF {
                    let hi = (operand >> 8) as u8;
                    let lo = (operand & 0xFF) as u8;
                    format!("{:02X} {:02X}", lo, hi)
                } else {
                    format!("{:02X}   ", operand)
                }
            }
            None => "     ".to_string(),
        };

        println!(
            "{:04X} {:02X} {}  {} {:<9} |{:02X} {:02X} {:02X} {:02X}|{}{}{}{}{}{}|{} ",
            self.pc,
            self.instruction.opcode,
            operand_asm,
            name,
            operand,
            self.a,
            self.x,
            self.y,
            self.sp,
            n_flag,
            v_flag,
            d_flag,
            i_flag,
            z_flag,
            c_flag,
            self.instruction.cycles,
        );
    }
}

fn format_operation(operation: instruction::Operation) -> String {
    format!("{:?}", operation)
}

fn format_operand(operand: u16, pc: u16, mode: instruction::AddressingMode) -> String {
    match mode {
        AddressingMode::ACC => "A".to_string(),
        AddressingMode::ABS => format!("${:04X}", operand),
        AddressingMode::ABSX => format!("${:04X},X", operand),
        AddressingMode::ABSY => format!("${:04X},Y", operand),
        AddressingMode::IMM => format!("#${:02X}", operand),
        AddressingMode::IMPL => "".to_string(),
        AddressingMode::IND => format!("(${:04X})", operand),
        AddressingMode::XIND => format!("(${:02X},X)", operand),
        AddressingMode::INDY => format!("(${:02X}),Y", operand),
        AddressingMode::REL => {
            let signed_operand = operand as i8;
            format!(
                "${:04X}",
                // 2 comes from having read the opcode and operand bytes
                pc.wrapping_add(signed_operand as u16).wrapping_add(2)
            )
        }
        AddressingMode::ZPG => format!("${:02X}", operand),
        AddressingMode::ZPGX => format!("${:02X},X", operand),
        AddressingMode::ZPGY => format!("${:02X},Y", operand),
    }
}