commit 293c707ee9362023db879a5d65a4b062a64c2e10 Author: Joey Pollack Date: Mon Nov 6 18:47:23 2023 -0500 very basics working! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e9092e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*.pdf \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..28ebbe7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 're6502'", + "cargo": { + "args": [ + "build", + "--bin=re6502", + "--package=re6502" + ], + "filter": { + "name": "re6502", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 're6502'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=re6502", + "--package=re6502" + ], + "filter": { + "name": "re6502", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ce04797 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "re6502" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..819ddca --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "re6502" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d40b37a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ + + + +mod r6502; +use r6502::{R6502, Flags, Bus}; + +// All-RAM bus for testing +struct RAMBus +{ + ram: [u8; 64 * 1024] +} + +impl RAMBus +{ + fn new() -> RAMBus + { + RAMBus { ram: [0; 64 * 1024] } + } +} + +impl Bus for RAMBus +{ + fn read(&self, addr: u16) -> u8 + { + self.ram[addr as usize] + } + + fn write(&mut self, addr: u16, value: u8) + { + self.ram[addr as usize] = value; + } +} + +fn main() +{ + let mut cpu = R6502::new(); + + println!("\nStatus flag testing..."); + cpu.set_flag(Flags::I); + println!("\nI Flag is: {}", cpu.check_flag(Flags::I)); + + cpu.clear_flag(Flags::I); + println!("\nI Flag is: {}", cpu.check_flag(Flags::I)); + + println!("\nRunning a very simple test program:\n\tLDA #9\n\tORA #2\n Result should be 11 in the A register"); + + ////////////////////////// + // Setup Bus with program + ////////////////////////// + + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // write the program to memory + + // LDA #9 + bus.write(addr, 0xA9); // LDA - Immediate mode + bus.write(addr + 1, 0x09); // Argument + + // ORA #2 + bus.write(addr + 2, 0x09); // ORA - Immediate mode + bus.write(addr + 3, 0x02); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + cpu.clock(&mut bus); + + println!("\nProgram result, A register: {}", cpu.a); + + println!("\nFinished."); +} diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs new file mode 100644 index 0000000..57635c2 --- /dev/null +++ b/src/r6502/addressing_modes.rs @@ -0,0 +1,103 @@ + + +use super::{R6502, Bus}; + + +// GROUP ONE ADDRESS MODES +// 000 (zero page,X) IZX +// 001 zero page ZP0 +// 010 #immediate IMM +// 011 absolute ABS +// 100 (zero page),Y IZY +// 101 zero page,X ZPX +// 110 absolute,Y ABY +// 111 absolute,X ABX + +// GROUP TWO ADDRESS MODES +// 000 #immediate IMM +// 001 zero page ZP0 +// 010 accumulator IMP +// 011 absolute ABS +// 101 zero page,X ZPX +// 111 absolute,X ABX + +pub struct AddressingModes; +impl AddressingModes +{ + pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + AddressingModes::IZX, + AddressingModes::ZP0, + AddressingModes::IMM, + AddressingModes::ABS, + AddressingModes::IZY, + AddressingModes::ZPX, + AddressingModes::ABY, + AddressingModes::ABX, + ]; + + +} +impl AddressingModes +{ + // This is also the accumulator mode + pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.working_data = bus.read(cpu.pc); + cpu.pc += 1; + } + + pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } +} \ No newline at end of file diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs new file mode 100644 index 0000000..e8bd287 --- /dev/null +++ b/src/r6502/instructions.rs @@ -0,0 +1,113 @@ + + +#![allow(dead_code, non_snake_case)] + +use super::{R6502, Bus, Flags}; + +// uint8_t ADC(); uint8_t AND(); uint8_t ASL(); uint8_t BCC(); +// uint8_t BCS(); uint8_t BEQ(); uint8_t BIT(); uint8_t BMI(); +// uint8_t BNE(); uint8_t BPL(); uint8_t BRK(); uint8_t BVC(); +// uint8_t BVS(); uint8_t CLC(); uint8_t CLD(); uint8_t CLI(); +// uint8_t CLV(); uint8_t CMP(); uint8_t CPX(); uint8_t CPY(); +// uint8_t DEC(); uint8_t DEX(); uint8_t DEY(); uint8_t EOR(); +// uint8_t INC(); uint8_t INX(); uint8_t INY(); uint8_t JMP(); +// uint8_t JSR(); uint8_t LDA(); uint8_t LDX(); uint8_t LDY(); +// uint8_t LSR(); uint8_t NOP(); uint8_t ORA(); uint8_t PHA(); +// uint8_t PHP(); uint8_t PLA(); uint8_t PLP(); uint8_t ROL(); +// uint8_t ROR(); uint8_t RTI(); uint8_t RTS(); uint8_t SBC(); +// uint8_t SEC(); uint8_t SED(); uint8_t SEI(); uint8_t STA(); +// uint8_t STX(); uint8_t STY(); uint8_t TAX(); uint8_t TAY(); +// uint8_t TSX(); uint8_t TXA(); uint8_t TXS(); uint8_t TYA(); + +// GROUP ONE +// 000 ORA +// 001 AND +// 010 EOR +// 011 ADC +// 100 STA +// 101 LDA +// 110 CMP +// 111 SBC + +pub struct Instructions; + +impl Instructions +{ + pub const GROUP_ONE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + Instructions::ORA, + Instructions::AND, + Instructions::EOR, + Instructions::ADC, + Instructions::STA, + Instructions::LDA, + Instructions::CMP, + Instructions::SBC, + ]; +} + +impl Instructions +{ + /////////////////////////////////////////////////////////// + // GROUP ONE + pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) + { + cpu.a = cpu.a | cpu.working_data; + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.a = cpu.working_data; + + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + /////////////////////////////////////////////////////////// + // GROUP TWO +} \ No newline at end of file diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs new file mode 100644 index 0000000..7ed51bf --- /dev/null +++ b/src/r6502/mod.rs @@ -0,0 +1,156 @@ + +#![allow(dead_code)] + +mod addressing_modes; +mod instructions; + +use addressing_modes::AddressingModes; +use instructions::Instructions; + +pub trait Bus +{ + fn read(&self, addr: u16) -> u8; + fn write(&mut self, addr: u16, value: u8); +} + +// impl Sized for Bus +// { + +// } + + +#[derive(Copy, Clone, Debug)] +pub enum Flags +{ + C = (1 << 0), // Carry Flag + Z = (1 << 1), // Zero Flag + I = (1 << 2), // Interrupt Disable + D = (1 << 3), // Decimal Mode Flag + B = (1 << 4), // Break Command + U = (1 << 5), // Unused + V = (1 << 6), // Overflow Flag + N = (1 << 7), // Negative Flag +} + +pub struct R6502 +{ + pub a: u8, // Accumulator + x: u8, // X Register + y: u8, // Y Register + + pc: u16, // Program Counter + sp: u8, // Stack Pointer + status: u8, // Status Flags + + cycles: u32, // Track cycles + + // Helper Vars + working_data: u8, // value fetched for the ALU + addr_abs: u16, + addr_rel: u16, +} + +impl R6502 +{ + // constructor + pub fn new() -> R6502 + { + R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, working_data: 0, addr_abs: 0, addr_rel: 0 } + } + + // signals + pub fn clock(&mut self, bus: &mut dyn Bus) + { + // TODO: Track instructions cycles + // if self.cycles == 0 + //{ + let opcode = bus.read(self.pc); + self.pc += 1; + + execute(opcode, self, bus); + //self.pc += 1; + //} + + self.cycles -= 1; + } + + pub fn reset(&mut self, bus: &mut dyn Bus) + { + self.a = 0; + self.x = 0; + self.y = 0; + self.sp = 0xFF; // stack actually starts at 0x01FF but we only need the low byte since the end of the stack is at 0x0100 + self.status = 0; + self.set_flag(Flags::U); + + let lo: u16 = bus.read(0xFFFC) as u16; + let hi: u16 = bus.read(0xFFFD) as u16; + + self.pc = (hi << 8) | lo; + + // internal helper variables + self.working_data = 0; + self.addr_abs = 0; + self.addr_rel = 0; + + self.cycles = 8; + } + + pub fn irq(&mut self, bus: &mut impl Bus) + { + + } + + pub fn nmi(&mut self, bus: &mut impl Bus) + { + + } + + // helpers + pub fn set_flag(&mut self, bit: Flags) + { + self.status |= bit as u8; + } + + pub fn clear_flag(&mut self, bit: Flags) + { + self.status &= !(bit as u8); + } + + pub fn check_flag(&mut self, bit: Flags) -> bool + { + self.status & (bit as u8) > 0 + } +} + + + +fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + + let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01 + match group_code + { + 0x01 => exe_group_one(instruction, cpu, bus), + 0x02 => exe_group_two(instruction, cpu, bus), + + // TODO: Conditionals and specially formatted instructions + + _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code) + } +} + +fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + let addr_mask = (instruction & 0x1C) >> 2; + let op_mask = (instruction & 0xE0) >> 5; + + AddressingModes::GROUP_ONE_ADDRS[addr_mask as usize](cpu, bus); + Instructions::GROUP_ONE_OPS[op_mask as usize](cpu, bus); +} + +fn exe_group_two(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + +} +