rusted-nes/src/sim6502.rs

357 lines
13 KiB
Rust

#![allow(dead_code)]
use InstructionName::*;
use AddressingShort::*;
use Register::*;
use Flag::*;
pub enum Addressing {
Implicit,
Accumulator,
Immediate,
ZeroPage,
ZeroPageX,
ZeroPageY,
Absolute,
AbsoluteX,
AbsoluteY,
IndirectX,
IndirectY,
AbsoluteIndirect,
Relative,
}
#[derive(Debug, PartialEq)]
pub enum InstructionName {
ADC, AHX, ALR, AND, ANC, ARR, ASL, AXS, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK,
BVC, BVS, CLC, CLD, CLI, CLV, CMP, CPX, CPY, DCP, DEC, DEX, DEY, EOR, ISC, INC,
INX, INY, JMP, JSR, KIL, LAX, LAS, LDA, LDX, LDY, LSR, NOP, ORA, PHA, PHP, PLA,
PLP, RLA, ROL, ROR, RRA, RTI, RTS, SAX, SBC, SEC, SED, SEI, SHX, SHY, SLO, SRE,
STA, STX, STY, TAS, TAX, TAY, TSX, TXA, TXS, TYA, XAA,
}
#[derive(Debug)]
pub enum AddressingShort {
Imp, Acc, Imm, ZPg, ZPX,
ZPY, Abs, AbX, AbY, InX,
InY, AbI, Rel,
}
#[derive(Debug)]
pub struct InstructionDefinition {
name: InstructionName,
addressing: AddressingShort,
}
#[derive(Copy, Clone)]
pub enum Register {
A,
X,
Y,
S,
}
#[derive(Copy, Clone)]
pub enum Flag {
Carry,
Zero,
Interrupt,
Decimal,
Overflow,
Negative,
}
macro_rules! impl_indexing {
($array_type:ty, $enum_name:ident) => {
impl std::ops::Index<$enum_name> for $array_type {
type Output = <$array_type as std::ops::Index<usize>>::Output;
fn index(&self, flag: $enum_name) -> &Self::Output {
&self[flag as usize]
}
}
impl std::ops::IndexMut<$enum_name> for $array_type {
fn index_mut(&mut self, flag: $enum_name) -> &mut Self::Output {
&mut self[flag as usize]
}
}
};
}
impl_indexing!([bool; 6], Flag);
impl_indexing!([u8; 4], Register);
#[derive(Debug)]
pub enum Operand {
None,
}
pub struct Cpu {
interrupt_request: bool,
registers: [u8; 4],
memory: Vec<u8>,
stack: Vec<u8>,
status_flags: [bool; 6],
program_counter: i16,
}
#[derive(Debug)]
pub struct Instruction {
opcode: u8,
name: InstructionName,
src_opr: Operand,
dst_opr: Operand,
bytes_read: i32,
raw_data: Vec<u8>,
}
impl Cpu {
pub fn store(&mut self, _operand: &Operand, _value: u8) {}
pub fn load(&mut self, _operand: &Operand) -> u8 { 0 }
pub fn load_u16(&mut self, _operand: &Operand) -> u16 { 0 }
pub fn check_z_n_flags(&mut self, result: u8) -> u8 {
self.status_flags[Zero] = result == 0;
self.status_flags[Negative] = result & 0x80 > 0;
result
}
pub fn check_overflow_flag(&mut self, value: u8) {
self.status_flags[Overflow] = value & 0x40 > 0
}
pub fn get_flags_as_byte(&self) -> u8 {
let mut byte_: u8 = 0;
for (i,t) in self.status_flags.iter().enumerate() {
byte_ |= (0x1 << i as u8) & if *t { 0x1 } else { 0x0 }
}
return byte_
}
pub fn set_flags_from_byte(&mut self, byte_: u8) {
for (i,_) in (0..8).enumerate() {
let idx = i as u8;
self.status_flags[i] = (byte_ & (idx + 1)) > 0;
}
}
pub fn rotate_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) {
let val = self.load(&inst.src_opr);
let bit = val & head_bit;
let mut val = if head_bit > tail_bit { val << 1 } else { val >> 1 };
val |= if self.status_flags[Carry] { tail_bit } else { 0x0 };
let flags_result = self.check_z_n_flags(val);
self.store(&inst.dst_opr, flags_result);
self.status_flags[Carry] = bit > 0;
}
pub fn logic_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) {
let mut val = self.load(&inst.src_opr);
let bit = val & head_bit;
val = if head_bit > tail_bit { val << 1 } else { val >> 1 };
let flags_result = self.check_z_n_flags(val);
self.store(&inst.dst_opr, flags_result);
self.status_flags[Carry] = bit > 0x0;
}
pub fn branch(mut self, offset: i8) {
self.program_counter += 2 + offset as i16;
}
}
macro_rules! instruction_defs {
($({$name:ident, $addr:ident}),* $(,)?) => {
[
$(
InstructionDefinition {
name: $name,
addressing: $addr
},
)*
]
};
}
const INSTRUCTION_DEFS: [InstructionDefinition; 256] = instruction_defs![
// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
/*0x*/{BRK,Imp},{ORA,InX},{KIL,Imp},{SLO,InX},{NOP,ZPg},{ORA,ZPg},{ASL,ZPg},{SLO,ZPg},{PHP,Imp},{ORA,Imm},{ASL,Acc},{ANC,Imm},{NOP,Abs},{ORA,Abs},{ASL,Abs},{SLO,Abs},
/*1x*/{BPL,Rel},{ORA,InY},{KIL,Imp},{SLO,InY},{NOP,ZPX},{ORA,ZPX},{ASL,ZPX},{SLO,ZPX},{CLC,Imp},{ORA,AbY},{NOP,Imp},{SLO,AbY},{NOP,AbX},{ORA,AbX},{ASL,AbX},{SLO,AbX},
/*2x*/{JSR,Abs},{AND,InX},{KIL,Imp},{RLA,InX},{BIT,ZPg},{AND,ZPg},{ROL,ZPg},{RLA,ZPg},{PLP,Imp},{AND,Imm},{ROL,Acc},{ANC,Imm},{BIT,Abs},{AND,Abs},{ROL,Abs},{RLA,Abs},
/*3x*/{BMI,Rel},{AND,InY},{KIL,Imp},{RLA,InY},{NOP,ZPX},{AND,ZPX},{ROL,ZPX},{RLA,ZPX},{SEC,Imp},{AND,AbY},{NOP,Imp},{RLA,AbY},{NOP,AbX},{AND,AbX},{ROL,AbX},{RLA,AbX},
/*4x*/{RTI,Imp},{EOR,InX},{KIL,Imp},{SRE,InX},{NOP,ZPg},{EOR,ZPg},{LSR,ZPg},{SRE,ZPg},{PHA,Imp},{EOR,Imm},{LSR,Acc},{ALR,Imm},{JMP,Abs},{EOR,Abs},{LSR,Abs},{SRE,Abs},
/*5x*/{BVC,Rel},{EOR,InY},{KIL,Imp},{SRE,InY},{NOP,ZPX},{EOR,ZPX},{LSR,ZPX},{SRE,ZPX},{CLI,Imp},{EOR,AbY},{NOP,Imp},{SRE,AbY},{NOP,AbX},{EOR,AbX},{LSR,AbX},{SRE,AbX},
/*6x*/{RTS,Imp},{ADC,InX},{KIL,Imp},{RRA,InX},{NOP,ZPg},{ADC,ZPg},{ROR,ZPg},{RRA,ZPg},{PLA,Imp},{ADC,Imm},{ROR,Acc},{ARR,Imm},{JMP,AbI},{ADC,Abs},{ROR,Abs},{RRA,Abs},
/*7x*/{BVS,Rel},{ADC,InY},{KIL,Imp},{RRA,InY},{NOP,ZPX},{ADC,ZPX},{ROR,ZPX},{RRA,ZPX},{SEI,Imp},{ADC,AbY},{NOP,Imp},{RRA,AbY},{NOP,AbX},{ADC,AbX},{ROR,AbX},{RRA,AbX},
/*8x*/{NOP,Imm},{STA,InX},{NOP,Imm},{SAX,InX},{STY,ZPg},{STA,ZPg},{STX,ZPg},{SAX,ZPg},{DEY,Imp},{NOP,Imm},{TXA,Imp},{XAA,Imm},{STY,Abs},{STA,Abs},{STX,Abs},{SAX,Abs},
/*9x*/{BCC,Rel},{STA,InY},{KIL,Imp},{AHX,InY},{STY,ZPX},{STA,ZPX},{STX,ZPY},{SAX,ZPY},{TYA,Imp},{STA,AbY},{TXS,Imp},{TAS,AbY},{SHY,AbX},{STA,AbX},{SHX,AbY},{AHX,AbY},
/*Ax*/{LDY,Imm},{LDA,InX},{LDX,Imm},{LAX,InX},{LDY,ZPg},{LDA,ZPg},{LDX,ZPg},{LAX,ZPg},{TAY,Imp},{LDA,Imm},{TAX,Imp},{LAX,Imm},{LDY,Abs},{LDA,Abs},{LDX,Abs},{LAX,Abs},
/*Bx*/{BCS,Rel},{LDA,InY},{KIL,Imp},{LAX,InY},{LDY,ZPX},{LDA,ZPX},{LDX,ZPY},{LAX,ZPY},{CLV,Imp},{LDA,AbY},{TSX,Imp},{LAS,AbY},{LDY,AbX},{LDA,AbX},{LDX,AbY},{LAX,AbY},
/*Cx*/{CPY,Imm},{CMP,InX},{NOP,Imm},{DCP,InX},{CPY,ZPg},{CMP,ZPg},{DEC,ZPg},{DCP,ZPg},{INY,Imp},{CMP,Imm},{DEX,Imp},{AXS,Imm},{CPY,Abs},{CMP,Abs},{DEC,Abs},{DCP,Abs},
/*Dx*/{BNE,Rel},{CMP,InY},{KIL,Imp},{DCP,InY},{NOP,ZPX},{CMP,ZPX},{DEC,ZPX},{DCP,ZPX},{CLD,Imp},{CMP,AbY},{NOP,Imp},{DCP,AbY},{NOP,AbX},{CMP,AbX},{DEC,AbX},{DCP,AbX},
/*Ex*/{CPX,Imm},{SBC,InX},{NOP,Imm},{ISC,InX},{CPX,ZPg},{SBC,ZPg},{INC,ZPg},{ISC,ZPg},{INX,Imp},{SBC,Imm},{NOP,Imp},{SBC,Imm},{CPX,Abs},{SBC,Abs},{INC,Abs},{ISC,Abs},
/*Fx*/{BEQ,Rel},{SBC,InY},{KIL,Imp},{ISC,InY},{NOP,ZPX},{SBC,ZPX},{INC,ZPX},{ISC,ZPX},{SED,Imp},{SBC,AbY},{NOP,Imp},{ISC,AbY},{NOP,AbX},{SBC,AbX},{INC,AbX},{ISC,AbX},
];
fn execute(cpu: &mut Cpu, inst: &Instruction) {
match inst.name {
// Access
LDA | STA | LDX | STX | LDY | STY => {
let val = cpu.load(&inst.src_opr);
cpu.store(&inst.dst_opr, val);
},
// Transfer
TAX | TAY | TXA | TYA => {
let val = cpu.load(&inst.src_opr);
let flags_result = cpu.check_z_n_flags(val);
cpu.store(&inst.dst_opr, flags_result);
}
// Arithmetic
ADC => {
let memory = cpu.load(&inst.src_opr);
let result = cpu.registers[A] as u16 + memory as u16;
let carry_val = if cpu.status_flags[Carry] { 1 } else { 0 };
cpu.registers[A] = result as u8 + carry_val;
cpu.status_flags[Carry] = result > 0xFF;
let reg_a = cpu.registers[A];
let res = result as u8;
cpu.status_flags[Overflow] = (res ^ reg_a) & (res ^ memory) & 0x80 > 0;
cpu.check_z_n_flags(result as u8);
},
// Arithmetic
INC | INX | INY => {
let val = cpu.load(&inst.src_opr) + 1;
let flags_result = cpu.check_z_n_flags(val);
cpu.store(&inst.dst_opr, flags_result);
},
DEC | DEX | DEY => {
let val = cpu.load(&inst.src_opr) - 1;
let flags_result = cpu.check_z_n_flags(val);
cpu.store(&inst.dst_opr, flags_result);
},
// Compare
SBC | CMP | CPX | CPY => {
let reg = match inst.name {
SBC | CMP => A,
CPX => X,
_ => Y
};
let memory = cpu.load(&inst.src_opr);
let reg_val = cpu.registers[reg] as u16;
let result = reg_val - (memory as u16);
if inst.name == SBC {
let carry_val = if cpu.status_flags[Carry] { 1 } else { 0 };
cpu.registers[reg] = result as u8 - carry_val;
}
cpu.status_flags[Carry] = result < 0;
let reg_a = cpu.registers[A];
let res = result as u8;
cpu.status_flags[Overflow] = (res ^ reg_a) & (res ^ !memory) & 0x80 > 0;
cpu.check_z_n_flags(res);
},
// Shift
ASL => cpu.logic_shift(inst, 0x80, 0x01),
LSR => cpu.logic_shift(inst, 0x01, 0x08),
ROL => cpu.rotate_shift(inst, 0x80, 0x01),
ROR => cpu.rotate_shift(inst, 0x01, 0x08),
// Bitwise
AND => {
cpu.registers[A] &= cpu.load(&inst.src_opr);
cpu.check_z_n_flags(cpu.registers[A]);
},
BIT => {
let mem_val = cpu.load(&inst.src_opr);
let result = cpu.registers[A] & mem_val;
cpu.check_z_n_flags(result);
cpu.check_overflow_flag(result);
},
ORA => {
cpu.registers[A] |= cpu.load(&inst.src_opr);
cpu.check_z_n_flags(cpu.registers[A]);
},
EOR => {
cpu.registers[A] != cpu.load(&inst.src_opr);
cpu.check_z_n_flags(cpu.registers[A]);
},
// Branching
BCC => if !cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BCS => if cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BNE => if !cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BEQ => if cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BPL => if !cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BMI => if cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BVC => if !cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
BVS => if cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
// Jump
JMP => cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16,
JSR => {
// TODO: This is likely wrong, you can't pass in a u8 just like that
cpu.stack.push((cpu.program_counter + 2) as u8);
cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16;
},
RTS => {
let pc = cpu.stack.pop().unwrap();
cpu.program_counter = (pc + 1) as i16;
},
BRK => {
cpu.stack.push((cpu.program_counter + 2) as u8);
cpu.stack.push(cpu.get_flags_as_byte());
let pc_constant = 0xFFFE as u16;
cpu.program_counter = pc_constant as i16;
cpu.status_flags[Interrupt] = true;
},
RTI => {
let flags_byte = cpu.stack.pop().unwrap();
let pc = cpu.stack.pop().unwrap();
cpu.set_flags_from_byte(flags_byte);
// TODO: This is likely wrong, you can't pass in a u8 just like that
cpu.program_counter = pc as i16;
cpu.status_flags[Interrupt] = false;
}
// Stack
PHA | PHP | PLA | PLP | TSX | TXS => {},
// Something about PusH/PulL accumulator or PS
// Flags
SEC => cpu.status_flags[Carry] = true,
SED => cpu.status_flags[Decimal] = true,
SEI => cpu.status_flags[Interrupt] = true,
CLC => cpu.status_flags[Carry] = false,
CLD => cpu.status_flags[Decimal] = false,
CLI => cpu.status_flags[Interrupt] = false,
CLV => cpu.status_flags[Overflow] = false,
// We can figure this out later
NOP | KIL => {},
// Unofficial/Illegal
AHX | ALR | ANC | ARR | AXS | DCP | ISC | LAX | LAS
| RLA | RRA | SAX | SHX | SHY | SLO | SRE | TAS | XAA => {},
}
}
pub fn decode_instructions(data: &[u8], bytes_to_read: i32) -> Vec<&InstructionDefinition>{
let mut idx: usize = 0;
let mut instructions: Vec<&InstructionDefinition> =
Vec::with_capacity((bytes_to_read / 2) as usize);
while (idx as i32) < bytes_to_read {
let mut processed = 0;
let inst = &INSTRUCTION_DEFS[data[idx] as usize];
instructions.push(inst);
processed += match inst.addressing {
Imp | Acc => 1,
Imm | ZPg | Rel | AbI
| ZPX | InX | InY => 2,
ZPY | AbX | AbY | Abs => 3,
};
idx += processed
}
instructions
}