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