closer to ticking.

has microsteps in the UI
decodes from a NOP only binary
some basic instructions are starting to move data around
This commit is contained in:
Trevor Merritt 2025-06-30 08:36:55 -04:00
parent 8ed93fc90e
commit 9e0e8b5910
15 changed files with 269 additions and 34 deletions

View File

@ -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) {

View 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;
// write the rom to disk
fs::write("outputfile.bin", array.as_slice());
}

View File

@ -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();
}
} }

View File

@ -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) {

View File

@ -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
View File

@ -0,0 +1,7 @@
use core::mos6502cpu::Mos6502Cpu;
fn main() {
let x = Mos6502Cpu::default();
}

View 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;

View File

@ -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;

View File

@ -2,6 +2,7 @@ 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::{Absolute, AbsoluteX, AbsoluteY, Accumulator, Immediate, Implied, Indirect, IndirectX, IndirectY, ZeroPage, ZeroPageX};
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::{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};
@ -14,11 +15,14 @@ pub struct Instruction {
} }
impl Instruction { impl Instruction {
pub fn decode(bytes: &[u8]) -> Option<Instruction> { pub fn opinfo(bytes: &[u8]) -> Option<OpInfo> {
println!("DECODING : {bytes:?}"); println!("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).unwrap();
let operand = match info.length { let operand = match info.length {
2 => Operand::Byte(bytes.get(1).copied()?), 2 => Operand::Byte(bytes.get(1).copied()?),
3 => { 3 => {

View File

@ -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,35 +148,172 @@ 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 + 2]).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 -= 1;
}
Operation::DEY => {
self.y -= 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 {} into A", value);
self.a = value;
}
_ => {}
}
}
AddressMode::ZeroPage => {}
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 => {}
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(Mos6502Flag::Interrupt);
}
Operation::STA => {}
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}",
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);
} }
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) {

View File

@ -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

View File

@ -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;

View File

@ -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.

File diff suppressed because one or more lines are too long