very basics working!
commit
293c707ee9
@ -0,0 +1,2 @@
|
||||
/target
|
||||
*.pdf
|
||||
@ -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}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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"
|
||||
@ -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]
|
||||
@ -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.");
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue