diff --git a/src/sixty_five/addr b/src/sixty_five/addr new file mode 100644 index 0000000..54361c7 --- /dev/null +++ b/src/sixty_five/addr @@ -0,0 +1,32 @@ + +# ___0 _0__ +A 0__0 1010 +--------abs--------- + +abs ___0 11__ +absX ___1 11__ +absY ___1 1___ +-------------------- +rel 0000 ___1 +--------ind--------- +ind ___a bb0c + a b c +ind 0 1 0 +X 0 0 1 +Y 1 0 1 + +_: opcode? + +ind 0110 1100 +Xind ___0 0001 +indY ___1 0001 +-------------------- +zpg ___0 01__ +zpgX ___1 01__ +zpgY 10_1 0110 + + + +___a bb_c +c => uses X|Y +a => x / y \ No newline at end of file diff --git a/src/sixty_five/addressing_modes.rs b/src/sixty_five/addressing_modes.rs new file mode 100644 index 0000000..6228147 --- /dev/null +++ b/src/sixty_five/addressing_modes.rs @@ -0,0 +1,24 @@ +pub fn get_size(addr_mode: AddressingMode) -> usize { + OP_SIZES[addr_mode as usize] +} + +//A,abs,absX,absY,imm,impl,ind,indX,indY,rel,zpg,zpgX,zpgY +//1, 3, 3, 3, 2, 1, 3, 2, 2, 2, 2, 2, 2 +pub static OP_SIZES: [usize; 13] = [1, 3, 3, 3, 2, 1, 3, 2, 2, 2, 2, 2, 2]; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum AddressingMode { + A = 0, // LSR A + ABS, // LDA $1234 + ABSX, // STA $3000,X + ABSY, // AND $4000,Y + IMM, // LDA #$10 + IMPL, // CLC + IND, // JMP ($FFFC) + INDX, // LDA ($40,X) + INDY, // LDA ($40),Y + REL, // LABEL // +4 + ZPG, // LDA $10 + ZPGX, // LDA $10,X + ZPGY, // LDA $10,Y +} diff --git a/src/sixty_five/cpu.rs b/src/sixty_five/cpu.rs new file mode 100644 index 0000000..094dacf --- /dev/null +++ b/src/sixty_five/cpu.rs @@ -0,0 +1,68 @@ +#[repr(u8)] +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum Flags { + Negative = 128, + Overflow = 64, + AlwaysOne = 32, + Break = 16, + Decimal = 8, + Int = 4, + Zero = 2, + Carry = 1, +} +#[allow(non_snake_case)] +pub struct Cpu { + pub A: u8, + pub X: u8, + pub Y: u8, + pub PC: u16, + pub flags: u8, +} +impl Cpu { + pub fn test(&self, flag: Flags) -> bool { + (self.flags & flag as u8) != 0 + } + pub fn set_flag(&mut self, flag: Flags, status: bool) { + if status { + self.flags |= flag as u8; + } else { + self.flags &= 0xFF - flag as u8; + } + } + pub fn add_a(&mut self, n: u8) { + let n = n as u16; + let a = self.A as u16; + let res = n + a; + if res > 0xFF { + self.set_flag(Flags::Carry, true); + } else { + self.set_flag(Flags::Carry, false); + } + self.set_a((res & 0xFF) as u8); + } + pub fn set_a(&mut self, value: u8) { + self.set_flag(Flags::Zero, value == 0x00); // Set Zero if A is zero + self.set_flag(Flags::Negative, value & 0x80 != 0); // Test sign bit + self.A = value; + } +} +impl std::default::Default for Cpu { + fn default() -> Self { + Self { + A: 0x00, + X: 0x00, + Y: 0x00, + PC: 0x0000, + flags: 0b_0010_0000, + } + } +} +impl std::fmt::Debug for Cpu { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "Registers: \n PC: {:04X}\n A: {:02X} X: {:02X} Y: {:02X}\nNV-BDIZC\n{:08b}", + self.PC, self.A, self.X, self.Y, self.flags + ) + } +} diff --git a/src/sixty_five/emulator.rs b/src/sixty_five/emulator.rs new file mode 100644 index 0000000..564912d --- /dev/null +++ b/src/sixty_five/emulator.rs @@ -0,0 +1,194 @@ +use super::cpu::{Cpu, Flags}; +use super::error; +use super::opcodes; +use super::OpcodeType; +use super::{addressing_modes::get_size, AddressingMode}; + +fn invalid_mode(mode_used: AddressingMode) -> T { + panic!("The addressign mode used ({:?}) is either not valid for this opcode, or expects an argument which was not provided",mode_used) +} + +static RESET_VEC_ADDR: usize = 0xFFFC; + +macro_rules! fetch { + ($self:ident PC+$off:expr) => { + $self.ram[$self.cpu.PC as usize + $off] + }; + ($self:ident $addr:expr) => { + $self.ram[$addr as usize] + }; + ($self:ident D $addr:expr) => { + ($self.ram[$addr as usize + 1] as u16) << 8 | $self.ram[$addr as usize] as u16 + }; +} +macro_rules! operation { + ($self:ident A+$b:expr) => { + $self.cpu.A.wrapping_add($b as u8) + }; + ($self:ident X+$b:expr) => { + $self.cpu.X.wrapping_add($b as u8) + }; + (unwrap $arg:ident $addr:ident) => { + $arg.unwrap_or_else(|| invalid_mode($addr.addr_mode)) + }; +} + +pub struct Emulator { + pub cycles: usize, + pub ram: [u8; 0x10000], + pub cpu: Cpu, +} +impl Emulator { + pub fn new() -> Self { + Self { + cycles: 0, + ram: [0x00; 0x10000], + cpu: Cpu::default(), + } + } + pub fn init(&mut self) -> Result<(), error::EmulatorError> { + let lo: u16 = self.ram[RESET_VEC_ADDR] as u16; + let hi: u16 = self.ram[RESET_VEC_ADDR + 1] as u16; + let addr = hi << 8 | lo; + self.cpu.PC = addr; + Ok(()) + } + pub fn step(&mut self) -> Result<(), error::EmulatorError> { + if self.cycles == 0 { + println!("Initializing"); + self.init()?; + } + if self.cpu.test(Flags::Break) { + return Err(error::EmulatorError::Break); + } + println!("Step on {:04X}", self.cpu.PC); + let code = self.ram[self.cpu.PC as usize]; + let code = match opcodes::from_code(code) { + None => return Err(error::EmulatorError::UnknownOp(code)), + Some(v) => v, + }; + println!(" Opcode {:?}", code.name); + let arg: Option = match code.addr_mode { + AddressingMode::IMPL => None, // No argument + AddressingMode::A => None, // No argument + AddressingMode::IMM => { + // Next byte is the argument + let addr = fetch!(self PC+1); + Some(addr as u16) + } + AddressingMode::ABS => { + // Next 2 bytes are an address from where to fetch the real argument + let addr = self.cpu.PC as usize + 1; + Some(fetch!(self D addr) as u16) + } + AddressingMode::ZPG => { + // Next byte is an address from the range 0x0000-0x00FF + let addr = self.cpu.PC as usize + 1; + Some(fetch!(self addr) as u16) + } + AddressingMode::INDX => { + // Take the next byte and add it to X, + // then use the result as an address and fetch 2 bytes + let arg = fetch!(self PC+1); // Opcode arg + let addr: u8 = operation!(self X+arg); // Zero-page addr + let addr_lo = self.ram[addr as usize] as usize; + let addr_hi = self.ram[addr.wrapping_add(1) as usize] as usize; + let res_addr = addr_hi << 8 | addr_lo; + Some(res_addr as u16) + } + AddressingMode::REL => { + // Add PC with the next byte + let arg = fetch!(self PC+1) as u8 as i8 as isize; + let pc = self.cpu.PC as usize as isize; + let new_pc = (arg + pc) & 0xFFFF; + Some(new_pc as u16) + } + _ => { + unimplemented!("Unimplemented addressing mode {:?}", code.addr_mode); + } + }; + let mut branch_taken = false; // Don't update PC if we take a branch + println!(" Argument: {:#04X?}", arg); + match code.name { + OpcodeType::BRK => { + println!("Stepped on break. Ending"); + println!("{:#?}", self.cpu); + return Err(error::EmulatorError::Break); + } + OpcodeType::NOP => {} + OpcodeType::LDA => match code.addr_mode { + AddressingMode::IMM => self.cpu.set_a(operation!(unwrap arg code) as u8), + AddressingMode::ABS => self.cpu.set_a(fetch!(self operation!(unwrap arg code))), + AddressingMode::ZPG => self.cpu.set_a(fetch!(self operation!(unwrap arg code))), + _ => panic!("Invalid addressing mode for {:?}", code.name), + }, + OpcodeType::STA => match code.addr_mode { + AddressingMode::ABS => self.ram[operation!(unwrap arg code) as usize] = self.cpu.A, + AddressingMode::ZPG => self.ram[operation!(unwrap arg code) as usize] = self.cpu.A, + AddressingMode::INDX => self.ram[operation!(unwrap arg code) as usize] = self.cpu.A, + _ => panic!("Invalid addressing mode for {:?}", code.name), + }, + OpcodeType::ADC => match code.addr_mode { + AddressingMode::IMM => self.cpu.add_a(operation!(unwrap arg code) as u8), + _ => panic!("Invalid addressing mode for {:?}", code.name), + }, + OpcodeType::JMP => match code.addr_mode { + AddressingMode::ABS => self.cpu.PC = operation!(unwrap arg code) as u16, + _ => panic!("Invalid addressing mode for {:?}", code.name), + }, + OpcodeType::BEQ if code.addr_mode == AddressingMode::REL => { + if self.cpu.test(Flags::Zero) { + self.cpu.PC = operation!(unwrap arg code); + branch_taken = true; + } + } + OpcodeType::BNE if code.addr_mode == AddressingMode::REL => { + if !self.cpu.test(Flags::Zero) { + self.cpu.PC = operation!(unwrap arg code); + branch_taken = true; + } + } + _ => { + unimplemented!( + "Unimplemented opcode {:?} with {:?}", + code.name, + code.addr_mode + ); + } + } + if branch_taken || code.name == OpcodeType::JMP { + println!("Don't increment PC"); + } else { + self.cpu.PC += get_size(code.addr_mode) as u16; + } + self.cycles += 1; + Ok(()) + } + pub fn restart(&mut self) { + self.cycles = 0; + self.ram = [0x00; 0x10000]; + self.cpu = Cpu::default(); + } +} + +mod test { + #[test] + fn test_flags() { + use super::{Cpu, Flags}; + let mut cpu: Cpu = Cpu::default(); + cpu.set_flag(Flags::Zero, true); + assert_eq!(cpu.test(Flags::Zero), true); + cpu.set_flag(Flags::Zero, false); + assert_eq!(cpu.test(Flags::Zero), false); + cpu.set_flag(Flags::Zero, true); + cpu.set_flag(Flags::Negative, true); + cpu.set_flag(Flags::Int, true); + assert_eq!(cpu.test(Flags::Zero), true); + assert_eq!(cpu.test(Flags::Negative), true); + assert_eq!(cpu.test(Flags::Int), true); + cpu.set_flag(Flags::Negative, false); + assert_eq!(cpu.test(Flags::Zero), true); + assert_eq!(cpu.test(Flags::Negative), false); + assert_eq!(cpu.test(Flags::Int), true); + } +} diff --git a/src/sixty_five/error.rs b/src/sixty_five/error.rs new file mode 100644 index 0000000..1ea0bb6 --- /dev/null +++ b/src/sixty_five/error.rs @@ -0,0 +1,25 @@ +use std::boxed::Box; +use std::error::Error; +use std::fmt::{Display, Error as FmtError, Formatter}; + +#[derive(Debug)] +pub enum EmulatorError { + UnknownOp(u8), + Break, + Suberror(Box), +} + +impl Display for EmulatorError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "Emulator Error: {}", + match self { + EmulatorError::Suberror(e) => e.description().to_string(), + EmulatorError::Break => "Emulator Terminated".to_string(), + EmulatorError::UnknownOp(code) => format!("Unknown OP with code {:02X}", code), + } + ) + } +} +impl Error for EmulatorError {} diff --git a/src/sixty_five/mod.rs b/src/sixty_five/mod.rs new file mode 100644 index 0000000..b0bfa51 --- /dev/null +++ b/src/sixty_five/mod.rs @@ -0,0 +1,9 @@ +pub mod error; + +mod addressing_modes; +use addressing_modes::AddressingMode; +mod opcodes; +use opcodes::OpcodeType; +mod cpu; +mod emulator; +pub use emulator::Emulator; diff --git a/src/sixty_five/opcodes.rs b/src/sixty_five/opcodes.rs new file mode 100644 index 0000000..5e3c160 --- /dev/null +++ b/src/sixty_five/opcodes.rs @@ -0,0 +1,883 @@ +use super::addressing_modes::AddressingMode; + +pub const OPCODES: [Option; 0x100] = [ + Some(OpcodeData { + name: OpcodeType::BRK, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::INDX, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::ASL, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::PHP, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::ASL, + addr_mode: AddressingMode::A, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::ASL, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BPL, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::ASL, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::CLC, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ORA, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::ASL, + addr_mode: AddressingMode::ABSX, + }), + None, + Some(OpcodeData { + name: OpcodeType::JSR, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::INDX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::BIT, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::ROL, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::PLP, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::ROL, + addr_mode: AddressingMode::A, + }), + None, + Some(OpcodeData { + name: OpcodeType::BIT, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::ROL, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BMI, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::ROL, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::SEC, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::AND, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::ROL, + addr_mode: AddressingMode::ABSX, + }), + None, + Some(OpcodeData { + name: OpcodeType::RTI, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::INDX, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::LSR, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::PHA, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::LSR, + addr_mode: AddressingMode::A, + }), + None, + Some(OpcodeData { + name: OpcodeType::JMP, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::LSR, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BVC, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::LSR, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::CLI, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::EOR, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::LSR, + addr_mode: AddressingMode::ABSX, + }), + None, + Some(OpcodeData { + name: OpcodeType::RTS, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::INDX, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::ROR, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::PLA, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::ROR, + addr_mode: AddressingMode::A, + }), + None, + Some(OpcodeData { + name: OpcodeType::JMP, + addr_mode: AddressingMode::IND, + }), + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::ROR, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BVS, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::ROR, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::SEI, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::ADC, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::ROR, + addr_mode: AddressingMode::ABSX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::INDX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::STY, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::STX, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::DEY, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::TXA, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::STY, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::STX, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BCC, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::INDY, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::STY, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::STX, + addr_mode: AddressingMode::ZPGY, + }), + None, + Some(OpcodeData { + name: OpcodeType::TYA, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::ABSY, + }), + Some(OpcodeData { + name: OpcodeType::TXS, + addr_mode: AddressingMode::IMPL, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::STA, + addr_mode: AddressingMode::ABSX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::LDY, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::INDX, + }), + Some(OpcodeData { + name: OpcodeType::LDX, + addr_mode: AddressingMode::IMM, + }), + None, + Some(OpcodeData { + name: OpcodeType::LDY, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::LDX, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::TAY, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::TAX, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::LDY, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::LDX, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BCS, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::INDY, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::LDY, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::LDX, + addr_mode: AddressingMode::ZPGY, + }), + None, + Some(OpcodeData { + name: OpcodeType::CLV, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::ABSY, + }), + Some(OpcodeData { + name: OpcodeType::TSX, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::LDY, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::LDA, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::LDX, + addr_mode: AddressingMode::ABSY, + }), + None, + Some(OpcodeData { + name: OpcodeType::CPY, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::INDX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::CPY, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::DEC, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::INY, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::DEX, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::CPY, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::DEC, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BNE, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::DEC, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::CLD, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::CMP, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::DEC, + addr_mode: AddressingMode::ABSX, + }), + None, + Some(OpcodeData { + name: OpcodeType::CPX, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::INDX, + }), + None, + None, + Some(OpcodeData { + name: OpcodeType::CPX, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::ZPG, + }), + Some(OpcodeData { + name: OpcodeType::INC, + addr_mode: AddressingMode::ZPG, + }), + None, + Some(OpcodeData { + name: OpcodeType::INX, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::IMM, + }), + Some(OpcodeData { + name: OpcodeType::NOP, + addr_mode: AddressingMode::IMPL, + }), + None, + Some(OpcodeData { + name: OpcodeType::CPX, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::ABS, + }), + Some(OpcodeData { + name: OpcodeType::INC, + addr_mode: AddressingMode::ABS, + }), + None, + Some(OpcodeData { + name: OpcodeType::BEQ, + addr_mode: AddressingMode::REL, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::INDY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::ZPGX, + }), + Some(OpcodeData { + name: OpcodeType::INC, + addr_mode: AddressingMode::ZPGX, + }), + None, + Some(OpcodeData { + name: OpcodeType::SED, + addr_mode: AddressingMode::IMPL, + }), + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::ABSY, + }), + None, + None, + None, + Some(OpcodeData { + name: OpcodeType::SBC, + addr_mode: AddressingMode::ABSX, + }), + Some(OpcodeData { + name: OpcodeType::INC, + addr_mode: AddressingMode::ABSX, + }), + None, +]; +/* pub fn get_code(name: OpcodeType, addr_mode: AddressingMode) -> Result { + for (i, opcode) in OPCODES.iter().enumerate() { + match opcode { + None => continue, + Some(ref opcode) => { + if opcode.name == name && opcode.addr_mode == addr_mode { + return Ok((i & 0xFF) as u8); + } + } + } + } + Err(Error::UnkownOpcode { name: name.into() }) +} */ + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum OpcodeType { + ADC, + AND, + ASL, + BCC, // Branch ops + BCS, // Branch ops + BEQ, // Branch ops + BIT, + BMI, // Branch ops + BNE, // Branch ops + BPL, // Branch ops + BRK, + BVC, // Branch ops + BVS, // Branch ops + CLC, + CLD, + CLI, + CLV, + CMP, + CPX, + CPY, + DEC, + DEX, + DEY, + EOR, + INC, + INX, + INY, + JMP, + JSR, + LDA, + LDX, + LDY, + LSR, + NOP, + ORA, + PHA, + PHP, + PLA, + PLP, + ROL, + ROR, + RTI, + RTS, + SBC, + SEC, + SED, + SEI, + STA, + STX, + STY, + TAX, + TAY, + TSX, + TXA, + TXS, + TYA, +} +impl OpcodeType { + pub fn identify<'s, S: std::ops::Deref>( + string: &S, + ) -> Result { + match **string { + "ADC" => Ok(OpcodeType::ADC), + "AND" => Ok(OpcodeType::AND), + "ASL" => Ok(OpcodeType::ASL), + "BCC" => Ok(OpcodeType::BCC), + "BCS" => Ok(OpcodeType::BCS), + "BEQ" => Ok(OpcodeType::BEQ), + "BIT" => Ok(OpcodeType::BIT), + "BMI" => Ok(OpcodeType::BMI), + "BNE" => Ok(OpcodeType::BNE), + "BPL" => Ok(OpcodeType::BPL), + "BRK" => Ok(OpcodeType::BRK), + "BVC" => Ok(OpcodeType::BVC), + "BVS" => Ok(OpcodeType::BVS), + "CLC" => Ok(OpcodeType::CLC), + "CLD" => Ok(OpcodeType::CLD), + "CLI" => Ok(OpcodeType::CLI), + "CLV" => Ok(OpcodeType::CLV), + "CMP" => Ok(OpcodeType::CMP), + "CPX" => Ok(OpcodeType::CPX), + "CPY" => Ok(OpcodeType::CPY), + "DEC" => Ok(OpcodeType::DEC), + "DEX" => Ok(OpcodeType::DEX), + "DEY" => Ok(OpcodeType::DEY), + "EOR" => Ok(OpcodeType::EOR), + "INC" => Ok(OpcodeType::INC), + "INX" => Ok(OpcodeType::INX), + "INY" => Ok(OpcodeType::INY), + "JMP" => Ok(OpcodeType::JMP), + "JSR" => Ok(OpcodeType::JSR), + "LDA" => Ok(OpcodeType::LDA), + "LDX" => Ok(OpcodeType::LDX), + "LDY" => Ok(OpcodeType::LDY), + "LSR" => Ok(OpcodeType::LSR), + "NOP" => Ok(OpcodeType::NOP), + "ORA" => Ok(OpcodeType::ORA), + "PHA" => Ok(OpcodeType::PHA), + "PHP" => Ok(OpcodeType::PHP), + "PLA" => Ok(OpcodeType::PLA), + "PLP" => Ok(OpcodeType::PLP), + "ROL" => Ok(OpcodeType::ROL), + "ROR" => Ok(OpcodeType::ROR), + "RTI" => Ok(OpcodeType::RTI), + "RTS" => Ok(OpcodeType::RTS), + "SBC" => Ok(OpcodeType::SBC), + "SEC" => Ok(OpcodeType::SEC), + "SED" => Ok(OpcodeType::SED), + "SEI" => Ok(OpcodeType::SEI), + "STA" => Ok(OpcodeType::STA), + "STX" => Ok(OpcodeType::STX), + "STY" => Ok(OpcodeType::STY), + "TAX" => Ok(OpcodeType::TAX), + "TAY" => Ok(OpcodeType::TAY), + "TSX" => Ok(OpcodeType::TSX), + "TXA" => Ok(OpcodeType::TXA), + "TXS" => Ok(OpcodeType::TXS), + "TYA" => Ok(OpcodeType::TYA), + _ => Err(()), + } + } + pub fn is_branch_op(self) -> bool { + use OpcodeType::*; + let branch_ops = [BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS]; + branch_ops.contains(&self) + } +} +impl std::convert::Into for OpcodeType { + fn into(self) -> String { + format!("{:?}", self) + } +} +#[derive(Debug, Copy, Clone)] +pub struct OpcodeData { + pub name: OpcodeType, + pub addr_mode: AddressingMode, +} + +pub fn from_code(code: u8) -> Option { + OPCODES[code as usize] +} + +mod test { + #[test] + fn test_opcode_name() { + use super::OpcodeType; + let strings = vec![("LDA", true), ("STA", true), ("JMP", true), ("xd", false)]; + for (string, is_ok) in strings.iter() { + let res = OpcodeType::identify(&string); + println!("{} -> {:?}", string, res); + assert_eq!(res.is_ok(), *is_ok); + } + } +} diff --git a/src/sixty_five/ops b/src/sixty_five/ops new file mode 100644 index 0000000..0f61576 --- /dev/null +++ b/src/sixty_five/ops @@ -0,0 +1,56 @@ +ADC 011A AA01 +AND 001A AA01 +ASL 000A AA10 +BCC 1001 0000 +BCS 1011 0000 +BEQ 1111 0000 +BIT 0010 A100 +BMI 0011 0000 +BNE 1101 0000 +BPL 0001 0000 +BRK 0000 0000 +BVC 0101 0000 +BVS 0111 0000 +CLC 0001 1000 +CLD 1101 1000 +CLI 0101 1000 +CLV 1011 1000 +CMP 110A AA01 +CPA 1110 AA00 +CPY 1100 AA00 +DEC 110A A110 +DEA 1100 1010 +DEY 1000 1000 +EOR 010A AA01 +INC 111A A110 +INA 1110 1000 +INY 1100 1000 +JMP 01A0 1100 +JSR 0010 0000 +LDA 10AA AA01 +LDA 101A AA10 +LDY 101A AA00 +LSR 010A AA10 +NOP 1110 1010 +ORA 000A AA01 +PHA 0100 1000 +PHP 0000 1000 +PLA 0110 1000 +PLP 0010 1000 +ROL 001A AA10 +ROR 011A AA10 +RTI 0100 0000 +RTS 0110 0000 +SBC 111A AA01 +SEC 0011 1000 +SED 1111 1000 +SEI 0111 0000 +STA 100A AA01 +STA 100A A110 +STY 100A A100 +TAA 1010 1010 +TAY 1010 1000 +TSA 1011 1010 +TAA 1000 1010 +TAS 1001 1010 +TYA 1001 1000 \ No newline at end of file