use crate::address_mode::AddressMode; use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR, SIZE_64KB}; use crate::instruction::Instruction; use crate::mos6502flags::{Mos6502Flag, Mos6502Flags}; use crate::mos6502flags::Mos6502Flag::{Carry, Decimal, Interrupt, Overflow}; use crate::op_info::OpInfo; use crate::operand::Operand; use crate::operation::Operation; pub struct Mos6502Cpu { // this is public for rendering quickly. pub memory: Box<[u8]>, a: u8, x: u8, y: u8, flags: Mos6502Flags, pc: u16, s: u8, pub microcode_step: u8, pub address_bus: u16, pub data_bus: u8, ir: Instruction, // Instruction Register oi: OpInfo, has_reset: bool, iv: u16 // Interrupt Vector } impl Default for Mos6502Cpu { fn default() -> Self { let vec = vec![0x00; SIZE_64KB]; let boxed_slize: Box<[u8]> = vec.into_boxed_slice(); 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: Default::default(), pc: 0xfffd, s: 0, microcode_step: 0, address_bus: 0, data_bus: 0, 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 }; working.reset_cpu(); working } } impl Mos6502Cpu { pub fn new() -> Mos6502Cpu { let vec = vec![0x00; SIZE_64KB]; let boxed_slize: Box<[u8]> = vec.into_boxed_slice(); 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 }; working.reset_cpu(); working } fn reset_cpu(&mut self) { // self = &mut Mos6502Cpu::default(); println!("Should tick 7 times."); // read the value at 0xfffc 0xfffd for our reset vector. // read the value at 0xfffe 0xffff for our int vector self.pc = self.read_word(&OFFSET_RESET_VECTOR); self.iv = self.read_word(&OFFSET_INT_VECTOR); println!("PC and IV are now set from ROM addresses"); } fn read_word(&self, offset: &u16) -> u16 { let low = self.memory[*offset as usize]; let high = self.memory[*offset as usize + 1]; (high as u16) << 8 | low as u16 } pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool { self.flags.flag(flag_to_read) } pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) { if new_value { self.flags.set_flag(flag_to_set) } else { self.flags.clear_flag(flag_to_set) } } pub fn peek(&self, offset: u16) -> u8 { self.memory[offset as usize] } pub fn poke(&mut self, offset: u16, value: u8) { self.memory[offset as usize] = value } pub fn peek_a(&self) -> u8 { self.a } pub fn poke_a(&mut self, new_a: u8) { self.a = new_a; } pub fn peek_x(&self) -> u8 { self.x } pub fn poke_x(&mut self, new_x: u8) { println!("Updating register X from [{}] to [{}]", self.x, new_x); self.x = new_x } pub fn peek_y(&self) -> u8 { self.y } pub fn poke_y(&mut self, new_y: u8) { self.y = new_y } fn advance_pc(&mut self, how_far: u16) { self.pc += how_far; } fn set_pc_to(&mut self, new_pc: u16) { self.pc = new_pc; } /// Ticks the CPU /// Returns /// AddressBus, DataBus, RW flag pub fn tick(&mut self) -> (u16, u8, bool) { println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc); if self.microcode_step == 0 { println!("OUT OF MICROSTEPS. Decoding the next instruction"); let offset = self.pc as usize; // TODO: this calls opinfo 2x self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap(); self.ir = Instruction::decode(&self.memory[offset..offset + 4]).unwrap(); self.microcode_step = self.oi.cycles; println!("Decoded [[{:?}]]", self.ir); self.advance_pc(self.oi.length as u16); // load the microstep buffer with what steps to run // set the counter to the number of steps left } else { // run 1 microcode step println!("Microstep {} for {:?}", self.microcode_step, self.ir.op); match self.ir.op { Operation::ADC => { } Operation::AND => {} Operation::ASL => {} Operation::BCC => {} Operation::BCS => {} Operation::BEQ => {} Operation::BIT => {} Operation::BMI => {} Operation::BNE => {} Operation::BPL => {} Operation::BRK => {} Operation::BVC => {} Operation::BVS => {} Operation::CLC => { self.flags.clear_flag(Carry); } Operation::CLD => { self.flags.clear_flag(Decimal); } Operation::CLI => { self.flags.clear_flag(Interrupt); } Operation::CLV => { self.flags.clear_flag(Overflow); } Operation::CMP => {} Operation::CPX => {} Operation::CPY => {} Operation::DEC => {} Operation::DEX => { if self.microcode_step == 1 { let (new_x, new_carry) = self.x.overflowing_sub(1); self.poke_x(new_x); self.poke_flag(Carry, new_carry); } } Operation::DEY => { if self.microcode_step == 1 { (self.y, _) = self.y.overflowing_sub(1); } } Operation::EOR => { } Operation::INC => { } Operation::INX => { if self.microcode_step == 1 { let (new_x, new_carry) = self.x.overflowing_add(1); self.poke_x(new_x); self.poke_flag(Carry, new_carry); } } Operation::INY => { if self.microcode_step == 1 { let (new_y, new_carry) = self.y.overflowing_add(1); self.poke_y(new_y); self.poke_flag(Carry, new_carry); } } Operation::JMP => { match self.ir.operand { Operand::Word(offset) => { self.pc = offset; } _ => {} } } Operation::JSR => {} Operation::LDA => { match self.oi.mode { AddressMode::Immediate => { match self.ir.operand { Operand::Byte(value) => { println!("Loading 0x{value:02x} ({value}) into A"); self.a = value; } _ => {} } } AddressMode::ZeroPage => { match self.ir.operand { Operand::Byte(value) => { println!("Loading from zero page at 0x{value:02x} ({value})"); self.a = self.memory[value as usize]; } _ => {} } } AddressMode::ZeroPageX => {} AddressMode::ZeroPageY => {} AddressMode::Absolute => {} AddressMode::AbsoluteX => {} AddressMode::AbsoluteY => {} AddressMode::Indirect => {} AddressMode::IndirectX => {} AddressMode::IndirectY => {} _ => { println!("INVALID ADDRESS MODE FOR LDA"); } } } Operation::LDX => {} Operation::LDY => {} Operation::LSR => {} Operation::NOP => { // do nothing. } Operation::ORA => {} Operation::PHA => {} Operation::PHP => {} Operation::PLA => {} Operation::PLP => {} Operation::ROL => { if self.microcode_step == 1 { self.a = self.a.rotate_left(1); } } Operation::ROR => { // rotate A if self.microcode_step == 1 { self.a = self.a.rotate_right(1); } } Operation::RTI => {} Operation::RTS => {} Operation::SBC => {} Operation::SEC => { self.flags.set_flag(Carry); } Operation::SED => { self.flags.set_flag(Decimal); } Operation::SEI => { self.flags.set_flag(Interrupt); } Operation::STA => { match self.oi.mode { AddressMode::ZeroPage => { // write to the zero page. match self.ir.operand { Operand::Byte(target) => { self.memory[target as usize] = self.a; } _ => { // Invalid parameter } } } AddressMode::ZeroPageX => { match self.ir.operand { Operand::Byte(target) => { let x = self.x; self.memory[(x + target) as usize] = self.a; } _ => { // Invalid Parameter } } } AddressMode::Absolute => { // write from A to the specified memory location match self.ir.operand { Operand::Word(offset) => { self.memory[offset as usize] = self.a; } _ => { // Invalid Parameter } } } AddressMode::AbsoluteX => { match self.ir.operand { Operand::Word(offset) => { self.memory[(offset + self.x as u16) as usize] = self.a; } _ => { // Invalid Parameter } } } AddressMode::AbsoluteY => {} AddressMode::IndirectX => {} AddressMode::IndirectY => {} _ => { // invalid memory mode } } } Operation::STX => {} Operation::STY => {} Operation::TAX => { self.x = self.a; } Operation::TAY => { self.y = self.a; } Operation::TSX => { } Operation::TXA => { self.a = self.x; } Operation::TXS => { } Operation::TYA => { self.y = self.a; } } self.microcode_step -= 1; } (0,0,false) } pub fn dump(&self) { println!("CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x} / MICROSTEPS: {:02x} / S: {}", self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step, self.flags.dump()); } pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8, u8) { (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 super::*; #[test] fn clc() { // setup the CPU for our test let mut cpu = Mos6502Cpu::default(); cpu.flags.set_flag(Carry); // Load our 'test program' cpu.memory[0x6000] = ISA_OP_CLC; // Start the PC at our program cpu.pc = 0x6000; // Tick the CPU through the instruction for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLC as usize].unwrap().cycles { cpu.tick(); } assert!(!cpu.peek_flag(Carry)); } #[test] fn cld() { let mut cpu = Mos6502Cpu::default(); cpu.flags.set_flag(Decimal); cpu.memory[0x6000] = ISA_OP_CLD; cpu.pc = 0x6000; for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLD as usize].unwrap().cycles { cpu.tick(); } assert!(!cpu.peek_flag(Decimal)); } #[test] fn cli() { let mut cpu = Mos6502Cpu::default(); cpu.flags.set_flag(Interrupt); cpu.memory[0x6000] = ISA_OP_CLI; cpu.pc = 0x6000; for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLI as usize].unwrap().cycles { cpu.tick(); } assert!(!cpu.peek_flag(Interrupt)); } #[test] fn clv() { let mut cpu = Mos6502Cpu::default(); cpu.flags.set_flag(Overflow); cpu.memory[0x6000] = ISA_OP_CLV; cpu.pc = 0x6000; for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLV as usize].unwrap().cycles { cpu.tick(); } assert!(!cpu.peek_flag(Overflow)); } #[test] fn lda_immediate() { let mut cpu = Mos6502Cpu::default(); cpu.memory[0x6000] = ISA_OP_LDA_I; cpu.memory[0x6001] = 0xab; cpu.pc = 0x6000; for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_I as usize].unwrap().cycles { cpu.tick(); } assert_eq!(cpu.a, 0xab); } #[test] fn lda_zeropage() { let mut cpu = Mos6502Cpu::default(); cpu.memory[0x6000] = ISA_OP_LDA_Z; cpu.memory[0x6001] = 0xab; cpu.memory[0x00ab] = 0xbe; cpu.pc = 0x6000; for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_Z as usize].unwrap().cycles + 1 { cpu.tick(); } assert_eq!(cpu.a, 0xbe); } #[test] fn dex_inx() { let mut cpu = Mos6502Cpu::default(); cpu.x = 0xab; cpu.memory[0x6000] = ISA_OP_DEX; cpu.memory[0x6001] = ISA_OP_INX; cpu.pc = 0x6000; for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEX as usize].unwrap().cycles { cpu.tick(); } assert_eq!(0xaa, cpu.x); for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INX as usize].unwrap().cycles { cpu.tick(); } assert_eq!(0xab, cpu.x); } #[test] fn dey_iny() { let mut cpu = Mos6502Cpu::default(); cpu.poke_y(0xab); cpu.memory[0x6000] = ISA_OP_DEY; cpu.memory[0x6001] = ISA_OP_INY; cpu.pc = 0x6000; for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEY as usize].unwrap().cycles { cpu.tick(); } assert_eq!(0xaa, cpu.peek_y()); for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INY as usize].unwrap().cycles { cpu.tick(); } assert_eq!(0xab, cpu.peek_y()); } #[test] fn rol_a_ror_a() { let mut cpu = Mos6502Cpu::default(); cpu.poke_a(0b1010_1010); // 0xaa cpu.memory[0x6000] = ISA_OP_ROL_A; 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(); } assert_eq!(cpu.peek_a(), 0b0101_0101); for _ in 0..=INSTRUCTION_TABLE[ISA_OP_ROR_A as usize].unwrap().cycles { cpu.tick(); } assert_eq!(cpu.peek_a(), 0b1010_1010); } }