From f9198cd0b1ce6fc5afafc4ab651fe1f346a17205 Mon Sep 17 00:00:00 2001 From: august kline Date: Wed, 19 Jun 2024 17:26:09 -0400 Subject: [PATCH] Architecture changes --- src/cpu.rs | 191 ++++++++++++++++++++++++++++++-------------- src/instructions.rs | 179 ++++++++++++++++++++--------------------- src/keyboard.rs | 32 ++++---- src/main.rs | 30 ++++--- src/memory.rs | 57 +++++++++++-- src/tui/mod.rs | 91 +++++++++++++-------- src/video.rs | 66 ++++++++------- 7 files changed, 393 insertions(+), 253 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index c58164e..d813631 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,17 +1,11 @@ use crate::instructions::{get_instruction, Instruction}; -use crate::memory::Mem; +use crate::memory::{MemHandle, MemoryReader, MemoryWriter}; use crate::types::{Byte, Word}; -use std::cell::RefCell; -use std::path::PathBuf; -use std::rc::Rc; -use std::str::FromStr; -use std::sync::mpsc::Receiver; -use std::sync::Arc; -use std::sync::Mutex; +use std::sync::mpsc::{Receiver, Sender}; use std::thread::sleep; use std::time::Duration; -use anyhow::{bail, Result}; +use anyhow::Result; #[derive(Clone, Copy)] pub enum StatusFlag { @@ -25,6 +19,55 @@ pub enum StatusFlag { Carry = 0b0000_0001, } +pub struct CpuController(Sender); + +pub enum CpuControl { + Irq, + Nmi, + Stop, + Resume, + Data, +} + +impl CpuController { + pub fn new(sender: Sender) -> Self { + Self(sender) + } + pub fn irq(&self) { + self.0.send(CpuControl::Irq); + } + pub fn nmi(&self) { + self.0.send(CpuControl::Nmi); + } + pub fn stop(&self) { + self.0.send(CpuControl::Stop); + } + pub fn resume(&self) { + self.0.send(CpuControl::Resume); + } + pub fn data(&self) { + self.0.send(CpuControl::Data); + } +} + +pub struct CpuReceiver(Receiver); +impl CpuReceiver { + pub fn new(receiver: Receiver) -> Self { + Self(receiver) + } +} + +pub struct CpuState { + pub a: Byte, // Accumulator Register + pub x: Byte, // X Register + pub y: Byte, // Y Register + pub pc: Word, // Program Counter + pub s: Byte, // Stack Pointer + pub p: Byte, // Status Register + pub irq: bool, + pub nmi: bool, +} + pub struct Cpu { pub a: Byte, // Accumulator Register pub x: Byte, // X Register @@ -32,15 +75,29 @@ pub struct Cpu { pub pc: Word, // Program Counter pub s: Byte, // Stack Pointer pub p: Byte, // Status Register - pub irq: Receiver, + pub irq: bool, pub nmi: bool, - pub memory: Arc>, + pub memory: MemHandle, pub pending_cycles: usize, + receiver: CpuReceiver, + stopped: bool, cycle_count: usize, + state_tx: Sender, +} + +impl MemoryReader for Cpu { + fn read(&self, address: Word) -> Byte { + self.memory.read(address) + } +} +impl MemoryWriter for Cpu { + fn write(&self, address: Word, data: Byte) { + self.memory.write(address, data); + } } impl Cpu { - pub fn new(memory: Arc>, irq: Receiver) -> Self { + pub fn new(memory: MemHandle, receiver: CpuReceiver, state_tx: Sender) -> Self { Cpu { a: 0x00, x: 0x00, @@ -48,11 +105,14 @@ impl Cpu { pc: 0x0000, s: 0xFF, p: 0b0010_0100, - irq, + irq: false, nmi: false, + receiver, memory, + stopped: false, pending_cycles: 0, cycle_count: 0, + state_tx, } } pub fn reset(&mut self) -> Result<()> { @@ -61,18 +121,18 @@ impl Cpu { self.pending_cycles = 0; Ok(()) } - pub fn read(&self, address: Word) -> Result { - let memory = match self.memory.lock() { - Ok(read) => read, - Err(_) => { - bail!("Couldn't acquire lock on memory in cpu thread") - } - }; - Ok(memory.read(address)) - } + // pub fn read(&self, address: Word) -> Result { + // let memory = match self.memory.lock() { + // Ok(read) => read, + // Err(_) => { + // bail!("Couldn't acquire lock on memory in cpu thread") + // } + // }; + // Ok(memory.read(address)) + // } pub fn read_word(&self, address: Word) -> Result { - let low_byte = self.read(address)?; - let high_byte = self.read(address + 0x1)?; + let low_byte = self.read(address); + let high_byte = self.read(address + 0x1); Ok((high_byte as u16) << 8 | (low_byte as u16)) } @@ -83,20 +143,20 @@ impl Cpu { pub fn push_stack(&mut self, data: Byte) -> Result<()> { self.s = self.s.wrapping_sub(0x1); - self.write(self.stack_addr(), data)?; + self.write(self.stack_addr(), data); Ok(()) } pub fn push_stack_word(&mut self, address: Word) -> Result<()> { self.s = self.s.wrapping_sub(0x1); - self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first + self.write(self.stack_addr(), address.to_le_bytes()[1]); // Upper byte first self.s = self.s.wrapping_sub(0x1); - self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second + self.write(self.stack_addr(), address.to_le_bytes()[0]); // Lower byte second Ok(()) } pub fn pop_stack(&mut self) -> Result { - let byte = self.read(self.stack_addr())?; + let byte = self.read(self.stack_addr()); self.s = self.s.wrapping_add(0x1); Ok(byte) } @@ -123,17 +183,6 @@ impl Cpu { value & 0b1000_0000 == 0b1000_0000 } - pub fn write(&mut self, address: Word, data: Byte) -> Result<()> { - let mut memory = match self.memory.lock() { - Ok(write) => write, - Err(_) => { - bail!("Couldn't acquire write lock on memory in cpu thread") - } - }; - memory.write(address, data); - Ok(()) - } - pub fn execute(&mut self) { self.cycle(); while self.pending_cycles != 0 { @@ -146,28 +195,57 @@ impl Cpu { } pub fn interrupt(&mut self) { + self.irq = false; self.push_stack_word(self.pc).unwrap(); self.push_stack(self.p).unwrap(); self.set_flag(StatusFlag::IrqDisable, true); self.pc = self.read_word(0xFFFE).unwrap(); } + fn receive_control(&mut self) { + let control = self.receiver.0.recv().unwrap(); + match control { + CpuControl::Nmi => self.nmi = true, + CpuControl::Irq => self.irq = true, + CpuControl::Stop => self.stopped = true, + CpuControl::Resume => self.stopped = false, + CpuControl::Data => self + .state_tx + .send(CpuState { + a: self.a.clone(), // Accumulator Register + x: self.x.clone(), // X Register + y: self.y.clone(), // Y Register + pc: self.pc.clone(), // Program Counter + s: self.s.clone(), // Stack Pointer + p: self.p.clone(), // Status Register + irq: self.irq.clone(), + nmi: self.nmi.clone(), + }) + .unwrap(), + } + } + pub fn cycle(&mut self) { + self.receive_control(); + if self.stopped { + return; + } while self.pending_cycles != 0 { // roughly cycle-accurate timing sleep(Duration::from_nanos(100)); self.pending_cycles -= 1; } - // if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() { - // self.interrupt(); - // } - let opcode = match self.read(self.pc) { - Ok(byte) => byte, - Err(_) => { - println!("Failed to read from memory at address {:#06x}!", self.pc); - return; - } - }; + if !self.get_flag(StatusFlag::IrqDisable) && self.irq { + self.interrupt(); + } + let opcode = self.read(self.pc); + // let opcode = match self.read(&self.memory, self.pc) { + // Ok(byte) => byte, + // Err(_) => { + // println!("Failed to read from memory at address {:#06x}!", self.pc); + // return; + // } + // }; let instruction = get_instruction(opcode); match instruction { Instruction::Valid(valid_instruction) => { @@ -189,18 +267,8 @@ impl Cpu { } Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode { 0x02 => { - let memory = match self.memory.lock() { - Ok(read) => read, - Err(_) => { - println!("Couldn't acquire read lock on memory in cpu thread"); - return; - } - }; - // println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq.try_recv().unwrap_or_default(), nmi = self.nmi); - memory - .dump(PathBuf::from_str("./cpu_dump.bin").unwrap()) - .unwrap(); + &self.memory.dump(); } _ => { println!( @@ -212,8 +280,7 @@ impl Cpu { } self.cycle_count += 1; } - pub fn stop(&mut self) { - unimplemented!() + self.stopped = true; } } diff --git a/src/instructions.rs b/src/instructions.rs index bb40e09..ddc8189 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -1,6 +1,7 @@ #![allow(clippy::upper_case_acronyms)] use crate::cpu::{Cpu, StatusFlag}; +use crate::memory::{MemoryReader, MemoryWriter}; use crate::types::{Byte, Word}; use std::collections::HashMap; @@ -1869,7 +1870,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { // zp - let address = cpu.read(cpu.pc)?; + let address = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address as Word)) } @@ -1898,7 +1899,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte = cpu.read(cpu.pc)?; + let byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); let address: Word = (byte.wrapping_add(cpu.x)) as Word; Ok(AddressingModeValue::Absolute(address)) @@ -1908,7 +1909,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte = cpu.read(cpu.pc)?; + let byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); let address: Word = (byte + cpu.y) as Word; Ok(AddressingModeValue::Absolute(address)) @@ -1938,7 +1939,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte: Byte = cpu.read(cpu.pc)?; + let byte: Byte = cpu.read(cpu.pc); let address: Word = cpu.read_word(byte as Word)?; cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address as Word)) @@ -1948,7 +1949,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte = cpu.read(cpu.pc)?; + let byte = cpu.read(cpu.pc); let address = cpu.read_word((byte.wrapping_add(cpu.x)) as Word)?; // Anytime you see something like `byte as Word`, it's using the byte as a zero-page address cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address)) @@ -1958,7 +1959,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte: Byte = cpu.read(cpu.pc)?; + let byte: Byte = cpu.read(cpu.pc); let address: Word = cpu.read_word(byte.wrapping_add(cpu.y) as Word)?; cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address)) @@ -1968,7 +1969,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte: Byte = cpu.read(cpu.pc)?; + let byte: Byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Relative(byte)) } @@ -1977,9 +1978,9 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result Result { - let byte: Byte = cpu.read(cpu.pc)?; + let byte: Byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); - let address = cpu.read(cpu.pc)?; + let address = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::RelativeTest(byte, address)) } @@ -2055,7 +2056,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as Word + byte as Word + carry as Word; cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value())); @@ -2083,7 +2084,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - cpu.a &= cpu.read(address.try_into()?)?; + cpu.a &= cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) @@ -2108,9 +2109,9 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?)?; + let value = cpu.read(address.try_into()?); let result = asl(cpu, value); - cpu.write(address.try_into()?, result)?; + cpu.write(address.try_into()?, result); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2123,7 +2124,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, value)?; Ok(()) } @@ -2138,7 +2139,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, value)?; Ok(()) } @@ -2153,7 +2154,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, value)?; Ok(()) } @@ -2168,7 +2169,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, value)?; Ok(()) } @@ -2183,7 +2184,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, value)?; Ok(()) } @@ -2198,7 +2199,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, value)?; Ok(()) } @@ -2213,7 +2214,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, value)?; Ok(()) } @@ -2228,7 +2229,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, value)?; Ok(()) } @@ -2243,7 +2244,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, value)?; Ok(()) } @@ -2258,7 +2259,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, value)?; Ok(()) } @@ -2273,7 +2274,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, value)?; Ok(()) } @@ -2288,7 +2289,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, value)?; Ok(()) } @@ -2303,7 +2304,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, value)?; Ok(()) } @@ -2318,7 +2319,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, value)?; Ok(()) } @@ -2333,7 +2334,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, value)?; Ok(()) } @@ -2348,7 +2349,7 @@ impl Opcode { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; - let test_byte = cpu.read(zero_page_address)?; + let test_byte = cpu.read(zero_page_address); branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, value)?; Ok(()) } @@ -2389,7 +2390,7 @@ impl Opcode { | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; - let result = cpu.a & cpu.read(address.try_into()?)?; + let result = cpu.a & cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Zero, result == 0); cpu.set_flag( StatusFlag::Overflow, @@ -2498,7 +2499,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Carry, cpu.a >= byte); cpu.set_flag(StatusFlag::Zero, cpu.a == byte); cpu.set_flag(StatusFlag::Negative, cpu.a <= byte); @@ -2511,7 +2512,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::AbsoluteA => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Carry, cpu.x >= byte); cpu.set_flag(StatusFlag::Zero, cpu.x == byte); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte)); @@ -2524,7 +2525,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::AbsoluteA => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); cpu.set_flag(StatusFlag::Zero, cpu.y == byte); cpu.set_flag(StatusFlag::Negative, cpu.y <= byte); @@ -2538,9 +2539,9 @@ impl Opcode { | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let dec_byte = byte.wrapping_sub(1); - cpu.write(address.try_into()?, dec_byte)?; + cpu.write(address.try_into()?, dec_byte); cpu.set_flag(StatusFlag::Zero, dec_byte == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte)); Ok(()) @@ -2576,7 +2577,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - cpu.a ^= cpu.read(address.try_into()?)?; + cpu.a ^= cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) @@ -2595,10 +2596,10 @@ impl Opcode { | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Zero, byte.wrapping_add(1) == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(byte.wrapping_add(1))); - cpu.write(address.try_into()?, byte.wrapping_add(1))?; + cpu.write(address.try_into()?, byte.wrapping_add(1)); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2652,7 +2653,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedWithX | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.a = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); @@ -2667,7 +2668,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithY => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.x = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); @@ -2682,7 +2683,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); cpu.y = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); @@ -2708,9 +2709,9 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?)?; + let value = cpu.read(address.try_into()?); let result = lsr(cpu, value); - cpu.write(address.try_into()?, result)?; + cpu.write(address.try_into()?, result); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2731,7 +2732,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - cpu.a |= cpu.read(address.try_into()?)?; + cpu.a |= cpu.read(address.try_into()?); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) @@ -2797,9 +2798,9 @@ impl Opcode { Opcode::RMB0(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1111_1110; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2807,9 +2808,9 @@ impl Opcode { Opcode::RMB1(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1111_1101; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2817,9 +2818,9 @@ impl Opcode { Opcode::RMB2(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1111_1011; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2827,9 +2828,9 @@ impl Opcode { Opcode::RMB3(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1111_0111; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2837,9 +2838,9 @@ impl Opcode { Opcode::RMB4(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1110_1111; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2847,9 +2848,9 @@ impl Opcode { Opcode::RMB5(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1101_1111; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2857,9 +2858,9 @@ impl Opcode { Opcode::RMB6(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b1011_1111; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2867,9 +2868,9 @@ impl Opcode { Opcode::RMB7(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b0111_1111; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2893,9 +2894,9 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?)?; + let value = cpu.read(address.try_into()?); let result = rol(cpu, value); - cpu.write(address.try_into()?, result)?; + cpu.write(address.try_into()?, result); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2920,9 +2921,9 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?)?; + let value = cpu.read(address.try_into()?); let result = ror(cpu, value); - cpu.write(address.try_into()?, result)?; + cpu.write(address.try_into()?, result); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -2955,7 +2956,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as Word + byte as Word + !carry as Word; cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value())); @@ -2995,9 +2996,9 @@ impl Opcode { Opcode::SMB0(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0000_0001; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3005,9 +3006,9 @@ impl Opcode { Opcode::SMB1(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0000_0010; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3015,9 +3016,9 @@ impl Opcode { Opcode::SMB2(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0000_0100; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3025,9 +3026,9 @@ impl Opcode { Opcode::SMB3(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0000_1000; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3035,9 +3036,9 @@ impl Opcode { Opcode::SMB4(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0001_0000; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3045,9 +3046,9 @@ impl Opcode { Opcode::SMB5(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b0010_0000; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3055,9 +3056,9 @@ impl Opcode { Opcode::SMB6(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte & 0b0100_0000; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3065,9 +3066,9 @@ impl Opcode { Opcode::SMB7(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; + let byte = cpu.read(address.try_into()?); let reset_byte = byte | 0b1000_0000; - cpu.write(address.try_into()?, reset_byte)?; + cpu.write(address.try_into()?, reset_byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3082,7 +3083,7 @@ impl Opcode { | AddressingMode::ZeroPageIndexedWithX | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.a)?; + cpu.write(address.try_into()?, cpu.a); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3099,7 +3100,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithY => { let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.x)?; + cpu.write(address.try_into()?, cpu.x); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3109,7 +3110,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.y)?; + cpu.write(address.try_into()?, cpu.y); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3120,7 +3121,7 @@ impl Opcode { | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, 0)?; + cpu.write(address.try_into()?, 0); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) @@ -3149,8 +3150,8 @@ impl Opcode { // (Garth rocks) http://forum.6502.org/viewtopic.php?t=48 AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; - cpu.write(address.try_into()?, !cpu.a & byte)?; + let byte = cpu.read(address.try_into()?); + cpu.write(address.try_into()?, !cpu.a & byte); cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); Ok(()) } @@ -3159,8 +3160,8 @@ impl Opcode { Opcode::TSB(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?)?; - cpu.write(address.try_into()?, cpu.a | byte)?; + let byte = cpu.read(address.try_into()?); + cpu.write(address.try_into()?, cpu.a | byte); Ok(()) } _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) diff --git a/src/keyboard.rs b/src/keyboard.rs index 0a3f94c..91931a2 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,18 +1,23 @@ use minifb::{InputCallback, Key}; -use std::sync::{Arc, Mutex}; -use crate::memory::Mem; +use crate::memory::{MemHandle, MemoryWriter}; pub struct Keyboard { - memory: Arc>, + memory: MemHandle, } impl Keyboard { - pub fn new(memory: Arc>) -> Self { + pub fn new(memory: MemHandle) -> Self { Self { memory } } } +impl MemoryWriter for Keyboard { + fn write(&self, address: u16, data: u8) { + self.memory.write(address, data); + } +} + impl InputCallback for Keyboard { fn add_char(&mut self, _uni_char: u32) {} fn set_key_state(&mut self, key: Key, _state: bool) { @@ -70,18 +75,11 @@ impl InputCallback for Keyboard { _ => {} }; - match &mut self.memory.lock() { - Ok(memory) => { - memory.write(0x4400, row0); - memory.write(0x4401, row1); - memory.write(0x4402, row2); - memory.write(0x4403, row3); - memory.write(0x4404, row4); - memory.write(0x4405, row5); - } - Err(_error) => { - println!("couldnt get a lock on memory while saving keyboard registers"); - } - } + self.memory.write(0x4400, row0); + self.memory.write(0x4401, row1); + self.memory.write(0x4402, row2); + self.memory.write(0x4403, row3); + self.memory.write(0x4404, row4); + self.memory.write(0x4405, row5); } } diff --git a/src/main.rs b/src/main.rs index a7a1cf7..60fff2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ use crate::keyboard::Keyboard; use crate::memory::Mem; use crate::video::Crtc; +use cpu::{CpuController, CpuReceiver}; +use memory::MemHandle; // use clap::Parser; use minifb::{Scale, ScaleMode, Window, WindowOptions}; use serde::{Deserialize, Serialize}; @@ -23,7 +25,7 @@ use std::{ io::{stdout, Read, Result}, path::PathBuf, str::FromStr, - sync::{mpsc, Arc, Mutex}, + sync::mpsc, thread, }; @@ -78,20 +80,21 @@ fn main() -> Result<()> { .dump(PathBuf::from_str("./coredump.bin").unwrap()) .unwrap(); - let shared_memory = Arc::new(Mutex::new(memory)); - + let shared_memory = MemHandle::new(memory); + let screen_memory = shared_memory.clone(); let cpu_memory = shared_memory.clone(); - let display_memory = shared_memory.clone(); let keyboard_memory = shared_memory.clone(); - let (interrupt_tx, interrupt_rx) = mpsc::channel(); + let (cpu_tx, cpu_rx) = mpsc::channel(); + let (state_tx, state_rx) = mpsc::channel(); + let screen_cpu_tx = cpu_tx.clone(); let (window_tx, window_rx) = mpsc::channel(); thread::spawn(move || { let mut screen = Crtc::new( - display_memory, + screen_memory, config.char_rom.as_ref(), - interrupt_tx, + CpuController::new(screen_cpu_tx), window_tx, ); screen.run(); @@ -116,9 +119,16 @@ fn main() -> Result<()> { window.set_input_callback(Box::new(Keyboard::new(keyboard_memory))); - let cpu = Cpu::new(cpu_memory, interrupt_rx); - let mut tui = tui::App::new(cpu); - tui.init(); + let mut cpu = Cpu::new(cpu_memory, CpuReceiver::new(cpu_rx), state_tx); + let mut tui = tui::App::new( + CpuController::new(cpu_tx.clone()), + shared_memory.clone(), + state_rx, + ); + thread::spawn(move || { + cpu.reset().unwrap(); + cpu.execute() + }); loop { if event::poll(std::time::Duration::from_millis(16))? { diff --git a/src/memory.rs b/src/memory.rs index 66e3e5b..fb5e739 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,13 +1,56 @@ -use crate::error::MemoryError; use crate::types::{Byte, Word}; +use anyhow::{bail, Result}; use std::io::{self, Write}; use std::path::PathBuf; +use std::str::FromStr; +use std::sync::{Arc, Mutex, MutexGuard}; use std::u16; use std::{fs::File, io::Read}; +#[derive(Clone, Debug)] +pub struct MemHandle(Arc>); +impl MemHandle { + pub fn new(memory: Mem) -> Self { + Self(Arc::new(Mutex::new(memory))) + } + pub fn read(&self, address: Word) -> Byte { + let memory = self.lock().unwrap(); + memory.read(address) + } + pub fn write(&self, address: Word, data: Byte) { + let mut memory = self.lock().unwrap(); + memory.write(address, data); + } + + pub fn dump(&self) { + let memory = self.lock().unwrap(); + memory.dump(PathBuf::from_str("./cpu_dump.bin").unwrap()); + } + + fn lock(&self) -> Result> { + match self.0.lock() { + Ok(result) => Ok(result), + Err(error) => bail!("{error}"), + } + } +} + +// TODO: impl Iterator for MemHandle so we can get references to the underlying data from other threads without cloning + +pub trait MemoryReader { + fn read(&self, address: u16) -> u8; +} + +pub trait MemoryWriter { + fn write(&self, address: u16, data: u8); +} + +// pub trait MemoryAccess: MemoryReader + MemoryWriter {} +// impl MemoryAccess for T where T: MemoryReader + MemoryWriter {} + #[derive(Debug, Clone)] pub struct Mem { - pub data: Vec, + pub data: Vec, } impl Mem { @@ -30,27 +73,25 @@ impl Mem { self.data[address as usize] = data; } - pub fn load_rom(&mut self, rom: File) -> Result<(), MemoryError> { + pub fn load_rom(&mut self, rom: File) -> Result<()> { let bytes = rom.bytes(); for (address, byte) in bytes.enumerate() { match byte { Ok(value) => self.write(address as Word + 0x8000, value), Err(_) => { - println!("Loading rom: couldn't write byte {:#04x}", address); - return Err(MemoryError::Unwritable); + bail!("Loading rom: couldn't write byte {:#04x}", address); } } } Ok(()) } - //pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> { + //pub fn read_from_bin(&mut self, f: File) -> Result<()> { // let bytes = f.bytes(); // for (address, byte) in bytes.enumerate() { // match byte { // Ok(value) => self.write(address as Word, value), // Err(_) => { - // println!("couldn't write byte {:#04x}", address); - // return Err(MemoryError::Unwritable); + // bail!("couldn't write byte {:#04x}", address); // } // } // } diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 31a0110..49c9793 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -2,44 +2,56 @@ mod tabs; mod term; -use std::{io::Result, time::Duration}; +use std::{ + io::Result, + sync::{mpsc::Receiver, Arc, Mutex}, + time::Duration, +}; use crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ + layout::Layout, prelude::*, widgets::{Block, Cell, Paragraph, Row, Table, TableState}, }; -use crate::cpu::Cpu; +use crate::{ + cpu::{CpuController, CpuState}, + memory::{MemHandle, MemoryReader, MemoryWriter}, +}; pub struct App { - cpu: Cpu, + cpu: CpuController, + state: Receiver, + memory: MemHandle, running: bool, table_state: TableState, } +impl MemoryReader for App { + fn read(&self, address: u16) -> u8 { + self.memory.read(address) + } +} +impl MemoryWriter for App { + fn write(&self, address: u16, data: u8) { + self.memory.write(address, data) + } +} + impl App { - pub fn new(cpu: Cpu) -> Self { + pub fn new(cpu: CpuController, memory: MemHandle, state: Receiver) -> Self { Self { cpu, + memory, running: true, table_state: TableState::default(), + state, } } - pub fn init(&mut self) { - let _ = self.cpu.reset(); - } pub fn update(&mut self, terminal: &mut Terminal) -> Result<()> { self.draw(terminal)?; self.handle_events()?; - self.handle_cpu()?; - Ok(()) - } - - fn handle_cpu(&mut self) -> Result<()> { - if self.running { - self.cpu.cycle(); - } Ok(()) } @@ -64,14 +76,19 @@ impl App { } fn handle_key_press(&mut self, key: KeyEvent) { + // let mut cpu = self.cpu.lock().unwrap(); match key.code { KeyCode::Enter => { if !self.running { - self.cpu.cycle() + // cpu.cycle() } } KeyCode::Char(' ') => { self.running = !self.running; + match self.running { + true => self.cpu.stop(), + false => self.cpu.resume(), + } } _ => {} }; @@ -83,26 +100,32 @@ impl App { /// matter, but for larger apps this can be a significant performance improvement. impl Widget for &App { fn render(self, area: Rect, buf: &mut Buffer) { + self.cpu.data(); let [cpu_area, memory_area] = Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(area); + let [memory_table, memory_info] = + Layout::vertical([Constraint::Fill(1), Constraint::Percentage(10)]).areas(memory_area); - let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.cpu.a, x = self.cpu.x, y = self.cpu.y, pc = self.cpu.pc, s = self.cpu.s, p = self.cpu.p, irq = self.cpu.irq, nmi = self.cpu.nmi); + let cpu = self.state.recv().unwrap(); + let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); Paragraph::new(cpu_info) .block(Block::bordered().title("cpu info!")) .render(cpu_area, buf); - let memory = self.cpu.memory.lock().unwrap(); - let table_height = memory_area.rows().count() - 2; - let rows: Vec = memory.data[0..table_height * 16] - .chunks(16) - .map(|chunk| { - chunk - .iter() - .map(|content| Cell::from(Text::from(format!("{content:#02}")))) - .collect::() - }) - .collect(); + // let table_height = memory_table.rows().count() - 2; + // TODO: impl Iterator for MemHandle so we can get references to the underlying data from other threads without cloning + // let rows: Vec = self.memory.data()[0..table_height * 16] + // .chunks(16) + // .map(|chunk| { + // chunk + // .iter() + // .map(|content| Cell::from(Text::from(format!("{content:x}")))) + // .collect::() + // }) + // .collect(); + let zero = self.memory.read(0); + let rows: Vec = vec![Row::new([format!("{zero}")])]; let widths = vec![Constraint::Length(2); 16]; Widget::render( Table::new(rows, widths) @@ -111,14 +134,16 @@ impl Widget for &App { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", ]) - .style(Style::new().bold()) - // To add space between the header and the rest of the rows, specify the margin - .bottom_margin(1), + .style(Style::new().bold()), // To add space between the header and the rest of the rows, specify the margin + // .bottom_margin(1), ) - .block(Block::bordered().title("memory!")), - memory_area, + .block(Block::bordered().title_top("memory")), + memory_table, buf, ); + Paragraph::new(format!("program counter: {:#04x}", cpu.pc)) + .block(Block::bordered().title_top("info")) + .render(memory_info, buf); } } diff --git a/src/video.rs b/src/video.rs index c86ac7b..bbd4a4c 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,24 +1,16 @@ -use crate::Mem; -use std::{ - fs::File, - io::Read, - path::Path, - sync::{mpsc::Sender, Arc, Mutex}, - thread::sleep, - time::Duration, +use crate::{ + cpu::CpuController, + memory::{MemHandle, MemoryReader}, + types::{Byte, Word}, }; +use std::{fs::File, io::Read, path::Path, sync::mpsc::Sender, thread::sleep, time::Duration}; const FG_COLOR: u32 = 0xFFCC00; const BG_COLOR: u32 = 0x110500; - -pub struct Crtc { - memory: Arc>, - buffer: Vec, - char_rom: Vec, - interrupt: Sender, - window: Sender>, -} - +// // BGR for softbuffer +// const FG_COLOR: u32 = 0x00CCFF; +// const BG_COLOR: u32 = 0x000511; +// pub fn get_char_bin

