From cf14804df298aea75183ffe60e41fda64e74fde6 Mon Sep 17 00:00:00 2001 From: Trevor Merritt Date: Sat, 5 Jul 2025 11:49:04 -0400 Subject: [PATCH] more stuff --- beneater/src/bin/beneater.rs | 5 +- beneater/src/bin/urn.rs | 26 ++++++ beneater/src/parts/backplane.rs | 29 ++++-- beneater/src/parts/ben_eater_pc.rs | 9 +- beneater/src/parts/display_matrix.rs | 92 ++++++++++++++++--- beneater/src/parts/mos6522_peripheral.rs | 7 ++ beneater/src/parts/via6522.rs | 111 ++++++++++++++++++++--- core/src/constants/constants_system.rs | 8 +- core/src/mos6502cpu.rs | 18 +++- core/src/periph/at28c256/mod.rs | 7 +- 10 files changed, 268 insertions(+), 44 deletions(-) create mode 100644 beneater/src/bin/urn.rs diff --git a/beneater/src/bin/beneater.rs b/beneater/src/bin/beneater.rs index da15e06..fca8a86 100644 --- a/beneater/src/bin/beneater.rs +++ b/beneater/src/bin/beneater.rs @@ -11,12 +11,9 @@ async fn main() { let mut backplane = Backplane::new(); backplane.load_rom("resources/beneater/roms/ror.bin"); - let mut dm = DisplayMatrix::new(200.0, 50.0); - let message_to_show = "Taxation is theft"; - let mut message_index = 0; - dm.push_letter('T'); + // dm.push_letter('T'); let mut frame_number: u32 = 0x00; loop { diff --git a/beneater/src/bin/urn.rs b/beneater/src/bin/urn.rs new file mode 100644 index 0000000..d2ed7c8 --- /dev/null +++ b/beneater/src/bin/urn.rs @@ -0,0 +1,26 @@ +use std::fs; +use std::ops::Index; +use beneater::parts::backplane::Backplane; +use core::constants::constants_system::*; +use core::constants::constants_isa_op::*; +fn main() { + println!("Taxation is Theft"); + + let mut backplane = Backplane::new(); + + println!("Backplane is live."); + + let mut new_program = [0x00u8; SIZE_32KB]; + new_program[(OFFSET_RESET_VECTOR - SIZE_32KB as u16) as usize] = 0x00; + new_program[(OFFSET_RESET_VECTOR + 1 - SIZE_32KB as u16) as usize] = 0x60; + println!("Set offset in rom..."); + println!("VALUE AT OFFSET_RESET_VECTOR = 0x{:02x} ", new_program[(OFFSET_RESET_VECTOR - SIZE_32KB as u16) as usize]); + // println!("{:?}", new_program); + + backplane.rom.program(&new_program); + backplane.cpu.pc = 0x6000; + backplane.memory[0x6000] = ISA_OP_LDA_I; + backplane.memory[0x6001] = 0xab; + backplane.tick(); + +} diff --git a/beneater/src/parts/backplane.rs b/beneater/src/parts/backplane.rs index 01ca2d4..6dc0d82 100644 --- a/beneater/src/parts/backplane.rs +++ b/beneater/src/parts/backplane.rs @@ -3,11 +3,13 @@ use crate::parts::mos6522_peripheral::Mos6522Peripheral; use crate::parts::via6522::VIA6522; use core::constants::constants_system::*; use core::periph::at28c256::At28C256; +use core::periph::rom_chip::RomChip; pub struct Backplane { - cpu: Mos6502Cpu, - via: VIA6522, - memory: [u8; SIZE_64KB], - rom: At28C256 + // pub for dev + pub cpu: Mos6502Cpu, + pub via: VIA6522, + pub memory: Box<[u8; SIZE_64KB]>, + pub rom: At28C256 } impl Backplane { @@ -15,7 +17,7 @@ impl Backplane { Backplane { cpu: Mos6502Cpu::default(), via: VIA6522::default(), - memory: [0x00; SIZE_64KB], + memory: Box::new([0x00; SIZE_64KB]), rom: At28C256::default() } } @@ -28,6 +30,17 @@ impl Backplane { // is the CPU in read or write state let address = self.cpu.address_bus(); let data = self.cpu.data_bus(); + let rw = self.cpu.read_signal; + println!("- TICK START:"); + println!("| CPU: Address: 0b{address:016b} Data: 0b{data:08b} CPU RW: {rw}"); + // via state + println!("| VIA 0b{:08b}/0b{:08b}/0b{:08b}/0b{:08b}/", + self.via.read(VIA6522_ORA), + self.via.read(VIA6522_ORA), + self.via.read(VIA6522_DDRB), + self.via.read(VIA6522_DDRA), + ); + println!("| LCD"); match address { /// VIA 0x6000..0x6010 => { @@ -36,23 +49,25 @@ impl Backplane { } else { self.via.write((address - 0x6000) as u8, self.cpu.data_bus()); } + self.via.tick(); } /// RAM 0x0000..=0x3fff => { if self.cpu.read_signal { self.cpu.set_data_bus(self.memory[address as usize]); } else { - self.memory[*address as usize] = data; + self.memory[address as usize] = data; } } /// ROM 0x4000..=0x7fff => { - + self.rom.read(&(address - 0x4000)); } /// The ether. Scarrrrrrrryyyy...... _ => { println!("Lost READ:?{} to {:04x}", self.cpu.read_signal, address); } } + println!("- TICK DONE"); } } \ No newline at end of file diff --git a/beneater/src/parts/ben_eater_pc.rs b/beneater/src/parts/ben_eater_pc.rs index 5d7e1fc..eb27df6 100644 --- a/beneater/src/parts/ben_eater_pc.rs +++ b/beneater/src/parts/ben_eater_pc.rs @@ -4,6 +4,7 @@ use std::io::{BufReader, Read}; use std::path::Path; use crate::parts::clock::Clock; use core::mos6502cpu::Mos6502Cpu; +use core::constants::constants_system::*; pub struct BenEaterPC { clock: Clock, @@ -49,7 +50,11 @@ impl BenEaterPC { buffer.truncate(bytes_read); chunks.push(buffer[0]); } - println!("Loaded {}b of data.", chunks.len()); - self.cpu.memory = chunks.into(); + let data_size = chunks.len(); + print!("Loaded {}b of data. Poking into memory...", data_size); + for i in 0..SIZE_32KB { + self.cpu.poke(i as u16, chunks[i]); + } + println!("poke complete. Poked {}b of data.", data_size); } } diff --git a/beneater/src/parts/display_matrix.rs b/beneater/src/parts/display_matrix.rs index ef4e8dd..67e1f69 100644 --- a/beneater/src/parts/display_matrix.rs +++ b/beneater/src/parts/display_matrix.rs @@ -1,4 +1,5 @@ use std::time::{Duration, Instant}; +use crate::parts::mos6522_peripheral::Mos6522Peripheral; #[derive(Debug)] pub struct HD44780 { @@ -31,6 +32,36 @@ pub struct HD44780 { last_command_time: Instant, } +impl Mos6522Peripheral for HD44780 { + fn write(&mut self, port: u8) { + println!("Writing {port:08b}/{port:02x} to HD44780"); + self.set_data_bus(port); + } + + /// Control + /// + /// Takes a u8 with the bottom 3 bits representing + /// X X X X X rs rw en + fn control(&mut self, control: u8) { + self.write_control_lines( + control & 1 << 2 == 0, + control & 1 << 1 == 0, + control & 1 == 0 + ); + } + + fn read(&mut self) -> u8 { + self.data_bus + } + + fn tick(&mut self) { + println!("TICK OF HD44780"); + if self.busy && self.last_command_time.elapsed() > Duration::from_micros(50) { + self.busy = false; + } + } +} + impl HD44780 { pub fn new() -> Self { Self { @@ -86,12 +117,6 @@ impl HD44780 { } } - pub fn tick(&mut self) { - if self.busy && self.last_command_time.elapsed() > Duration::from_micros(50) { - self.busy = false; - } - } - fn evaluate(&mut self) { if self.rw { return; @@ -157,7 +182,6 @@ impl HD44780 { } } - #[cfg(test)] mod tests { use super::*; @@ -171,8 +195,7 @@ mod tests { fn test_clear_display() { let mut lcd = HD44780::new(); lcd.set_data_bus(0x01); - lcd.write_control_lines(false, false, true); - lcd.write_control_lines(false, false, false); + pulse_enable(&mut lcd); lcd.tick(); assert!(lcd.ddram.iter().all(|&b| b == b' ')); } @@ -181,8 +204,7 @@ mod tests { fn test_write_data() { let mut lcd = HD44780::new(); lcd.set_data_bus(0x41); // 'A' - lcd.write_control_lines(true, false, true); - lcd.write_control_lines(true, false, false); + pulse_enable(&mut lcd); lcd.tick(); assert_eq!(lcd.ddram[0], b'A'); } @@ -191,8 +213,7 @@ mod tests { fn test_set_cursor() { let mut lcd = HD44780::new(); lcd.set_data_bus(0x80 | 0x40); - lcd.write_control_lines(false, false, true); - lcd.write_control_lines(false, false, false); + pulse_enable(&mut lcd); lcd.tick(); assert_eq!(lcd.cursor_position, 0x40); } @@ -206,4 +227,47 @@ mod tests { let data = lcd.read_data_bus(); assert_eq!(data, 0x80 | 0x15); } -} \ No newline at end of file + + #[test] + fn hello_world_display() { + let mut lcd = HD44780::new(); + + let setup_instructions = [ + 0b0000_0000, // cls + 0b0011_1000, // display mode + 0b0000_1110, // display on + 0b0000_0110, // entry mode + ]; + + for instruction in setup_instructions { + lcd.write_control_lines(false, false, true); + lcd.set_data_bus(instruction); + pulse_enable(&mut lcd); + lcd.tick(); + } + + let letter_instructions = [ + 0b0100_1000, // H + 0b0100_0101, // E + 0b0100_1100, // L + 0b0100_1100, // L + 0b0100_1111, // O + 0b0100_0000, // _ + 0b0101_0111, // W + 0b0100_1111, // O + 0b0101_0010, // R + 0b0100_1100, // L + 0b0100_0100, // D + ]; + + // move cursor to start of next line. + lcd.write_control_lines(true, false, true); + lcd.set_data_bus(0b1100_0000); + pulse_enable(&mut lcd); + lcd.tick(); + + let (line1, line2) = lcd.get_display_lines(); + println!("LINE1 -> [{}]", line1); + println!("LINE2 -> [{}]", line2); + } +} diff --git a/beneater/src/parts/mos6522_peripheral.rs b/beneater/src/parts/mos6522_peripheral.rs index 14cdc37..724b112 100644 --- a/beneater/src/parts/mos6522_peripheral.rs +++ b/beneater/src/parts/mos6522_peripheral.rs @@ -1,5 +1,12 @@ pub trait Mos6522Peripheral { + /// Write to the data bus fn write(&mut self, port: u8); fn control(&mut self, control: u8); + /// Read the data bus fn read(&mut self) -> u8; + + /// Tick + /// + /// Run 1 clock cycle of the peripheral + fn tick(&mut self); } diff --git a/beneater/src/parts/via6522.rs b/beneater/src/parts/via6522.rs index 0ad5fd7..0ae0263 100644 --- a/beneater/src/parts/via6522.rs +++ b/beneater/src/parts/via6522.rs @@ -1,6 +1,7 @@ use crate::parts::display_matrix::HD44780; use crate::parts::mos6522_peripheral::Mos6522Peripheral; - +use core::constants::constants_system::*; +#[derive(Default)] pub struct VIA6522 { // Data registers ora: u8, @@ -52,16 +53,17 @@ impl VIA6522 { t2_enabled: false, ifr: 0, ier: 0, - lcd: Some(HD44780::new().into()), + lcd: Some(Box::new(HD44780::new())), } } + pub fn read(&self, addr: u8) -> u8 { - match addr & 0x0F { - 0x0 => self.orb, - 0x1 => self.ora, - 0x2 => self.ddrb, - 0x3 => self.ddra, + match (addr & 0x0F) { + VIA6522_ORB => self.orb, + VIA6522_ORA => self.ora, + VIA6522_DDRB => self.ddrb, + VIA6522_DDRA => self.ddra, 0x4 => (self.t1_counter & 0xFF) as u8, 0x5 => (self.t1_counter >> 8) as u8, 0x6 => (self.t1_latch & 0xFF) as u8, @@ -75,11 +77,12 @@ impl VIA6522 { } pub fn write(&mut self, addr: u8, value: u8) { + println!("VIA6522 write value 0x{value:02x} to address 0x{addr:02x}"); match addr & 0x0F { - 0x0 => self.orb = value, - 0x1 => self.ora = value, - 0x2 => self.ddrb = value, - 0x3 => self.ddra = value, + VIA6522_ORB => self.orb = value, + VIA6522_ORA => self.ora = value, + VIA6522_DDRB => self.ddrb = value, + VIA6522_DDRA => self.ddra = value, 0x4 => { self.t1_latch = (self.t1_latch & 0xFF00) | value as u16; self.t1_counter = self.t1_latch; @@ -125,3 +128,89 @@ impl VIA6522 { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_initialization() { + let via = VIA6522::new(); + assert_eq!(via.ora, 0); + assert_eq!(via.orb, 0); + assert_eq!(via.ddra, 0); + assert_eq!(via.ddrb, 0); + assert_eq!(via.t1_counter, 0); + assert_eq!(via.t2_counter, 0); + assert!(via.lcd.is_some()); + } + + #[test] + fn test_read_write_registers() { + let mut via = VIA6522::new(); + + via.write(0x0, 0xAA); + assert_eq!(via.read(0x0), 0xAA); + + via.write(0x1, 0xBB); + assert_eq!(via.read(0x1), 0xBB); + + via.write(0x2, 0xCC); + assert_eq!(via.read(0x2), 0xCC); + + via.write(0x3, 0xDD); + assert_eq!(via.read(0x3), 0xDD); + } + + #[test] + fn test_timer1_write_and_tick() { + let mut via = VIA6522::new(); + + via.write(0x4, 0x34); // Low byte + via.write(0x5, 0x12); // High byte, also enables timer + assert_eq!(via.t1_latch, 0x1234); + assert_eq!(via.t1_counter, 0x1234); + assert!(via.t1_enabled); + + for _ in 0..0x1234 { + via.tick(); + } + + assert_eq!(via.t1_counter, 0); + assert_eq!(via.ifr & 0x40, 0x40); // T1 interrupt flag set + } + + #[test] + fn test_timer2_write_and_tick() { + let mut via = VIA6522::new(); + via.t2_enabled = true; + + via.write(0x8, 0x05); // Counter + for _ in 0..5 { + via.tick(); + } + + assert_eq!(via.t2_counter, 0); + assert_eq!(via.ifr & 0x20, 0x20); // T2 interrupt flag set + } + + #[test] + fn test_interrupt_enable_disable() { + let mut via = VIA6522::new(); + + via.write(0xE, 0x82); // Enable bit 0x02 + assert_eq!(via.ier & 0x02, 0x02); + + via.write(0xE, 0x02); // Disable bit 0x02 + assert_eq!(via.ier & 0x02, 0x00); + } + + #[test] + fn test_clear_interrupt_flags() { + let mut via = VIA6522::new(); + via.ifr = 0xFF; + + via.write(0xD, 0x40); // Clear T1 interrupt + assert_eq!(via.ifr & 0x40, 0x00); + } +} diff --git a/core/src/constants/constants_system.rs b/core/src/constants/constants_system.rs index e1cc22c..c1406b9 100644 --- a/core/src/constants/constants_system.rs +++ b/core/src/constants/constants_system.rs @@ -3,4 +3,10 @@ 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; \ No newline at end of file +pub const OFFSET_INT_VECTOR: u16 = 0xfffe; + + +pub const VIA6522_ORB: u8 = 0; +pub const VIA6522_ORA: u8 = 1; +pub const VIA6522_DDRB: u8 = 2; +pub const VIA6522_DDRA: u8 = 3; diff --git a/core/src/mos6502cpu.rs b/core/src/mos6502cpu.rs index bde7943..508318b 100644 --- a/core/src/mos6502cpu.rs +++ b/core/src/mos6502cpu.rs @@ -21,7 +21,7 @@ pub struct Mos6502Cpu { /// cpu flags flags: Mos6502Flags, /// program counter - pc: u16, + pub pc: u16, /// stack offset s: u8, pub microcode_step: u8, @@ -104,14 +104,22 @@ impl Mos6502Cpu { // 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); + println!("READ FROM {OFFSET_RESET_VECTOR} AND GOT {}", self.pc); self.iv = self.read_word(&OFFSET_INT_VECTOR); - println!("PC and IV are now set from ROM addresses"); + self.address_bus = self.pc; + self.read_signal = true; + println!("PC and IV are now set from ROM addresses / AB = {:016b}", self.address_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]; - (high as u16) << 8 | low as u16 + 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 { @@ -513,6 +521,10 @@ impl Mos6502Cpu { self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step, self.flags.dump()); } + /// 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) { (self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step) } diff --git a/core/src/periph/at28c256/mod.rs b/core/src/periph/at28c256/mod.rs index be1e3d4..4ef17dd 100644 --- a/core/src/periph/at28c256/mod.rs +++ b/core/src/periph/at28c256/mod.rs @@ -1,6 +1,7 @@ mod default; mod rom_chip; +use std::io::Read; use crate::constants::constants_system::SIZE_32KB; use crate::periph::rom_chip::RomChip; @@ -15,8 +16,10 @@ pub struct At28C256 { } impl At28C256 { - pub fn program(&mut self, new_program: &[u8; 32768]) { - self.data= new_program.into(); + pub fn program(&mut self, new_program: &[u8; SIZE_32KB]) { + // panic!("FAIL. Cant program the chip."); + // println!("PROGRAMMING {:?}", new_program); + self.data = Box::new(*new_program); } }