Architecture changes
This commit is contained in:
		
							parent
							
								
									0ec54d6672
								
							
						
					
					
						commit
						f9198cd0b1
					
				
							
								
								
									
										191
									
								
								src/cpu.rs
								
								
								
								
							
							
						
						
									
										191
									
								
								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<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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										30
									
								
								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))? {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    //            }
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										66
									
								
								src/video.rs
								
								
								
								
							
							
						
						
									
										66
									
								
								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<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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue