From 48bfaf500a2f0fc5530369e53c9ba5328880600b Mon Sep 17 00:00:00 2001 From: Joey Pollack Date: Thu, 18 Jan 2024 16:40:21 -0500 Subject: [PATCH] All instructions implemented NOT TESTED --- src/r6502/instructions.rs | 230 +++++++++++++++++++++++++++++++++++++- src/r6502/mod.rs | 107 +++++++++++++++++- 2 files changed, 328 insertions(+), 9 deletions(-) diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs index 1744d1f..6793b00 100644 --- a/src/r6502/instructions.rs +++ b/src/r6502/instructions.rs @@ -2,7 +2,8 @@ #![allow(dead_code, non_snake_case)] -use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}}; +use super::{R6502, Bus, Flags, addressing_modes::ModeID, stack_push, stack_pop}; +//use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}}; // Instruction decoding: // https://llx.com/Neil/a2/opcodes.html @@ -32,7 +33,7 @@ impl Instructions Instructions::SBC, ]; - pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ Instructions::ASL, // 000 Instructions::ROL, // 001 Instructions::LSR, // 010 @@ -43,7 +44,7 @@ impl Instructions Instructions::INC, // 111 ]; - pub const GROUP_THREE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + pub const GROUP_THREE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ Instructions::ERR, Instructions::BIT, // 001 BIT Instructions::JMP, // 010 JMP @@ -54,7 +55,7 @@ impl Instructions Instructions::CPX, // 111 CPX ]; - pub const GROUP_BRANCHING_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + pub const GROUP_BRANCHING_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ Instructions::BPL, Instructions::BMI, Instructions::BVC, @@ -65,6 +66,40 @@ impl Instructions Instructions::BEQ, ]; + pub const GROUP_IRP_OPS: [fn(&mut R6502, &mut dyn Bus); 4] = [ + Instructions::BRK, + Instructions::JSR, + Instructions::RTI, + Instructions::RTS, + ]; + + + pub const GROUP_SB1_OPS: [fn(&mut R6502, &mut dyn Bus); 16] = [ + Instructions::PHP, + Instructions::CLC, + Instructions::PLP, + Instructions::SEC, + Instructions::PHA, + Instructions::CLI, + Instructions::PLA, + Instructions::SEI, + Instructions::DEY, + Instructions::TYA, + Instructions::TAY, + Instructions::CLV, + Instructions::INY, + Instructions::CLD, + Instructions::INX, + Instructions::SED, + ]; + pub const GROUP_SB2_OPS: [fn(&mut R6502, &mut dyn Bus); 6] = [ + Instructions::TXA, + Instructions::TXS, + Instructions::TAX, + Instructions::TSX, + Instructions::DEX, + Instructions::NOP, + ]; } @@ -79,6 +114,8 @@ impl Instructions /////////////////////////////////////////////////////////// // GROUP ONE + /////////////////////////////////////////////////////////// + pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) { let data = cpu.working_data as u8; @@ -255,6 +292,8 @@ impl Instructions /////////////////////////////////////////////////////////// // GROUP TWO + /////////////////////////////////////////////////////////// + pub fn ASL(cpu: &mut R6502, bus: &mut dyn Bus) { cpu.clear_flag(Flags::C); @@ -450,6 +489,8 @@ impl Instructions /////////////////////////////////////////////////////////// // GROUP THREE + /////////////////////////////////////////////////////////// + pub fn BIT(cpu: &mut R6502, bus: &mut dyn Bus) { cpu.set_flag(Flags::Z); @@ -549,6 +590,8 @@ impl Instructions /////////////////////////////////////////////////////////// // BRANCHING + /////////////////////////////////////////////////////////// + fn BPL(cpu: &mut R6502, bus: &mut dyn Bus) { if cpu.check_flag(Flags::N) == 0 @@ -615,8 +658,187 @@ impl Instructions /////////////////////////////////////////////////////////// // INTERRUPT AND SUBROUTINE + /////////////////////////////////////////////////////////// + + pub fn BRK(cpu: &mut R6502, bus: &mut dyn Bus) + { + + let pc_hi = ((cpu.pc & 0xFF00) >> 8) as u8; + let pc_lo = (cpu.pc & 0x00FF) as u8; + stack_push(pc_hi, cpu, bus, ); + stack_push(pc_lo, cpu, bus, ); + + stack_push(cpu.status, cpu, bus); + + let addr_hi = bus.read(0xFFFE); + let addr_lo = bus.read(0xFFFF); + cpu.pc = ((addr_hi as u16) << 8) | (addr_lo as u16); + cpu.set_flag(Flags::B); + } + + pub fn JSR(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.pc -= 1; + let pc_hi = ((cpu.pc & 0xFF00) >> 8) as u8; + let pc_lo = (cpu.pc & 0x00FF) as u8; + + stack_push(pc_hi, cpu, bus); + stack_push(pc_lo, cpu, bus); + + // NOTE: Not 100% sure this is the address and not + // the address of the address (i.e. working_data). + cpu.pc = cpu.working_addr; + + } + + pub fn RTI(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.status = stack_pop(cpu, bus); + let pc_lo = stack_pop(cpu, bus) as u16; + let pc_hi = stack_pop(cpu, bus) as u16; + + cpu.pc = (pc_hi << 8) | pc_lo; + } + + pub fn RTS(cpu: &mut R6502, bus: &mut dyn Bus) + { + let pc_lo = stack_pop(cpu, bus) as u16; + let pc_hi = stack_pop(cpu, bus) as u16; + + cpu.pc = (pc_hi << 8) | pc_lo; + cpu.pc += 1; + } + + /////////////////////////////////////////////////////////// // SINGLE BYTE + pub fn PHP(cpu: &mut R6502, bus: &mut dyn Bus) + { + stack_push(cpu.status, cpu, bus); + } + + pub fn CLC(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.clear_flag(Flags::C); + } + + pub fn PLP(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.status = stack_pop(cpu, bus); + } + + pub fn SEC(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.set_flag(Flags::C); + } + + pub fn PHA(cpu: &mut R6502, bus: &mut dyn Bus) + { + stack_push(cpu.a, cpu, bus); + } + + pub fn CLI(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.clear_flag(Flags::I); + } + + pub fn PLA(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.a = stack_pop(cpu, bus); + + cpu.set_zn_flags(cpu.a); + } + + pub fn SEI(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.set_flag(Flags::I); + } + + pub fn DEY(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.y -= 1; + + cpu.set_zn_flags(cpu.y); + } + + pub fn TYA(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.a = cpu.y; + + cpu.set_zn_flags(cpu.a); + } + + pub fn TAY(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.y = cpu.a; + + cpu.set_zn_flags(cpu.y); + } + + pub fn CLV(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.clear_flag(Flags::V); + } + + pub fn INY(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.y += 1; + + cpu.set_zn_flags(cpu.y); + } + + pub fn CLD(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.clear_flag(Flags::D); + } + + pub fn INX(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.x += 1; + + cpu.set_zn_flags(cpu.x); + } + + pub fn SED(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.set_flag(Flags::D); + } + + pub fn TXA(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.a = cpu.x; + cpu.set_zn_flags(cpu.a); + } + + pub fn TXS(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.sp = (cpu.x as u16) + 0x100; + } + + pub fn TAX(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.x = cpu.a; + cpu.set_zn_flags(cpu.x); + } + + pub fn TSX(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.x = (cpu.sp - 0x100) as u8; + } + + pub fn DEX(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.x -= 1; + cpu.set_zn_flags(cpu.x); + } + + pub fn NOP(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + + } \ No newline at end of file diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index a81b38b..56db8ae 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -110,7 +110,9 @@ impl R6502 self.pc += 1; execute(opcode, self, bus); - //self.pc += 1; + + // Addressing modes increment pc instead + //self.pc += 1; //} self.cycles -= 1; @@ -139,15 +141,55 @@ impl R6502 pub fn irq(&mut self, bus: &mut impl Bus) { + if self.check_flag(Flags::I) != 0 + { + return; + } + + let pc_hi = ((self.pc & 0xFF00) >> 8) as u8; + let pc_lo = (self.pc & 0x00FF) as u8; + stack_push(pc_hi, self, bus, ); + stack_push(pc_lo, self, bus, ); + + stack_push(self.status, self, bus); + self.set_flag(Flags::I); + + let addr_lo = bus.read(0xFFFE) as u16; + let addr_hi = bus.read(0xFFFF) as u16; + self.pc = (addr_hi >> 8) | addr_lo; } pub fn nmi(&mut self, bus: &mut impl Bus) { - + let pc_hi = ((self.pc & 0xFF00) >> 8) as u8; + let pc_lo = (self.pc & 0x00FF) as u8; + stack_push(pc_hi, self, bus, ); + stack_push(pc_lo, self, bus, ); + + stack_push(self.status, self, bus); + + let addr_lo = bus.read(0xFFFA) as u16; + let addr_hi = bus.read(0xFFFB) as u16; + self.pc = (addr_hi >> 8) | addr_lo; } // helpers + pub fn set_zn_flags(&mut self, val: u8) + { + self.clear_flag(Flags::Z); + if val == 0 + { + self.set_flag(Flags::Z); + } + + self.clear_flag(Flags::N); + if val & 0x80 != 0 + { + self.set_flag(Flags::N); + } + } + pub fn set_flag(&mut self, bit: Flags) { self.status |= bit as u8; @@ -170,14 +212,14 @@ impl R6502 } -fn stack_push(value: u8, cpu: &mut R6502, bus: &mut dyn Bus) +pub(crate) fn stack_push(value: u8, cpu: &mut R6502, bus: &mut dyn Bus) { // TODO: Check for out of bounds errors bus.write(cpu.sp, value); cpu.sp -= 1; } -fn stack_pop(cpu: &mut R6502, bus: &mut dyn Bus) -> u8 +pub(crate) fn stack_pop(cpu: &mut R6502, bus: &mut dyn Bus) -> u8 { cpu.sp += 1; bus.read(cpu.sp) @@ -202,8 +244,26 @@ fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) } // Interrupt and Subroutine + const BRK: u8 = 0x00; + const JSR: u8 = 0x20; + const RTI: u8 = 0x40; + const RTS: u8 = 0x60; + + match instruction + { + BRK => {Instructions::BRK(cpu, bus); return; } + JSR => {Instructions::JSR(cpu, bus); return; } + RTI => {Instructions::RTI(cpu, bus); return; } + RTS => {Instructions::RTS(cpu, bus); return; } + + _ => () + } // Single byte instructions + if exe_single_byte(instruction, cpu, bus) + { + return; + } // Instructions with arguments const GROUP_ONE_OP: u8 = 0x01; @@ -217,8 +277,10 @@ fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) GROUP_TWO_OP => exe_group_two(instruction, cpu, bus), GROUP_THREE_OP => exe_group_three(instruction, cpu, bus), - _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code) + _ => panic!("UNKNOWN INSTRUCTION: {:#02X}", instruction) } + + } fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) @@ -302,4 +364,39 @@ fn exe_branch(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) let idx = ((instruction - 16) / 32) as usize; Instructions::GROUP_BRANCHING_OPS[idx](cpu, bus); +} + +// Returns true if the insruction was handled +fn exe_single_byte(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) -> bool +{ + // Group of 8's + // PHP CLC PLP SEC PHA CLI PLA SEI DEY TYA TAY CLV INY CLD INX SED + // 08 18 28 38 48 58 68 78 88 98 A8 B8 C8 D8 E8 F8 + // Index = (Value-8) / 16 + const EIGHT_MASK: u8 = 0x0F; + if instruction & EIGHT_MASK == 0x08 + { + let i = ((instruction - 0x08) / 0x10) as usize; + Instructions::GROUP_SB1_OPS[i](cpu, bus); + return true; + } + + // Group of A's + // TXA TXS TAX TSX DEX NOP + // 8A 9A AA BA CA EA + // Index = (Value-8A) / 16 + if instruction < 0x8A + { + return false; + } + + const A_MASK: u8 = 0x05; + if instruction & A_MASK == 0xA + { + let i = ((instruction - 0x8A) / 0x10) as usize; + Instructions::GROUP_SB2_OPS[i](cpu, bus); + return true; + } + + false } \ No newline at end of file