(char_rom: Option

) -> Vec where P: AsRef, @@ -35,11 +27,25 @@ where } } +pub struct Crtc { + memory: MemHandle, + buffer: Vec, + char_rom: Vec, + cpu_controller: CpuController, + window: Sender>, +} + +impl MemoryReader for Crtc { + fn read(&self, address: Word) -> Byte { + self.memory.read(address) + } +} + impl Crtc { pub fn new

( - memory: Arc>, + memory: MemHandle, char_rom: Option

, - interrupt: Sender, + cpu_controller: CpuController, window: Sender>, ) -> Self where @@ -51,31 +57,24 @@ impl Crtc { buffer: vec![0; 512 * 380], window, char_rom, - interrupt, + cpu_controller, } } fn draw(&mut self) { - let memory = match self.memory.lock() { - Ok(memory) => memory, - Err(_) => { - println!("Couldn't acquire read on shared memory in video thread"); - return; - } - }; - // the rest of this function is arcane wizardry // based on the specifics of george's weird // display and characters... don't fuck around w it let mut i = 0; for char_row in 0..29 { for char_col in 0..64 { - let ascii = memory.read(0x6000 + i); + let ascii = self.memory.read(0x6000 + i); i += 1; for row in 0..13 { let byte = self.char_rom[ascii as usize + (row * 0x100)]; - for i in (0..8).rev() { - let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i); - if (byte << i) & 0x80 == 0x80 { + for bit_index in (0..8).rev() { + let buffer_index = + ((char_row) * 13 + (row)) * 512 + (char_col * 8 + bit_index); + if (byte << bit_index) & 0x80 == 0x80 { self.buffer[buffer_index] = FG_COLOR; } else { self.buffer[buffer_index] = BG_COLOR; @@ -92,10 +91,9 @@ impl Crtc { pub fn run(&mut self) { loop { - let _ = self.interrupt.send(false); + let _ = self.cpu_controller.irq(); sleep(Duration::from_millis(16)); self.draw(); - let _ = self.interrupt.send(true); } } }