diff --git a/beneater/src/parts/ben_eater_pc.rs b/beneater/src/parts/ben_eater_pc.rs index f401481..5d7e1fc 100644 --- a/beneater/src/parts/ben_eater_pc.rs +++ b/beneater/src/parts/ben_eater_pc.rs @@ -23,7 +23,7 @@ impl BenEaterPC { } pub fn tick_system(&mut self) { - let (address, data, rw) = self.cpu.tick(); + self.cpu.tick(); if self.cpu.microcode_step == 0 { // tick the clock. // tick the memory diff --git a/beneater/src/parts/display_matrix.rs b/beneater/src/parts/display_matrix.rs index 80026b6..66a1bb0 100644 --- a/beneater/src/parts/display_matrix.rs +++ b/beneater/src/parts/display_matrix.rs @@ -1,6 +1,14 @@ use macroquad::prelude::*; use crate::parts::address_bus::AddressBus; use crate::parts::data_bus::DataBus; +use crate::parts::display_matrix::MatrixEntryMode::{Delete, DeleteShift, Insert, InsertShift}; + +pub enum MatrixEntryMode { + Delete, + DeleteShift, + Insert, + InsertShift, +} pub struct DisplayMatrix { width: f32, @@ -9,42 +17,138 @@ pub struct DisplayMatrix { data_bus: DataBus, rs: bool, rw: bool, - cursor_position: u8 + cursor_position: u8, + busy: bool, + entry_mode: MatrixEntryMode, + cursor: bool, + on: bool, + blink: bool, + cursor_shift: bool, + display_shift: bool, + enabled: bool, } impl DisplayMatrix { + pub fn set_bus(&mut self, rs: bool, rw: bool, bus: u8, e: bool) { + self.rs = rs; + self.rw = rw; + self.data_bus.data = bus; + self.enabled = e; + } + + /// Reset the Display to its 'factory boot' state + pub fn reset(&mut self) { + // 1. Display clear + self.set_bus(false, false, 0b0000_0001, true); + self.tick(false, false, true, 0b0000_0001); + // Function Set + // DL = 1; 8-bit interface data + // N = 0; 1-line display + // F = 0; 5 × 8 dot character font + self.tick(false, false, true, 0b0011_0000);; + // 3. Display on/off control: + // D = 0; Display off + // C = 0; Cursor off + // B = 0; Blinking off + self.tick(false, false, true, 0b0000_1000); + // 4. Entry mode set: + // I/D = 1; Increment by 1 + // S = 0; No shift + self.tick(false, false,true, 0b0000_0110); + self.busy = false; + } + pub fn new(width: f32, height: f32) -> DisplayMatrix { - DisplayMatrix { + let mut matrix = DisplayMatrix { width, height, text_buffer: String::from(""), data_bus: DataBus::new(), rs: false, rw: false, - cursor_position: 0x00 - } + cursor_position: 0x00, + busy: false, + entry_mode: MatrixEntryMode::InsertShift, + on: false, + cursor: false, + blink: false, + cursor_shift: false, + display_shift: false, + enabled: false, + }; + matrix } /// Tick - /// - /// Checks the data bus and sees what the setup is. - /// - /// 0 0 0 0 0 0 0 1 -> Clear Display - /// 0 0 0 0 0 0 1 - -> Return Home - /// 0 0 0 0 0 0 A B -> Sets cursor move direction and shift - /// A -> 0 = Insert, 1 = Delete - /// B -> Scroll bool - /// 0 0 0 0 1 D C B -> Sets display mode - /// D -> Display On/Off - /// C -> Cursor On/Off - /// B -> Blink - pub fn tick(&mut self) { + pub fn tick(&mut self, rw: bool, rs: bool, enabled: bool, data: u8) { + self.enabled = enabled; + self.data_bus.data = data; + self.rw = rw; + self.rs = rs; // parse whats on the data bus. - - } - - pub fn set_busses(&mut self, address_bus: &mut AddressBus, data_bus: &mut DataBus) { - + match (self.rs, self.rw) { + (true, true) => { + // read from cg or ddram + } + (true, false) => { + // write to cg or ddram + println!("WRITE TO CG/DD RAM -> {:08b} / {:02x} / {}", self.data_bus.data, self.data_bus.data, self.data_bus.data); + } + (false, true) => { + // read the busy flag. + self.data_bus.data = (self.busy as u8) << 7 | (self.cursor_position & 0x7F); + self.enabled = true; + } + (false, false) => { + match self.data_bus.data { + 0b0000_0001 => { + // clear display + self.text_buffer = " ".repeat(32); + } + 0b0000_0010..=0b0000_0011 => { + // return home + self.cursor_position = 0x00; + } + 0b0000_0100..=0b0000_0111 => { + self.entry_mode = match self.data_bus.data { + 0b01 => DeleteShift, + 0b10 => Insert, + 0b11 => InsertShift, + _ => { + // Default mode + Delete + } + }; + } + 0b0000_1000..=0b0000_1111 => { + // display control + self.on = self.data_bus.data & 0b100 == 0b100; + self.cursor = self.data_bus.data & 0b10 == 0b10; + self.blink = self.data_bus.data & 0b1 == 0b1; + } + 0b0001_0000..=0b0001_1111 => { + // cursor control + self.cursor_shift = self.data_bus.data & 0b1000 == 0b1000; + self.display_shift = self.data_bus.data & 0b100 == 0b100; + } + 0b0010_0000..=0b0011_1111 => { + // function set + println!("Ignoring function set. this is going to play like an 8bit 2 row"); + } + 0b0100_0000..=0b0111_1111 => { + // set CGRam Address + println!("Ignoring set CGRam Address. Not Changing how the characters are displayed."); + } + 0b1000_0000..=0b1111_1111 => { + // set DDRam address + self.cursor_position = self.data_bus.data & 0b0111_1111; + } + _ => { + // Invalid Command + } + } + } + } } pub fn push_letter(&mut self, letter_to_push: char) { @@ -60,7 +164,7 @@ impl DisplayMatrix { pub fn render(&self, x_offset: f32, y_offset: f32) { DisplayMatrix::draw_square(x_offset, y_offset, - x_offset + self.width , + x_offset + self.width, y_offset + self.height, BLACK); @@ -76,4 +180,17 @@ impl DisplayMatrix { draw_line(x1, y2, x2, y2, 1.0, color); draw_line(x2, y1, x2, y2, 1.0, color); } -} \ No newline at end of file +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn smoke() { assert!(true); } + + #[test] + fn device_init() { + let display = DisplayMatrix::new(100.0, 100.0); + } +} diff --git a/core/src/instruction_table.rs b/core/src/instruction_table.rs index 2aac88d..808b5e8 100644 --- a/core/src/instruction_table.rs +++ b/core/src/instruction_table.rs @@ -4,6 +4,15 @@ use crate::op_info::OpInfo; use crate::operation::Operation; use crate::operation::Operation::*; + +pub fn INSTRUCTION_CYCLES(instruction: u8) -> u8 { + INSTRUCTION_TABLE[instruction as usize].unwrap().cycles +} + +pub fn INSTRUCTION_LENGTH(instruction: u8) -> u8 { + INSTRUCTION_TABLE[instruction as usize].unwrap().length +} + pub const INSTRUCTION_TABLE: [Option; 256] = { let mut table: [Option; 256] = [const { None }; 256]; diff --git a/core/src/mos6502cpu.rs b/core/src/mos6502cpu.rs index 3c281ee..7be3c70 100644 --- a/core/src/mos6502cpu.rs +++ b/core/src/mos6502cpu.rs @@ -1,11 +1,13 @@ use crate::address_mode::AddressMode; -use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR, SIZE_64KB}; +use crate::constants::constants_isa_op::ISA_OP_NOP; use crate::instruction::Instruction; use crate::mos6502flags::{Mos6502Flag, Mos6502Flags}; -use crate::mos6502flags::Mos6502Flag::{Carry, Decimal, Interrupt, Overflow}; +use crate::mos6502flags::Mos6502Flag::*; use crate::op_info::OpInfo; use crate::operand::Operand; use crate::operation::Operation; +use crate::constants::constants_system::*; +use crate::instruction_table::INSTRUCTION_TABLE; pub struct Mos6502Cpu { // this is public for rendering quickly. @@ -22,7 +24,9 @@ pub struct Mos6502Cpu { ir: Instruction, // Instruction Register oi: OpInfo, has_reset: bool, - iv: u16 // Interrupt Vector + iv: u16, // Interrupt Vector + cycle_carry: u16, // Value to hold between microsteps + ir_bytes: Box<[u8]> } impl Default for Mos6502Cpu { @@ -33,28 +37,25 @@ impl Default for Mos6502Cpu { let mut working = Mos6502Cpu { memory: boxed_array, - a: 0, - x: 0, - y: 0, + a: 0x00, + x: 0x00, + y: 0x00, flags: Default::default(), pc: 0xfffd, - s: 0, - microcode_step: 0, - address_bus: 0, - data_bus: 0, + s: 0x00, + microcode_step: 0x00, + address_bus: 0x00, + data_bus: 0x00, ir: Instruction { op: Operation::NOP, mode: AddressMode::Implied, operand: Operand::None, }, - oi: OpInfo { - operation: Operation::NOP, - mode: AddressMode::Implied, - length: 1, - cycles: 2, - }, + oi: INSTRUCTION_TABLE[ISA_OP_NOP as usize].unwrap(), has_reset: false, - iv: 0xfffe + iv: 0xfffe, + cycle_carry: 0x0000, + ir_bytes: [].into() }; working.reset_cpu(); working @@ -68,28 +69,8 @@ impl Mos6502Cpu { let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory"); let mut working = Mos6502Cpu { memory: boxed_array, - a: 0, - x: 0, - y: 0, - flags: Mos6502Flags::default(), - pc: 0, - s: 0xfd, - microcode_step: 0, - address_bus: 0x0000, - data_bus: 0x00, - ir: Instruction { - op: Operation::NOP, - mode: AddressMode::Implied, - operand: Operand::None, - }, - oi: OpInfo { - operation: Operation::NOP, - mode: AddressMode::Implied, - length: 1, - cycles: 2, - }, - has_reset: false, - iv: 0xfffe + ir_bytes: [].into(), + ..Default::default() }; working.reset_cpu(); working @@ -159,9 +140,8 @@ impl Mos6502Cpu { } /// Ticks the CPU - /// Returns - /// AddressBus, DataBus, RW flag - pub fn tick(&mut self) -> (u16, u8, bool) { + pub fn tick(&mut self) { + println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc); if self.microcode_step == 0 { println!("OUT OF MICROSTEPS. Decoding the next instruction"); @@ -176,9 +156,27 @@ impl Mos6502Cpu { // set the counter to the number of steps left } else { // run 1 microcode step - println!("Microstep {} for {:?}", self.microcode_step, self.ir.op); + println!("Microstep {}/{} for {:?}", self.microcode_step, self.oi.cycles, self.ir.op); match self.ir.op { Operation::ADC => { + match self.microcode_step { + 1 => { + match self.ir.mode { + AddressMode::Immediate => {} + AddressMode::ZeroPage => {} + AddressMode::ZeroPageX => {} + AddressMode::Absolute => {} + AddressMode::AbsoluteX => {} + AddressMode::AbsoluteY => {} + AddressMode::Indirect => {} + AddressMode::IndirectX => {} + AddressMode::IndirectY => {} + _ => {} + } + }, + 2 => {}, + _ => {} + } } Operation::AND => {} Operation::ASL => {} @@ -207,7 +205,65 @@ impl Mos6502Cpu { Operation::CMP => {} Operation::CPX => {} Operation::CPY => {} - Operation::DEC => {} + Operation::DEC => { + match self.microcode_step { + // DEC Step 1 + 1 => { + let working_value = match self.oi.mode { + AddressMode::ZeroPage => { + // read from + let offset = match self.ir.operand { + Operand::Byte(z) => { + z + } + _ => { 0x00 } + }; + println!("READING FROM MEMORY AT 0x{offset:04x}"); + self.memory[offset as usize] + // self.peek(offset); + } + AddressMode::ZeroPageX => { + let offset = match self.ir.operand { + Operand::Byte(z) => { + z + } + _ => { 0x00 } + }; + // self.memory.peek(offset + self.x); + self.memory[offset as usize] + } + AddressMode::Absolute => { + let offset = match self.ir.operand { + Operand::Word(offset) => { + offset + } + _ => { 0x00 } + }; + // self.memory.peek(offset) + self.memory[offset as usize] + } + AddressMode::AbsoluteX => { + let offset = match self.ir.operand { + Operand::Word(offset) => { + offset + } + _ => { 0x00 } + }; + // self.memory.peek(offset + self.x); + self.memory[offset as usize] + } + _ => { + 0x00 + } + }; + } + // DEC write memory + 2 => { + self.a = self.cycle_carry as u8; + } + _ => {} + } + } Operation::DEX => { if self.microcode_step == 1 { let (new_x, new_carry) = self.x.overflowing_sub(1); @@ -264,10 +320,28 @@ impl Mos6502Cpu { _ => {} } } - AddressMode::ZeroPageX => {} + AddressMode::ZeroPageX => { + match self.ir.operand { + Operand::Byte(value) => { + let x_offset = self.x; + self.a = self.memory[(value + x_offset) as usize]; + } + _ => {} + } + } AddressMode::ZeroPageY => {} - AddressMode::Absolute => {} - AddressMode::AbsoluteX => {} + AddressMode::Absolute => { + if let Operand::Word(offset) = self.ir.operand { + println!("Loading from absolute address 0x{offset:04x}"); + self.a = self.memory[offset as usize]; + } + + } + AddressMode::AbsoluteX => { + if let Operand::Word(offset) = self.ir.operand { + self.a = self.memory[(offset + self.x as u16) as usize]; + } + } AddressMode::AbsoluteY => {} AddressMode::Indirect => {} AddressMode::IndirectX => {} @@ -385,8 +459,6 @@ impl Mos6502Cpu { } self.microcode_step -= 1; } - - (0,0,false) } pub fn dump(&self) { @@ -398,16 +470,12 @@ impl Mos6502Cpu { (self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step) } - fn run_microstep(&self, instruction: Instruction, step: u8) { - - } } - #[cfg(test)] mod test { use crate::constants::constants_isa_op::*; - use crate::instruction_table::INSTRUCTION_TABLE; + use crate::instruction_table::{INSTRUCTION_CYCLES, INSTRUCTION_TABLE}; use super::*; #[test] @@ -445,7 +513,7 @@ mod test { cpu.memory[0x6000] = ISA_OP_CLI; cpu.pc = 0x6000; - for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLI as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLI) { cpu.tick(); } assert!(!cpu.peek_flag(Interrupt)); @@ -458,7 +526,7 @@ mod test { cpu.memory[0x6000] = ISA_OP_CLV; cpu.pc = 0x6000; - for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLV as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLV) { cpu.tick(); } assert!(!cpu.peek_flag(Overflow)); } @@ -470,24 +538,73 @@ mod test { cpu.memory[0x6001] = 0xab; cpu.pc = 0x6000; - for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_I as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_I) { cpu.tick(); } assert_eq!(cpu.a, 0xab); } + #[test] + fn lda_zx() { + let mut cpu = Mos6502Cpu::default(); + cpu.poke_x(1); + cpu.memory[0x6000] = ISA_OP_LDA_ZX; + cpu.memory[0x6001] = 0xab; + cpu.memory[0x00ac] = 0xbe; + cpu.pc = 0x6000; + + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ZX) { cpu.tick(); }; + + // println!("MEMORY AT 0x00aa, ab, ac, ad, ae -> {:02x} {:02x} {:02x} {:02x} {:02x}", cpu.memory[0x00aa], cpu.memory[0x00ab], cpu.memory[0x00ac], cpu.memory[0x00ad], cpu.memory[0x00ae]); + // cpu.dump(); + assert_eq!(cpu.peek_a(), 0xbe); + assert!(!cpu.peek_flag(Zero)); + assert!(!cpu.peek_flag(Carry)); + assert!(!cpu.peek_flag(Negative)); + } + #[test] fn lda_zeropage() { let mut cpu = Mos6502Cpu::default(); cpu.memory[0x6000] = ISA_OP_LDA_Z; cpu.memory[0x6001] = 0xab; + // Load ZeroPage cpu.memory[0x00ab] = 0xbe; cpu.pc = 0x6000; - for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_Z as usize].unwrap().cycles + 1 { cpu.tick(); } + for _ in 0..INSTRUCTION_CYCLES(ISA_OP_LDA_Z) { cpu.tick(); } assert_eq!(cpu.a, 0xbe); } + #[test] + fn lda_absolute() { + let mut cpu = Mos6502Cpu::default(); + cpu.memory[0x6000] = ISA_OP_LDA_ABS; + cpu.memory[0x6001] = 0xef; + cpu.memory[0x6002] = 0xbe; + cpu.memory[0xbeef] = 0xab; + cpu.pc = 0x6000; + + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABS) { cpu.tick(); } + + assert_eq!(cpu.a, 0xab); + } + + #[test] + fn lda_absolutex() { + let mut cpu = Mos6502Cpu::default(); + cpu.memory[0x6000] = ISA_OP_LDA_ABSX; + cpu.memory[0x6001] = 0xef; + cpu.memory[0x6002] = 0xbe; + cpu.poke_x(0x01); + cpu.memory[0xbef0] = 0xab; + cpu.pc = 0x6000; + + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABS) { cpu.tick(); } + + assert_eq!(cpu.a, 0xab); + } + #[test] fn dex_inx() { let mut cpu = Mos6502Cpu::default(); @@ -496,9 +613,9 @@ mod test { cpu.memory[0x6001] = ISA_OP_INX; cpu.pc = 0x6000; - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEX as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEX) { cpu.tick(); } assert_eq!(0xaa, cpu.x); - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INX as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INX) { cpu.tick(); } assert_eq!(0xab, cpu.x); } @@ -510,9 +627,9 @@ mod test { cpu.memory[0x6001] = ISA_OP_INY; cpu.pc = 0x6000; - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEY as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEY) { cpu.tick(); } assert_eq!(0xaa, cpu.peek_y()); - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INY as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INY) { cpu.tick(); } assert_eq!(0xab, cpu.peek_y()); } @@ -524,9 +641,9 @@ mod test { cpu.memory[0x6001] = ISA_OP_ROR_A; cpu.pc = 0x6000; - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_ROL_A as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROL_A) { cpu.tick(); } assert_eq!(cpu.peek_a(), 0b0101_0101); - for _ in 0..=INSTRUCTION_TABLE[ISA_OP_ROR_A as usize].unwrap().cycles { cpu.tick(); } + for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROR_A) { cpu.tick(); } assert_eq!(cpu.peek_a(), 0b1010_1010); } -} \ No newline at end of file +} diff --git a/core/src/operation.rs b/core/src/operation.rs index 1d420ad..5702bd5 100644 --- a/core/src/operation.rs +++ b/core/src/operation.rs @@ -5,117 +5,121 @@ pub enum Operation { /// /// Affects flags: N, V, Z, C /// - /// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY + /// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4), + /// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5) ADC, /// Logical AND with Accumulator /// /// Affects flags: N, Z /// - /// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY + /// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4), + /// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5) AND, /// Arithmetic Shift Left /// /// Affects flags: N, Z, C /// - /// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX + /// Addressing Modes: Accumulator (1/2), ZeroPage (2/5), ZeroPageX (2/6), Absolute (3/6), + /// AbsoluteX (3/7) ASL, /// Branch if Carry Clear /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BCC, /// Branch if Carry Set /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BCS, /// Branch if Equal (Zero Set) /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BEQ, /// Bit Test /// /// Affects flags: N, V, Z /// - /// Addressing Modes: ZeroPage, Absolute + /// Addressing Modes: ZeroPage (2/3), Absolute (3/4) BIT, /// Branch if Minus (Negative Set) /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BMI, /// Branch if Not Equal (Zero Clear) /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BNE, /// Branch if Positive (Negative Clear) /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BPL, /// Force Interrupt /// /// Affects flags: B /// - /// Addressing Modes: Implied + /// Addressing Modes: Implied (1/7) BRK, /// Branch if Overflow Clear /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BVC, /// Branch if Overflow Set /// - /// Addressing Modes: Relative + /// Addressing Modes: Relative (2/2) BVS, /// Clear Carry Flag /// /// Affects flags: C /// - /// Addressing Modes: Implied + /// Addressing Modes: Implied (1/2) CLC, /// Clear Decimal Mode /// /// Affects flags: D /// - /// Addressing Modes: Implied + /// Addressing Modes: Implied (1/2) CLD, /// Clear Interrupt Disable /// /// Affects flags: I /// - /// Addressing Modes: Implied + /// Addressing Modes: Implied (1/2) CLI, /// Clear Overflow Flag /// /// Affects flags: V /// - /// Addressing Modes: Implied + /// Addressing Modes: Implied (2/2) CLV, /// Compare Accumulator /// /// Affects flags: N, Z, C /// - /// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY + /// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4), + /// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5) CMP, /// Compare X Register /// /// Affects flags: N, Z, C /// - /// Addressing Modes: Immediate, ZeroPage, Absolute + /// Addressing Modes: Immediate (2/2), ZeroPage (2/3), Absolute (3/4) CPX, /// Compare Y Register diff --git a/core/tests/execution_tests.rs b/core/tests/execution_tests.rs new file mode 100644 index 0000000..e69de29