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