start of rom_only PC
This commit is contained in:
@@ -0,0 +1,717 @@
|
||||
use crate::address_mode::AddressMode;
|
||||
use crate::constants::constants_isa_op::ISA_OP_NOP;
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::instruction::Instruction;
|
||||
use crate::instruction_table::INSTRUCTION_TABLE;
|
||||
use crate::mos6502flags::Mos6502Flag::*;
|
||||
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
||||
use crate::op_info::OpInfo;
|
||||
use crate::operand::Operand;
|
||||
use crate::operation::Operation;
|
||||
use log::trace;
|
||||
|
||||
pub struct Mos6502Cpu {
|
||||
pub(crate) memory: [u8; SIZE_64KB],
|
||||
/// accumulator
|
||||
pub(crate) a: u8,
|
||||
/// x register
|
||||
pub(crate) x: u8,
|
||||
/// y register
|
||||
pub(crate) y: u8,
|
||||
/// cpu flags
|
||||
pub(crate) flags: Mos6502Flags,
|
||||
/// program counter
|
||||
pub pc: u16,
|
||||
/// stack offset
|
||||
pub(crate) s: u8,
|
||||
pub microcode_step: u8,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) data_bus: u8,
|
||||
pub(crate) ir: Instruction, // Instruction Register
|
||||
pub(crate) oi: OpInfo,
|
||||
pub(crate) has_reset: bool,
|
||||
pub(crate) iv: u16, // Interrupt Vector
|
||||
pub(crate) cycle_carry: u16, // Value to hold between microsteps
|
||||
pub(crate) ir_bytes: [u8; 4],
|
||||
/// CPU Read signal
|
||||
pub read_signal: bool,
|
||||
pub(crate) reset_vector: u16,
|
||||
pub(crate) int_vector: u16
|
||||
}
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// set_data_bus
|
||||
///
|
||||
/// Sets data on the data bus.
|
||||
/// Used when CPU is in "R" mode
|
||||
pub fn set_data_bus(&mut self, to_set: u8) {
|
||||
self.data_bus = to_set;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mos6502Cpu {
|
||||
fn default() -> Self {
|
||||
let mut working = Mos6502Cpu {
|
||||
memory: [0x00; SIZE_64KB],
|
||||
a: 0x00,
|
||||
x: 0x00,
|
||||
y: 0x00,
|
||||
flags: Default::default(),
|
||||
pc: 0xfffd,
|
||||
s: 0x00,
|
||||
microcode_step: 0x00,
|
||||
address_bus: 0x00,
|
||||
data_bus: 0x00,
|
||||
ir: Instruction {
|
||||
op: Operation::NOP,
|
||||
mode: AddressMode::Implied,
|
||||
operand: Operand::None,
|
||||
},
|
||||
oi: INSTRUCTION_TABLE[ISA_OP_NOP as usize].unwrap(),
|
||||
has_reset: false,
|
||||
iv: 0xfffe,
|
||||
cycle_carry: 0x0000,
|
||||
ir_bytes: [0x00; 4],
|
||||
read_signal: true,
|
||||
reset_vector: 0x0000,
|
||||
int_vector: 0x0000
|
||||
};
|
||||
working.reset_cpu();
|
||||
working
|
||||
}
|
||||
}
|
||||
|
||||
impl Mos6502Cpu {
|
||||
pub fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
pub fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
//
|
||||
// fn read_word(&self, offset: &u16) -> u16 {
|
||||
// println!("READING OFFSET 0x{offset:04x} and 0x{:04x}", offset + 1);
|
||||
// let low = self.memory[*offset as usize];
|
||||
// let high = self.memory[*offset as usize + 1];
|
||||
// println!("LOW = 0x{low:02x} HIGH = 0x{high:02x}");
|
||||
// let result = (high as u16) << 8 | low as u16;
|
||||
// // println!("MEMORY: {:?}", self.memory);
|
||||
// println!("READ {result:04x}");
|
||||
// result
|
||||
// }
|
||||
|
||||
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||
self.flags.flag(flag_to_read)
|
||||
}
|
||||
pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) {
|
||||
if new_value {
|
||||
self.flags.set_flag(flag_to_set)
|
||||
} else {
|
||||
self.flags.clear_flag(flag_to_set)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self, offset: u16) -> u8 {
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
|
||||
pub fn poke(&mut self, offset: u16, value: u8) {
|
||||
println!("Setting memory at {offset:04x} to {value:02x}");
|
||||
self.memory[offset as usize] = value
|
||||
}
|
||||
|
||||
pub fn peek_a(&self) -> u8 {
|
||||
println!("Readding register A => 0x{:02x}", self.a);
|
||||
self.a
|
||||
}
|
||||
pub fn poke_a(&mut self, new_a: u8) {
|
||||
println!("Updating register A from [{}] to [{}]", self.a, new_a);
|
||||
self.a = new_a;
|
||||
}
|
||||
|
||||
pub fn peek_x(&self) -> u8 {
|
||||
println!("Readding register X => 0x{}", self.x);
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn poke_x(&mut self, new_x: u8) {
|
||||
println!("Updating register X from [{}] to [{}]", self.x, new_x);
|
||||
self.x = new_x
|
||||
}
|
||||
|
||||
pub fn peek_y(&self) -> u8 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn poke_y(&mut self, new_y: u8) {
|
||||
self.y = new_y
|
||||
}
|
||||
|
||||
fn advance_pc(&mut self, how_far: u16) {
|
||||
self.pc += how_far;
|
||||
}
|
||||
|
||||
fn set_pc_to(&mut self, new_pc: u16) {
|
||||
self.pc = new_pc;
|
||||
}
|
||||
|
||||
/// Ticks the CPU
|
||||
pub fn tick(&mut self) {
|
||||
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
|
||||
if self.microcode_step == 0 {
|
||||
println!("OUT OF MICROSTEPS. Decoding the next instruction");
|
||||
let offset = self.pc as usize;
|
||||
// TODO: this calls opinfo 2x
|
||||
self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap();
|
||||
self.ir = Instruction::decode(&self.memory[offset..offset + 4]).unwrap();
|
||||
self.microcode_step = self.oi.cycles;
|
||||
println!("Decoded [[{:?}]]", self.ir);
|
||||
self.advance_pc(self.oi.length as u16);
|
||||
// load the microstep buffer with what steps to run
|
||||
// set the counter to the number of steps left
|
||||
} else {
|
||||
// run 1 microcode step
|
||||
println!(
|
||||
"Microstep {}/{} for {:?}",
|
||||
self.microcode_step, self.oi.cycles, self.ir.op
|
||||
);
|
||||
match self.ir.op {
|
||||
Operation::ADC => match self.microcode_step {
|
||||
1 => match self.ir.mode {
|
||||
AddressMode::Immediate => {}
|
||||
AddressMode::ZeroPage => {}
|
||||
AddressMode::ZeroPageX => {}
|
||||
AddressMode::Absolute => {}
|
||||
AddressMode::AbsoluteX => {}
|
||||
AddressMode::AbsoluteY => {}
|
||||
AddressMode::Indirect => {}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {}
|
||||
},
|
||||
2 => {}
|
||||
_ => {}
|
||||
},
|
||||
Operation::AND => {}
|
||||
Operation::ASL => {}
|
||||
Operation::BCC => {}
|
||||
Operation::BCS => {}
|
||||
Operation::BEQ => {}
|
||||
Operation::BIT => {}
|
||||
Operation::BMI => {}
|
||||
Operation::BNE => {}
|
||||
Operation::BPL => {}
|
||||
Operation::BRK => {}
|
||||
Operation::BVC => {}
|
||||
Operation::BVS => {}
|
||||
Operation::CLC => {
|
||||
self.flags.clear_flag(Carry);
|
||||
}
|
||||
Operation::CLD => {
|
||||
self.flags.clear_flag(Decimal);
|
||||
}
|
||||
Operation::CLI => {
|
||||
self.flags.clear_flag(Interrupt);
|
||||
}
|
||||
Operation::CLV => {
|
||||
self.flags.clear_flag(Overflow);
|
||||
}
|
||||
Operation::CMP => {}
|
||||
Operation::CPX => {}
|
||||
Operation::CPY => {}
|
||||
Operation::DEC => {
|
||||
match self.microcode_step {
|
||||
// DEC Step 1
|
||||
1 => {
|
||||
let working_value = match self.oi.mode {
|
||||
AddressMode::ZeroPage => {
|
||||
// read from
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Byte(z) => z,
|
||||
_ => 0x00,
|
||||
};
|
||||
trace!("READING FROM MEMORY AT 0x{offset:04x}");
|
||||
self.memory[offset as usize]
|
||||
// self.peek(offset);
|
||||
}
|
||||
AddressMode::ZeroPageX => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Byte(z) => z,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset + self.x);
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
AddressMode::Absolute => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Word(offset) => offset,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset)
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Word(offset) => offset,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset + self.x);
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
_ => 0x00,
|
||||
};
|
||||
}
|
||||
// DEC write memory
|
||||
2 => {
|
||||
self.a = self.cycle_carry as u8;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Operation::DEX => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_x, new_carry) = self.x.overflowing_sub(1);
|
||||
self.poke_x(new_x);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
}
|
||||
}
|
||||
Operation::DEY => {
|
||||
if self.microcode_step == 1 {
|
||||
(self.y, _) = self.y.overflowing_sub(1);
|
||||
}
|
||||
}
|
||||
Operation::EOR => {}
|
||||
Operation::INC => {}
|
||||
Operation::INX => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_x, new_carry) = self.x.overflowing_add(1);
|
||||
self.poke_x(new_x);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
}
|
||||
Operation::INY => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_y, new_carry) = self.y.overflowing_add(1);
|
||||
self.poke_y(new_y);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
}
|
||||
Operation::JMP => match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.pc = offset;
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Operation::JSR => {
|
||||
// push pc to stack.
|
||||
// jump to the subroutine.
|
||||
}
|
||||
Operation::LDA => match self.oi.mode {
|
||||
AddressMode::Immediate => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
println!("Loading 0x{value:02x} ({value}) into A");
|
||||
self.a = value;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::ZeroPage => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
println!("Loading from zero page at 0x{value:02x} ({value})");
|
||||
self.a = self.memory[value as usize];
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::ZeroPageX => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
let x_offset = self.x;
|
||||
self.a = self.memory[(value + x_offset) as usize];
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::Absolute => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
println!("Loading from absolute address 0x{offset:04x}");
|
||||
self.a = self.memory[offset as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
self.a = self.memory[(offset + self.x as u16) as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteY => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
let real_offset = offset + self.y as u16;
|
||||
println!("offset: {offset:04x} + {:02x}", self.y);
|
||||
self.a = self.memory[(offset + self.y as u16) as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::Indirect => {}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {
|
||||
println!("INVALID ADDRESS MODE FOR LDA");
|
||||
}
|
||||
},
|
||||
Operation::LDX => {}
|
||||
Operation::LDY => {}
|
||||
Operation::LSR => {}
|
||||
Operation::NOP => {
|
||||
// do nothing.
|
||||
}
|
||||
Operation::ORA => {}
|
||||
Operation::PHA => {}
|
||||
Operation::PHP => {}
|
||||
Operation::PLA => {}
|
||||
Operation::PLP => {}
|
||||
Operation::ROL => {
|
||||
if self.microcode_step == 1 {
|
||||
self.a = self.a.rotate_left(1);
|
||||
}
|
||||
}
|
||||
Operation::ROR => {
|
||||
// rotate A
|
||||
if self.microcode_step == 1 {
|
||||
self.a = self.a.rotate_right(1);
|
||||
}
|
||||
}
|
||||
Operation::RTI => {}
|
||||
Operation::RTS => {}
|
||||
Operation::SBC => {}
|
||||
Operation::SEC => {
|
||||
self.flags.set_flag(Carry);
|
||||
}
|
||||
Operation::SED => {
|
||||
self.flags.set_flag(Decimal);
|
||||
}
|
||||
Operation::SEI => {
|
||||
self.flags.set_flag(Interrupt);
|
||||
}
|
||||
Operation::STA => {
|
||||
match self.oi.mode {
|
||||
AddressMode::ZeroPage => {
|
||||
// write to the zero page.
|
||||
match self.ir.operand {
|
||||
Operand::Byte(target) => {
|
||||
self.memory[target as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::ZeroPageX => {
|
||||
match self.ir.operand {
|
||||
Operand::Byte(target) => {
|
||||
let x = self.x;
|
||||
self.memory[(x + target) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::Absolute => {
|
||||
// write from A to the specified memory location
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[offset as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[(offset + self.x as u16) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteY => {
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[(offset + self.y as u16) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {
|
||||
// invalid memory mode
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::STX => {}
|
||||
Operation::STY => {}
|
||||
Operation::TAX => {
|
||||
self.x = self.a;
|
||||
}
|
||||
Operation::TAY => {
|
||||
self.y = self.a;
|
||||
}
|
||||
Operation::TSX => {}
|
||||
Operation::TXA => {
|
||||
self.a = self.x;
|
||||
}
|
||||
Operation::TXS => {}
|
||||
Operation::TYA => {
|
||||
self.y = self.a;
|
||||
}
|
||||
}
|
||||
self.microcode_step -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::constants::constants_isa_op::*;
|
||||
use crate::instruction_table::{INSTRUCTION_CYCLES, INSTRUCTION_TABLE};
|
||||
|
||||
#[test]
|
||||
fn clc() {
|
||||
// setup the CPU for our test
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Carry);
|
||||
// Load our 'test program'
|
||||
cpu.memory[0x6000] = ISA_OP_CLC;
|
||||
// Start the PC at our program
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
// Tick the CPU through the instruction
|
||||
for _ in 0..INSTRUCTION_CYCLES(ISA_OP_CLC) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Carry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cld() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Decimal);
|
||||
cpu.memory[0x6000] = ISA_OP_CLD;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..INSTRUCTION_CYCLES(ISA_OP_CLD) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Decimal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Interrupt);
|
||||
cpu.memory[0x6000] = ISA_OP_CLI;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLI) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Interrupt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clv() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Overflow);
|
||||
cpu.memory[0x6000] = ISA_OP_CLV;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLV) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Overflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_immediate() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_I;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_I) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_zx() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_x(1);
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ZX;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.memory[0x00ac] = 0xbe;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ZX) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
// println!("MEMORY AT 0x00aa, ab, ac, ad, ae -> {:02x} {:02x} {:02x} {:02x} {:02x}", cpu.memory[0x00aa], cpu.memory[0x00ab], cpu.memory[0x00ac], cpu.memory[0x00ad], cpu.memory[0x00ae]);
|
||||
// cpu.dump();
|
||||
assert_eq!(cpu.peek_a(), 0xbe);
|
||||
assert!(!cpu.peek_flag(Zero));
|
||||
assert!(!cpu.peek_flag(Carry));
|
||||
assert!(!cpu.peek_flag(Negative));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_zeropage() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_Z;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
// Load ZeroPage
|
||||
cpu.memory[0x00ab] = 0xbe;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..INSTRUCTION_CYCLES(ISA_OP_LDA_Z) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xbe);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolute() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABS;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.memory[0x0eef] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABS) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolutex() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABSX;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.poke_x(0x01);
|
||||
cpu.memory[0x0ef0] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABSX) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolutey() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABSY;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.poke_y(0x01);
|
||||
cpu.memory[0x0ef0] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABSY) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dex_inx() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.x = 0xab;
|
||||
cpu.memory[0x6000] = ISA_OP_DEX;
|
||||
cpu.memory[0x6001] = ISA_OP_INX;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEX) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xaa, cpu.x);
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INX) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xab, cpu.x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dey_iny() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_y(0xab);
|
||||
cpu.memory[0x6000] = ISA_OP_DEY;
|
||||
cpu.memory[0x6001] = ISA_OP_INY;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEY) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xaa, cpu.peek_y());
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INY) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xab, cpu.peek_y());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rol_a_ror_a() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_a(0b1010_1010); // 0xaa
|
||||
cpu.memory[0x6000] = ISA_OP_ROL_A;
|
||||
cpu.memory[0x6001] = ISA_OP_ROR_A;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROL_A) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(cpu.peek_a(), 0b0101_0101);
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROR_A) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(cpu.peek_a(), 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rol_zp_ror_zp() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x00ab] = 0b0101_0101;
|
||||
cpu.memory[0x6000] = ISA_OP_ROL_ZP;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROL_ZP) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.memory[0xab], 0b1010_1010);;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// dump_data
|
||||
///
|
||||
/// returns
|
||||
/// PC, A, X, Y, Address_Bus, Data_Bus, Microcode_Step
|
||||
pub fn dump_data(&self) -> (u16, u8, u8, u8, u16, u8, u8, u16, u16) {
|
||||
(
|
||||
self.pc,
|
||||
self.a,
|
||||
self.x,
|
||||
self.y,
|
||||
self.address_bus,
|
||||
self.data_bus,
|
||||
self.microcode_step,
|
||||
self.reset_vector,
|
||||
self.int_vector
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dump(&self) {
|
||||
println!(
|
||||
"CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x} / MICROSTEPS: {:02x} / S: {}",
|
||||
self.pc,
|
||||
self.a,
|
||||
self.x,
|
||||
self.y,
|
||||
self.address_bus,
|
||||
self.data_bus,
|
||||
self.microcode_step,
|
||||
self.flags.dump()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod cpu;
|
||||
pub mod new;
|
||||
|
||||
pub mod tick2;
|
||||
mod dbg;
|
||||
mod tick_stages;
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::constants::constants_system::{OFFSET_RESET_VECTOR, SIZE_64KB};
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
impl Mos6502Cpu {
|
||||
pub fn new() -> Mos6502Cpu {
|
||||
let array = [0x00u8; SIZE_64KB];
|
||||
let mut working = Mos6502Cpu {
|
||||
memory: array,
|
||||
ir_bytes: [0x00; 4],
|
||||
..Default::default()
|
||||
};
|
||||
working.reset_cpu();
|
||||
working
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn reset_cpu(&mut self) {
|
||||
self.microcode_step = 7 + 4;
|
||||
// self = &mut Mos6502Cpu::default();
|
||||
println!("Should tick 7 times, then 4 cycles to read the reset and int vectors.");
|
||||
// read the value at 0xfffc 0xfffd for our reset vector.
|
||||
// read the value at 0xfffe 0xffff for our int vector
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR};
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// AccurateTick
|
||||
///
|
||||
/// In: address_bus > Address of data operationm
|
||||
/// data_bus > Data read or written
|
||||
/// State:
|
||||
/// read_bus > Flag for if cpu is reading or writing the data bus
|
||||
/// cycle_step > Index for what step of the Decode->Load->Execute cycle we are in
|
||||
/// Out: address_bus > address for operation
|
||||
/// data_bus > data for the operation
|
||||
/// read_bus > lets rest of the computer know if the CPU is reading from the address
|
||||
/// provided or if we are writing to the address
|
||||
pub fn tick2(&mut self, address_bus: u16, data_bus: u8) -> (u16, u8, bool) {
|
||||
if self.has_reset {
|
||||
// we have completed the reset cycle
|
||||
if self.read_signal {
|
||||
// we should see new data in the data_bus for us
|
||||
let read_data = data_bus;
|
||||
} else {
|
||||
// we are writing to the bus.
|
||||
}
|
||||
} else {
|
||||
println!("Reset microstep {}", self.microcode_step);
|
||||
// we need to do the reset steps
|
||||
// reduce the number of remaining microsteps
|
||||
self.read_signal = true;
|
||||
match self.microcode_step {
|
||||
4 => {
|
||||
// read first byte of reset vector
|
||||
self.address_bus = OFFSET_RESET_VECTOR;
|
||||
}
|
||||
3 => {
|
||||
// at this point data holds the upper byte of our reset vector
|
||||
self.reset_vector = (data_bus as u16) << 8;
|
||||
// read secondd byte of reset vector
|
||||
self.address_bus = OFFSET_RESET_VECTOR + 1;
|
||||
}
|
||||
2 => {
|
||||
self.reset_vector |= data_bus as u16;
|
||||
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
|
||||
// read first byte of interrupt vector
|
||||
self.address_bus = OFFSET_INT_VECTOR;
|
||||
}
|
||||
1 => {
|
||||
// read second byte of interrupt vector
|
||||
self.address_bus = OFFSET_INT_VECTOR + 1;
|
||||
}
|
||||
0 => {
|
||||
self.int_vector |= data_bus as u16;
|
||||
println!("Loaded interrupt vector of 0x{:04x}", self.int_vector);
|
||||
self.pc = self.reset_vector;
|
||||
println!("Set PC to Reset Vector. Giddy-up!");
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
self.microcode_step -= 1;
|
||||
}
|
||||
(self.address_bus, self.data_bus, self.read_signal)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
/// Mos6502TickStates
|
||||
///
|
||||
/// The set of what a tick can be doing
|
||||
///
|
||||
enum Mos6502TickStates {
|
||||
/// Loading the first byte into the IR
|
||||
LoadingInstruction,
|
||||
/// Loading an 8 bit parameter
|
||||
Loading8BitParameter,
|
||||
/// Loading the MSB 8 bits
|
||||
Loading16BitParameter1,
|
||||
/// Loading the LSB 8 bits
|
||||
Loading16BitParameter2,
|
||||
/// Stalling for accurate emulation
|
||||
Stall(u8),
|
||||
/// Completed the instruction
|
||||
Complete
|
||||
}
|
||||
Reference in New Issue
Block a user