Architecture changes

This commit is contained in:
august kline 2024-06-19 17:26:09 -04:00
parent 0ec54d6672
commit f9198cd0b1
7 changed files with 393 additions and 253 deletions

View File

@ -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<CpuControl>);
pub enum CpuControl {
Irq,
Nmi,
Stop,
Resume,
Data,
}
impl CpuController {
pub fn new(sender: Sender<CpuControl>) -> 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<CpuControl>);
impl CpuReceiver {
pub fn new(receiver: Receiver<CpuControl>) -> 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<bool>,
pub irq: bool,
pub nmi: bool,
pub memory: Arc<Mutex<Mem>>,
pub memory: MemHandle,
pub pending_cycles: usize,
receiver: CpuReceiver,
stopped: bool,
cycle_count: usize,
state_tx: Sender<CpuState>,
}
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<Mutex<Mem>>, irq: Receiver<bool>) -> Self {
pub fn new(memory: MemHandle, receiver: CpuReceiver, state_tx: Sender<CpuState>) -> 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<Byte> {
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<Byte> {
// 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<Word> {
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<Byte> {
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;
}
}

View File

@ -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<AddressingModeVal
fn zero_page(cpu: &mut Cpu) -> Result<AddressingModeValue> {
// 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<AddressingModeVal
// zp, x
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// zp, y
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// (zp)
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// (zp, x)
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// (zp), y
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// r
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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<AddressingModeVal
// r
cpu: &mut Cpu,
) -> Result<AddressingModeValue> {
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)

View File

@ -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<Mutex<Mem>>,
memory: MemHandle,
}
impl Keyboard {
pub fn new(memory: Arc<Mutex<Mem>>) -> 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);
}
}

View File

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

View File

@ -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<Mutex<Mem>>);
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<MutexGuard<'_, Mem>> {
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<T> MemoryAccess for T where T: MemoryReader + MemoryWriter {}
#[derive(Debug, Clone)]
pub struct Mem {
pub data: Vec<Byte>,
pub data: Vec<u8>,
}
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);
// }
// }
// }

View File

@ -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<CpuState>,
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<CpuState>) -> 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<impl Backend>) -> 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<Row> = memory.data[0..table_height * 16]
.chunks(16)
.map(|chunk| {
chunk
.iter()
.map(|content| Cell::from(Text::from(format!("{content:#02}"))))
.collect::<Row>()
})
.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<Row> = self.memory.data()[0..table_height * 16]
// .chunks(16)
// .map(|chunk| {
// chunk
// .iter()
// .map(|content| Cell::from(Text::from(format!("{content:x}"))))
// .collect::<Row>()
// })
// .collect();
let zero = self.memory.read(0);
let rows: Vec<Row> = 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);
}
}

View File

@ -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<Mutex<Mem>>,
buffer: Vec<u32>,
char_rom: Vec<u8>,
interrupt: Sender<bool>,
window: Sender<Vec<u32>>,
}
// // BGR for softbuffer
// const FG_COLOR: u32 = 0x00CCFF;
// const BG_COLOR: u32 = 0x000511;
//
pub fn get_char_bin<P>(char_rom: Option<P>) -> Vec<u8>
where
P: AsRef<Path>,
@ -35,11 +27,25 @@ where
}
}
pub struct Crtc {
memory: MemHandle,
buffer: Vec<u32>,
char_rom: Vec<u8>,
cpu_controller: CpuController,
window: Sender<Vec<u32>>,
}
impl MemoryReader for Crtc {
fn read(&self, address: Word) -> Byte {
self.memory.read(address)
}
}
impl Crtc {
pub fn new<P>(
memory: Arc<Mutex<Mem>>,
memory: MemHandle,
char_rom: Option<P>,
interrupt: Sender<bool>,
cpu_controller: CpuController,
window: Sender<Vec<u32>>,
) -> 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);
}
}
}