Compare commits
3 Commits
8ed93fc90e
...
f9c938757f
| Author | SHA1 | Date | |
|---|---|---|---|
| f9c938757f | |||
| d97774e97b | |||
| 9e0e8b5910 |
@ -10,8 +10,8 @@ use beneater::parts::display_matrix::DisplayMatrix;
|
|||||||
async fn main() {
|
async fn main() {
|
||||||
println!("Taxation is Theft");
|
println!("Taxation is Theft");
|
||||||
|
|
||||||
let computer = BenEaterPC::new();
|
let mut computer = BenEaterPC::new();
|
||||||
|
computer.load_rom("resources/beneater/roms/ror.bin");
|
||||||
|
|
||||||
let mut dm = DisplayMatrix::new(200.0, 50.0);
|
let mut dm = DisplayMatrix::new(200.0, 50.0);
|
||||||
let message_to_show = "Taxation is theft";
|
let message_to_show = "Taxation is theft";
|
||||||
@ -31,6 +31,7 @@ async fn main() {
|
|||||||
|
|
||||||
if frame_number.is_multiple_of(60) {
|
if frame_number.is_multiple_of(60) {
|
||||||
dm.push_letter('X');
|
dm.push_letter('X');
|
||||||
|
computer.tick_system();
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame_number.is_multiple_of(60 * 6) {
|
if frame_number.is_multiple_of(60 * 6) {
|
||||||
|
|||||||
16
beneater/src/bin/make_rom.rs
Normal file
16
beneater/src/bin/make_rom.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use std::fs;
|
||||||
|
use core::constants::constants_system::SIZE_32KB;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// make the rom data in memory.
|
||||||
|
// Fill with 0x6a -> ROR, A
|
||||||
|
let vec = vec![0x6a; SIZE_32KB];
|
||||||
|
let slice: Box<[u8]> = vec.into_boxed_slice();
|
||||||
|
let mut array: Box<[u8; SIZE_32KB]> = slice.try_into().expect("Unable to make rom in ram");
|
||||||
|
|
||||||
|
array[0] = 0xa9; // LDA #$ab
|
||||||
|
array[1] = 0xab; // 1010 1011
|
||||||
|
|
||||||
|
// write the rom to disk
|
||||||
|
fs::write("outputfile.bin", array.as_slice());
|
||||||
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufReader, Read};
|
||||||
|
use std::path::Path;
|
||||||
use crate::parts::clock::Clock;
|
use crate::parts::clock::Clock;
|
||||||
use core::mos6502cpu::Mos6502Cpu;
|
use core::mos6502cpu::Mos6502Cpu;
|
||||||
|
|
||||||
@ -28,4 +32,24 @@ impl BenEaterPC {
|
|||||||
self.cpu.tick();
|
self.cpu.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_rom(&mut self, rom_to_load: &str) {
|
||||||
|
println!("Preparing to load {rom_to_load}");
|
||||||
|
|
||||||
|
let file = File::open(rom_to_load).unwrap();
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut chunks = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut buffer = vec![0u8; 1];
|
||||||
|
let bytes_read = reader.read(&mut buffer).unwrap_or(0);
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer.truncate(bytes_read);
|
||||||
|
chunks.push(buffer[0]);
|
||||||
|
}
|
||||||
|
println!("Loaded {}b of data.", chunks.len());
|
||||||
|
self.cpu.memory = chunks.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ pub struct CpuDisplay {}
|
|||||||
impl CpuDisplay {
|
impl CpuDisplay {
|
||||||
pub fn render(cpu: &Mos6502Cpu, x_offset: f32, y_offset: f32) {
|
pub fn render(cpu: &Mos6502Cpu, x_offset: f32, y_offset: f32) {
|
||||||
// get the data to display...
|
// get the data to display...
|
||||||
let (pc, a, x, y, address_bus, data_bus) = cpu.dump_data();
|
let (pc, a, x, y, address_bus, data_bus, microsteps_remaining) = cpu.dump_data();
|
||||||
|
|
||||||
// ...build the interface
|
// ...build the interface
|
||||||
Self::draw_square(x_offset, y_offset, x_offset + 300.0, y_offset + 85.0, BLACK);
|
Self::draw_square(x_offset, y_offset, x_offset + 300.0, y_offset + 85.0, BLACK);
|
||||||
@ -16,6 +16,7 @@ impl CpuDisplay {
|
|||||||
draw_text(format!("A: 0x{:02x} X: 0x{:02x} Y: 0x{:02x}", a, x, y).as_str(), x_offset + 5.0, y_offset + 35.0, 15.0, BLACK);
|
draw_text(format!("A: 0x{:02x} X: 0x{:02x} Y: 0x{:02x}", a, x, y).as_str(), x_offset + 5.0, y_offset + 35.0, 15.0, BLACK);
|
||||||
draw_text(format!("Address: {:016b} | {:04x}", address_bus, address_bus).as_str(), x_offset + 5.0, y_offset + 55.0, 15.0, BLACK);
|
draw_text(format!("Address: {:016b} | {:04x}", address_bus, address_bus).as_str(), x_offset + 5.0, y_offset + 55.0, 15.0, BLACK);
|
||||||
draw_text(format!("Data: {:08b} | {:02x}", data_bus, data_bus).as_str(), x_offset + 5.0, y_offset + 75.0, 15.0, BLACK);
|
draw_text(format!("Data: {:08b} | {:02x}", data_bus, data_bus).as_str(), x_offset + 5.0, y_offset + 75.0, 15.0, BLACK);
|
||||||
|
draw_text(format!("MS: {:02x}", microsteps_remaining).as_str(), x_offset + 5.0, y_offset + 95.0, 15.0, BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
|
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
|
||||||
|
|||||||
@ -5,4 +5,4 @@ pub mod display_matrix;
|
|||||||
pub mod address_bus;
|
pub mod address_bus;
|
||||||
pub mod data_bus;
|
pub mod data_bus;
|
||||||
pub mod cpu_display;
|
pub mod cpu_display;
|
||||||
mod ram_display;
|
pub mod ram_display;
|
||||||
|
|||||||
7
cli/src/bin/ticker.rs
Normal file
7
cli/src/bin/ticker.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use core::mos6502cpu::Mos6502Cpu;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
let x = Mos6502Cpu::default();
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,16 +1,77 @@
|
|||||||
|
|
||||||
|
/// Represents the various addressing modes of the 6502 CPU.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub enum AddressMode {
|
pub enum AddressMode {
|
||||||
|
/// Implied
|
||||||
|
///
|
||||||
|
/// No operand is needed; the instruction implicitly operates on a register or flag.
|
||||||
|
/// Example: `CLC` (Clear Carry Flag)
|
||||||
Implied,
|
Implied,
|
||||||
|
|
||||||
|
/// Accumulator
|
||||||
|
///
|
||||||
|
/// Operates directly on the accumulator register.
|
||||||
|
/// Example: `ASL A` (Arithmetic Shift Left on Accumulator)
|
||||||
Accumulator,
|
Accumulator,
|
||||||
|
|
||||||
|
/// Immediate
|
||||||
|
///
|
||||||
|
/// Operand is a constant 8-bit value.
|
||||||
|
/// Example: `LDA #$01` loads the value 0x01 into the accumulator.
|
||||||
Immediate,
|
Immediate,
|
||||||
|
|
||||||
|
/// Zero Page
|
||||||
|
///
|
||||||
|
/// Operand is an address in the first 256 bytes of memory (0x0000–0x00FF).
|
||||||
|
/// Example: `LDA $10` reads from address 0x0010.
|
||||||
ZeroPage,
|
ZeroPage,
|
||||||
|
|
||||||
|
/// Zero Page X
|
||||||
|
///
|
||||||
|
/// Zero page address offset by the X register.
|
||||||
|
/// Example: If X = 0x10, `LDA $23,X` reads from 0x33.
|
||||||
ZeroPageX,
|
ZeroPageX,
|
||||||
|
|
||||||
|
/// Zero Page Y
|
||||||
|
///
|
||||||
|
/// Zero page address offset by the Y register.
|
||||||
|
/// Used only by a few instructions like `LDX` and `STX`.
|
||||||
|
/// Example: If Y = 0x10, `LDX $23,Y` reads from 0x33.
|
||||||
ZeroPageY,
|
ZeroPageY,
|
||||||
|
|
||||||
|
/// Absolute
|
||||||
|
///
|
||||||
|
/// Full 16-bit address is provided as the operand.
|
||||||
|
/// Example: `LDA $1234` reads from address 0x1234.
|
||||||
Absolute,
|
Absolute,
|
||||||
|
|
||||||
|
/// Absolute X
|
||||||
|
///
|
||||||
|
/// Absolute address offset by the X register.
|
||||||
|
/// Example: If X = 0x10, `LDA $1234,X` reads from 0x1244.
|
||||||
AbsoluteX,
|
AbsoluteX,
|
||||||
|
|
||||||
|
/// Absolute Y
|
||||||
|
///
|
||||||
|
/// Absolute address offset by the Y register.
|
||||||
|
/// Example: If Y = 0x10, `LDA $1234,Y` reads from 0x1244.
|
||||||
AbsoluteY,
|
AbsoluteY,
|
||||||
|
|
||||||
|
/// Indirect
|
||||||
|
///
|
||||||
|
/// Only used by `JMP`. Operand is a 16-bit address pointing to another 16-bit address.
|
||||||
|
/// Example: `JMP ($1234)` jumps to the address stored at 0x1234/0x1235.
|
||||||
Indirect,
|
Indirect,
|
||||||
|
|
||||||
|
/// Indirect X (Indexed Indirect)
|
||||||
|
///
|
||||||
|
/// Operand is a zero-page address. Add X to it, then fetch the 16-bit address from that location.
|
||||||
|
/// Example: If X = 0x04 and operand = $20, `LDA ($20,X)` reads from the address at $24/$25.
|
||||||
IndirectX,
|
IndirectX,
|
||||||
|
|
||||||
|
/// Indirect Y (Indirect Indexed)
|
||||||
|
///
|
||||||
|
/// Operand is a zero-page address. Fetch the 16-bit address from that location, then add Y.
|
||||||
|
/// Example: If Y = 0x10 and ($20) = $3000, `LDA ($20),Y` reads from $3010.
|
||||||
IndirectY,
|
IndirectY,
|
||||||
}
|
}
|
||||||
|
|||||||
7
core/src/constants/constants_system.rs
Normal file
7
core/src/constants/constants_system.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub const SIZE_1KB: usize = 1024 * 1024;
|
||||||
|
pub const SIZE_32KB: usize = SIZE_1KB * 32;
|
||||||
|
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
||||||
|
|
||||||
|
|
||||||
|
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
|
||||||
|
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
|
||||||
@ -1,2 +1,3 @@
|
|||||||
pub mod constants_isa_stub;
|
pub mod constants_isa_stub;
|
||||||
pub mod constants_isa_op;
|
pub mod constants_isa_op;
|
||||||
|
pub mod constants_system;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
|
use log::trace;
|
||||||
use crate::address_mode::AddressMode;
|
use crate::address_mode::AddressMode;
|
||||||
use crate::address_mode::AddressMode::*;
|
use crate::address_mode::AddressMode::*;
|
||||||
use crate::address_mode::AddressMode::{Absolute, AbsoluteX, AbsoluteY, Accumulator, Immediate, Implied, Indirect, IndirectX, IndirectY, ZeroPage, ZeroPageX};
|
use crate::address_mode::AddressMode::*;
|
||||||
use crate::instruction_table::INSTRUCTION_TABLE;
|
use crate::instruction_table::INSTRUCTION_TABLE;
|
||||||
|
use crate::op_info::OpInfo;
|
||||||
use crate::operand::Operand;
|
use crate::operand::Operand;
|
||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
use crate::operation::Operation::{ADC, AND, ASL, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK, BVC, BVS, CLC, CLD, CLI, CLV, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, JMP, JSR, LDA, LDX, LDY, NOP, ORA, PHA, PHP, PLA, PLP, ROL, ROR, RTI, RTS, SBC, SEC, SED, SEI, STA, STX, STY, TAX, TAY, TSX, TXA, TXS, TYA};
|
use crate::operation::Operation::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Instruction {
|
pub struct Instruction {
|
||||||
@ -14,30 +16,29 @@ pub struct Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
pub fn decode(bytes: &[u8]) -> Option<Instruction> {
|
pub fn opinfo(bytes: &[u8]) -> Option<OpInfo> {
|
||||||
println!("DECODING : {bytes:?}");
|
trace!("DECODING : {bytes:?}");
|
||||||
let opcode = bytes.get(0).copied()?;
|
let opcode = bytes.get(0).copied()?;
|
||||||
let info = INSTRUCTION_TABLE[opcode as usize]?;
|
Some(INSTRUCTION_TABLE[opcode as usize])?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(bytes: &[u8]) -> Option<Instruction> {
|
||||||
|
let info = Instruction::opinfo(bytes)?;
|
||||||
|
|
||||||
let operand = match info.length {
|
let operand = match info.length {
|
||||||
2 => Operand::Byte(bytes.get(1).copied()?),
|
2 => Operand::Byte(*bytes.get(1)?),
|
||||||
3 => {
|
3 => Operand::Word(u16::from_le_bytes([*bytes.get(1)?, *bytes.get(2)?])),
|
||||||
let lo = *bytes.get(1)?;
|
|
||||||
let hi = *bytes.get(2)?;
|
|
||||||
Operand::Word(u16::from_le_bytes([lo, hi]))
|
|
||||||
}
|
|
||||||
_ => Operand::None,
|
_ => Operand::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let return_value = Some(Instruction {
|
let instruction = Instruction {
|
||||||
op: info.operation,
|
op: info.operation,
|
||||||
mode: info.mode,
|
mode: info.mode,
|
||||||
operand,
|
operand,
|
||||||
});
|
};
|
||||||
|
|
||||||
println!("RETURNING: {:?}", return_value);
|
trace!("RETURNING: {:?}", instruction);
|
||||||
|
Some(instruction)
|
||||||
return_value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ impl Instruction {
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::address_mode::AddressMode::*;
|
use crate::address_mode::AddressMode::*;
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
use crate::operation::Operation::{ADC, INY, LSR};
|
use crate::operation::Operation::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
use crate::address_mode::AddressMode;
|
use crate::address_mode::AddressMode;
|
||||||
|
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR, SIZE_64KB};
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
||||||
|
use crate::mos6502flags::Mos6502Flag::{Carry, Decimal, Interrupt, Overflow};
|
||||||
|
use crate::op_info::OpInfo;
|
||||||
use crate::operand::Operand;
|
use crate::operand::Operand;
|
||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
use crate::operation::Operation::NOP;
|
|
||||||
|
|
||||||
pub const SIZE_1KB: usize = 1024 * 1024;
|
|
||||||
pub const SIZE_32KB: usize = SIZE_1KB * 32;
|
|
||||||
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
|
||||||
|
|
||||||
pub struct Mos6502Cpu {
|
pub struct Mos6502Cpu {
|
||||||
// this is public for rendering quickly.
|
// this is public for rendering quickly.
|
||||||
@ -21,7 +19,10 @@ pub struct Mos6502Cpu {
|
|||||||
pub microcode_step: u8,
|
pub microcode_step: u8,
|
||||||
pub address_bus: u16,
|
pub address_bus: u16,
|
||||||
pub data_bus: u8,
|
pub data_bus: u8,
|
||||||
ir: Instruction // Instruction Register
|
ir: Instruction, // Instruction Register
|
||||||
|
oi: OpInfo,
|
||||||
|
has_reset: bool,
|
||||||
|
iv: u16 // Interrupt Vector
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Mos6502Cpu {
|
impl Default for Mos6502Cpu {
|
||||||
@ -30,7 +31,7 @@ impl Default for Mos6502Cpu {
|
|||||||
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
|
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
|
||||||
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
|
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
|
||||||
|
|
||||||
Mos6502Cpu {
|
let mut working = Mos6502Cpu {
|
||||||
memory: boxed_array,
|
memory: boxed_array,
|
||||||
a: 0,
|
a: 0,
|
||||||
x: 0,
|
x: 0,
|
||||||
@ -45,8 +46,18 @@ impl Default for Mos6502Cpu {
|
|||||||
op: Operation::NOP,
|
op: Operation::NOP,
|
||||||
mode: AddressMode::Implied,
|
mode: AddressMode::Implied,
|
||||||
operand: Operand::None,
|
operand: Operand::None,
|
||||||
}
|
},
|
||||||
}
|
oi: OpInfo {
|
||||||
|
operation: Operation::NOP,
|
||||||
|
mode: AddressMode::Implied,
|
||||||
|
length: 1,
|
||||||
|
cycles: 2,
|
||||||
|
},
|
||||||
|
has_reset: false,
|
||||||
|
iv: 0xfffe
|
||||||
|
};
|
||||||
|
working.reset_cpu();
|
||||||
|
working
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +66,7 @@ impl Mos6502Cpu {
|
|||||||
let vec = vec![0x00; SIZE_64KB];
|
let vec = vec![0x00; SIZE_64KB];
|
||||||
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
|
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
|
||||||
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
|
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
|
||||||
Mos6502Cpu {
|
let mut working = Mos6502Cpu {
|
||||||
memory: boxed_array,
|
memory: boxed_array,
|
||||||
a: 0,
|
a: 0,
|
||||||
x: 0,
|
x: 0,
|
||||||
@ -70,8 +81,33 @@ impl Mos6502Cpu {
|
|||||||
op: Operation::NOP,
|
op: Operation::NOP,
|
||||||
mode: AddressMode::Implied,
|
mode: AddressMode::Implied,
|
||||||
operand: Operand::None,
|
operand: Operand::None,
|
||||||
|
},
|
||||||
|
oi: OpInfo {
|
||||||
|
operation: Operation::NOP,
|
||||||
|
mode: AddressMode::Implied,
|
||||||
|
length: 1,
|
||||||
|
cycles: 2,
|
||||||
|
},
|
||||||
|
has_reset: false,
|
||||||
|
iv: 0xfffe
|
||||||
|
};
|
||||||
|
working.reset_cpu();
|
||||||
|
working
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_cpu(&mut self) {
|
||||||
|
// self = &mut Mos6502Cpu::default();
|
||||||
|
println!("Should tick 7 times.");
|
||||||
|
// read the value at 0xfffc 0xfffd for our reset vector.
|
||||||
|
// read the value at 0xfffe 0xffff for our int vector
|
||||||
|
self.pc = self.read_word(&OFFSET_RESET_VECTOR);
|
||||||
|
self.iv = self.read_word(&OFFSET_INT_VECTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_word(&self, offset: &u16) -> u16 {
|
||||||
|
let low = self.memory[*offset as usize];
|
||||||
|
let high = self.memory[*offset as usize + 1];
|
||||||
|
(high as u16) << 8 | low as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||||
@ -112,38 +148,336 @@ impl Mos6502Cpu {
|
|||||||
self.y = new_y
|
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
|
/// Ticks the CPU
|
||||||
/// Returns
|
/// Returns
|
||||||
/// AddressBus, DataBus, RW flag
|
/// AddressBus, DataBus, RW flag
|
||||||
pub fn tick(&mut self) -> (u16, u8, bool) {
|
pub fn tick(&mut self) -> (u16, u8, bool) {
|
||||||
|
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
|
||||||
println!("PREPARiNG TO TICK CPU AT {:04x}", self.pc);
|
if self.microcode_step == 0 {
|
||||||
|
println!("OUT OF MICROSTEPS. Decoding the next instruction");
|
||||||
let mut num_microsteps_left = 0;
|
let offset = self.pc as usize;
|
||||||
|
// TODO: this calls opinfo 2x
|
||||||
if num_microsteps_left == 0 {
|
self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap();
|
||||||
println!("OUT OF MICROSTEPS. Time To do something that isnt microstep.");
|
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
|
// load the microstep buffer with what steps to run
|
||||||
// set the counter to the number of steps left
|
// set the counter to the number of steps left
|
||||||
} else {
|
} else {
|
||||||
// run 1 microcode step
|
// run 1 microcode step
|
||||||
println!("Microstep {num_microsteps_left}");
|
println!("Microstep {}", self.microcode_step);
|
||||||
num_microsteps_left -= 1;
|
match self.ir.op {
|
||||||
|
Operation::ADC => {
|
||||||
|
}
|
||||||
|
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 => {}
|
||||||
|
Operation::DEX => {
|
||||||
|
(self.x, _) = self.x.overflowing_sub(1) ;
|
||||||
|
}
|
||||||
|
Operation::DEY => {
|
||||||
|
(self.y, _) = self.y.overflowing_sub(1);
|
||||||
|
}
|
||||||
|
Operation::EOR => { }
|
||||||
|
Operation::INC => { }
|
||||||
|
Operation::INX => {
|
||||||
|
self.x += 1;
|
||||||
|
}
|
||||||
|
Operation::INY => {
|
||||||
|
self.y += 1;
|
||||||
|
}
|
||||||
|
Operation::JMP => {
|
||||||
|
match self.ir.operand {
|
||||||
|
Operand::Word(offset) => {
|
||||||
|
self.pc = offset;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Operation::JSR => {}
|
||||||
|
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 => {}
|
||||||
|
AddressMode::ZeroPageY => {}
|
||||||
|
AddressMode::Absolute => {}
|
||||||
|
AddressMode::AbsoluteX => {}
|
||||||
|
AddressMode::AbsoluteY => {}
|
||||||
|
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 => {
|
||||||
|
self.a = self.a.rotate_left(1);
|
||||||
|
}
|
||||||
|
Operation::ROR => {
|
||||||
|
// rotate A
|
||||||
|
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 => {}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
(0,0,false)
|
(0,0,false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump(&self) {
|
pub fn dump(&self) {
|
||||||
println!("CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x}",
|
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.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step, self.flags.dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8) {
|
pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8, u8) {
|
||||||
(self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus)
|
(self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_microstep(&self, instruction: Instruction, step: u8) {
|
fn run_microstep(&self, instruction: Instruction, step: u8) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::constants::constants_isa_op::*;
|
||||||
|
use crate::instruction_table::INSTRUCTION_TABLE;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[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_TABLE[ISA_OP_CLC as usize].unwrap().cycles { 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_TABLE[ISA_OP_CLD as usize].unwrap().cycles { 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_TABLE[ISA_OP_CLI as usize].unwrap().cycles { 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_TABLE[ISA_OP_CLV as usize].unwrap().cycles { 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_TABLE[ISA_OP_LDA_I as usize].unwrap().cycles { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(cpu.a, 0xab);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lda_zeropage() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.memory[0x6000] = ISA_OP_LDA_Z;
|
||||||
|
cpu.memory[0x6001] = 0xab;
|
||||||
|
cpu.memory[0x00ab] = 0xbe;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_Z as usize].unwrap().cycles + 1 { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(cpu.a, 0xbe);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dex() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.a = 0xab;
|
||||||
|
cpu.memory[0x6000] = ISA_OP_DEX;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..INSTRUCTION_TABLE[ISA_OP_DEX as usize].unwrap().cycles { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(0xaa, cpu.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,45 @@
|
|||||||
|
|
||||||
|
/// Represents the status flags in the 6502 processor's status register (P).
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum Mos6502Flag {
|
pub enum Mos6502Flag {
|
||||||
|
/// Carry Flag (C)
|
||||||
|
///
|
||||||
|
/// Set if an arithmetic operation results in a carry out of the most significant bit (for addition),
|
||||||
|
/// or a borrow (for subtraction). Also used for bit shifts and rotates.
|
||||||
Carry,
|
Carry,
|
||||||
|
|
||||||
|
/// Zero Flag (Z)
|
||||||
|
///
|
||||||
|
/// Set if the result of an operation is zero.
|
||||||
Zero,
|
Zero,
|
||||||
|
|
||||||
|
/// Interrupt Disable Flag (I)
|
||||||
|
///
|
||||||
|
/// When set, disables maskable interrupts (IRQ).
|
||||||
Interrupt,
|
Interrupt,
|
||||||
|
|
||||||
|
/// Decimal Mode Flag (D)
|
||||||
|
///
|
||||||
|
/// When set, arithmetic operations use Binary-Coded Decimal (BCD) mode.
|
||||||
|
/// Note: Not supported on all 6502 variants (e.g., not on the NES CPU).
|
||||||
Decimal,
|
Decimal,
|
||||||
|
|
||||||
|
/// Break Command Flag (B)
|
||||||
|
///
|
||||||
|
/// Set when a BRK (break) instruction is executed.
|
||||||
|
/// Used to distinguish software interrupts from hardware ones.
|
||||||
Break,
|
Break,
|
||||||
|
|
||||||
|
/// Overflow Flag (V)
|
||||||
|
///
|
||||||
|
/// Set when an arithmetic operation results in a signed overflow.
|
||||||
|
/// For example, adding two positive numbers results in a negative.
|
||||||
Overflow,
|
Overflow,
|
||||||
Negative
|
|
||||||
|
/// Negative Flag (N)
|
||||||
|
///
|
||||||
|
/// Set if the result of an operation has bit 7 set (i.e., the result is negative in two's complement).
|
||||||
|
Negative,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -20,6 +53,20 @@ pub struct Mos6502Flags {
|
|||||||
negative: bool,
|
negative: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mos6502Flags {
|
||||||
|
pub fn dump(&self) -> String {
|
||||||
|
format!("{}{}{}{}{}{}{}",
|
||||||
|
if self.carry { 'C' } else { 'c' },
|
||||||
|
if self.zero { 'Z' } else { 'z' },
|
||||||
|
if self.interrupt { 'I' } else { 'i' },
|
||||||
|
if self.decimal { 'D' } else { 'd' },
|
||||||
|
if self.break_flag { 'B' } else { 'b' },
|
||||||
|
if self.overflow { 'O' } else { 'o' },
|
||||||
|
if self.negative { 'N' } else { 'n' }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Mos6502Flags {
|
impl Mos6502Flags {
|
||||||
|
|
||||||
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
|
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
|
||||||
|
|||||||
@ -3,8 +3,12 @@ use crate::operation::Operation;
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct OpInfo {
|
pub struct OpInfo {
|
||||||
|
/// What is the operation
|
||||||
pub operation: Operation,
|
pub operation: Operation,
|
||||||
|
/// How does this operation access memory
|
||||||
pub mode: AddressMode,
|
pub mode: AddressMode,
|
||||||
|
/// Bytes to represent the instruction and parameters
|
||||||
pub length: u8,
|
pub length: u8,
|
||||||
|
/// CPU Cycles to complete the instruction
|
||||||
pub cycles: u8,
|
pub cycles: u8,
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
use crate::mos6502cpu::{SIZE_32KB};
|
use crate::constants::constants_system::SIZE_32KB;
|
||||||
use crate::periph::rom_chip::RomChip;
|
use crate::periph::rom_chip::RomChip;
|
||||||
|
|
||||||
/// At28C256
|
/// At28C256
|
||||||
@ -38,7 +38,7 @@ impl RomChip for At28C256 {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::mos6502cpu::SIZE_1KB;
|
use crate::constants::constants_system::SIZE_1KB;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// HM62256 Static Ram
|
// HM62256 Static Ram
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use crate::mos6502cpu::SIZE_32KB;
|
use crate::constants::constants_system::SIZE_32KB;
|
||||||
use crate::periph::ram_chip::RamChip;
|
use crate::periph::ram_chip::RamChip;
|
||||||
use crate::periph::rom_chip::RomChip;
|
use crate::periph::rom_chip::RomChip;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::mos6502cpu::SIZE_32KB;
|
use crate::constants::constants_system::SIZE_32KB;
|
||||||
|
|
||||||
pub trait RomChip {
|
pub trait RomChip {
|
||||||
/// Read
|
/// Read
|
||||||
|
|||||||
Binary file not shown.
1
resources/beneater/roms/ror.bin
Normal file
1
resources/beneater/roms/ror.bin
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user