Architecture changes
This commit is contained in:
parent
0ec54d6672
commit
f9198cd0b1
189
src/cpu.rs
189
src/cpu.rs
|
@ -1,17 +1,11 @@
|
||||||
use crate::instructions::{get_instruction, Instruction};
|
use crate::instructions::{get_instruction, Instruction};
|
||||||
use crate::memory::Mem;
|
use crate::memory::{MemHandle, MemoryReader, MemoryWriter};
|
||||||
use crate::types::{Byte, Word};
|
use crate::types::{Byte, Word};
|
||||||
use std::cell::RefCell;
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
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::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum StatusFlag {
|
pub enum StatusFlag {
|
||||||
|
@ -25,6 +19,55 @@ pub enum StatusFlag {
|
||||||
Carry = 0b0000_0001,
|
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 struct Cpu {
|
||||||
pub a: Byte, // Accumulator Register
|
pub a: Byte, // Accumulator Register
|
||||||
pub x: Byte, // X Register
|
pub x: Byte, // X Register
|
||||||
|
@ -32,15 +75,29 @@ pub struct Cpu {
|
||||||
pub pc: Word, // Program Counter
|
pub pc: Word, // Program Counter
|
||||||
pub s: Byte, // Stack Pointer
|
pub s: Byte, // Stack Pointer
|
||||||
pub p: Byte, // Status Register
|
pub p: Byte, // Status Register
|
||||||
pub irq: Receiver<bool>,
|
pub irq: bool,
|
||||||
pub nmi: bool,
|
pub nmi: bool,
|
||||||
pub memory: Arc<Mutex<Mem>>,
|
pub memory: MemHandle,
|
||||||
pub pending_cycles: usize,
|
pub pending_cycles: usize,
|
||||||
|
receiver: CpuReceiver,
|
||||||
|
stopped: bool,
|
||||||
cycle_count: usize,
|
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 {
|
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 {
|
Cpu {
|
||||||
a: 0x00,
|
a: 0x00,
|
||||||
x: 0x00,
|
x: 0x00,
|
||||||
|
@ -48,11 +105,14 @@ impl Cpu {
|
||||||
pc: 0x0000,
|
pc: 0x0000,
|
||||||
s: 0xFF,
|
s: 0xFF,
|
||||||
p: 0b0010_0100,
|
p: 0b0010_0100,
|
||||||
irq,
|
irq: false,
|
||||||
nmi: false,
|
nmi: false,
|
||||||
|
receiver,
|
||||||
memory,
|
memory,
|
||||||
|
stopped: false,
|
||||||
pending_cycles: 0,
|
pending_cycles: 0,
|
||||||
cycle_count: 0,
|
cycle_count: 0,
|
||||||
|
state_tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn reset(&mut self) -> Result<()> {
|
pub fn reset(&mut self) -> Result<()> {
|
||||||
|
@ -61,18 +121,18 @@ impl Cpu {
|
||||||
self.pending_cycles = 0;
|
self.pending_cycles = 0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn read(&self, address: Word) -> Result<Byte> {
|
// pub fn read(&self, address: Word) -> Result<Byte> {
|
||||||
let memory = match self.memory.lock() {
|
// let memory = match self.memory.lock() {
|
||||||
Ok(read) => read,
|
// Ok(read) => read,
|
||||||
Err(_) => {
|
// Err(_) => {
|
||||||
bail!("Couldn't acquire lock on memory in cpu thread")
|
// bail!("Couldn't acquire lock on memory in cpu thread")
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
Ok(memory.read(address))
|
// Ok(memory.read(address))
|
||||||
}
|
// }
|
||||||
pub fn read_word(&self, address: Word) -> Result<Word> {
|
pub fn read_word(&self, address: Word) -> Result<Word> {
|
||||||
let low_byte = self.read(address)?;
|
let low_byte = self.read(address);
|
||||||
let high_byte = self.read(address + 0x1)?;
|
let high_byte = self.read(address + 0x1);
|
||||||
Ok((high_byte as u16) << 8 | (low_byte as u16))
|
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<()> {
|
pub fn push_stack(&mut self, data: Byte) -> Result<()> {
|
||||||
self.s = self.s.wrapping_sub(0x1);
|
self.s = self.s.wrapping_sub(0x1);
|
||||||
self.write(self.stack_addr(), data)?;
|
self.write(self.stack_addr(), data);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_stack_word(&mut self, address: Word) -> Result<()> {
|
pub fn push_stack_word(&mut self, address: Word) -> Result<()> {
|
||||||
self.s = self.s.wrapping_sub(0x1);
|
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.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_stack(&mut self) -> Result<Byte> {
|
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);
|
self.s = self.s.wrapping_add(0x1);
|
||||||
Ok(byte)
|
Ok(byte)
|
||||||
}
|
}
|
||||||
|
@ -123,17 +183,6 @@ impl Cpu {
|
||||||
value & 0b1000_0000 == 0b1000_0000
|
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) {
|
pub fn execute(&mut self) {
|
||||||
self.cycle();
|
self.cycle();
|
||||||
while self.pending_cycles != 0 {
|
while self.pending_cycles != 0 {
|
||||||
|
@ -146,28 +195,57 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupt(&mut self) {
|
pub fn interrupt(&mut self) {
|
||||||
|
self.irq = false;
|
||||||
self.push_stack_word(self.pc).unwrap();
|
self.push_stack_word(self.pc).unwrap();
|
||||||
self.push_stack(self.p).unwrap();
|
self.push_stack(self.p).unwrap();
|
||||||
self.set_flag(StatusFlag::IrqDisable, true);
|
self.set_flag(StatusFlag::IrqDisable, true);
|
||||||
self.pc = self.read_word(0xFFFE).unwrap();
|
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) {
|
pub fn cycle(&mut self) {
|
||||||
|
self.receive_control();
|
||||||
|
if self.stopped {
|
||||||
|
return;
|
||||||
|
}
|
||||||
while self.pending_cycles != 0 {
|
while self.pending_cycles != 0 {
|
||||||
// roughly cycle-accurate timing
|
// roughly cycle-accurate timing
|
||||||
sleep(Duration::from_nanos(100));
|
sleep(Duration::from_nanos(100));
|
||||||
self.pending_cycles -= 1;
|
self.pending_cycles -= 1;
|
||||||
}
|
}
|
||||||
// if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() {
|
if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
|
||||||
// self.interrupt();
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
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);
|
let instruction = get_instruction(opcode);
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Valid(valid_instruction) => {
|
Instruction::Valid(valid_instruction) => {
|
||||||
|
@ -189,18 +267,8 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
|
Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
|
||||||
0x02 => {
|
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);
|
// 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
|
&self.memory.dump();
|
||||||
.dump(PathBuf::from_str("./cpu_dump.bin").unwrap())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!(
|
println!(
|
||||||
|
@ -212,8 +280,7 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
self.cycle_count += 1;
|
self.cycle_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
unimplemented!()
|
self.stopped = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(clippy::upper_case_acronyms)]
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use crate::cpu::{Cpu, StatusFlag};
|
use crate::cpu::{Cpu, StatusFlag};
|
||||||
|
use crate::memory::{MemoryReader, MemoryWriter};
|
||||||
use crate::types::{Byte, Word};
|
use crate::types::{Byte, Word};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
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> {
|
fn zero_page(cpu: &mut Cpu) -> Result<AddressingModeValue> {
|
||||||
// zp
|
// zp
|
||||||
let address = cpu.read(cpu.pc)?;
|
let address = cpu.read(cpu.pc);
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::Absolute(address as Word))
|
Ok(AddressingModeValue::Absolute(address as Word))
|
||||||
}
|
}
|
||||||
|
@ -1898,7 +1899,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// zp, x
|
// zp, x
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> Result<AddressingModeValue> {
|
||||||
let byte = cpu.read(cpu.pc)?;
|
let byte = cpu.read(cpu.pc);
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
let address: Word = (byte.wrapping_add(cpu.x)) as Word;
|
let address: Word = (byte.wrapping_add(cpu.x)) as Word;
|
||||||
Ok(AddressingModeValue::Absolute(address))
|
Ok(AddressingModeValue::Absolute(address))
|
||||||
|
@ -1908,7 +1909,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// zp, y
|
// zp, y
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> Result<AddressingModeValue> {
|
||||||
let byte = cpu.read(cpu.pc)?;
|
let byte = cpu.read(cpu.pc);
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
let address: Word = (byte + cpu.y) as Word;
|
let address: Word = (byte + cpu.y) as Word;
|
||||||
Ok(AddressingModeValue::Absolute(address))
|
Ok(AddressingModeValue::Absolute(address))
|
||||||
|
@ -1938,7 +1939,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// (zp)
|
// (zp)
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> 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)?;
|
let address: Word = cpu.read_word(byte as Word)?;
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::Absolute(address as Word))
|
Ok(AddressingModeValue::Absolute(address as Word))
|
||||||
|
@ -1948,7 +1949,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// (zp, x)
|
// (zp, x)
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> 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
|
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);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::Absolute(address))
|
Ok(AddressingModeValue::Absolute(address))
|
||||||
|
@ -1958,7 +1959,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// (zp), y
|
// (zp), y
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> 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)?;
|
let address: Word = cpu.read_word(byte.wrapping_add(cpu.y) as Word)?;
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::Absolute(address))
|
Ok(AddressingModeValue::Absolute(address))
|
||||||
|
@ -1968,7 +1969,7 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// r
|
// r
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> Result<AddressingModeValue> {
|
||||||
let byte: Byte = cpu.read(cpu.pc)?;
|
let byte: Byte = cpu.read(cpu.pc);
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::Relative(byte))
|
Ok(AddressingModeValue::Relative(byte))
|
||||||
}
|
}
|
||||||
|
@ -1977,9 +1978,9 @@ fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result<AddressingModeVal
|
||||||
// r
|
// r
|
||||||
cpu: &mut Cpu,
|
cpu: &mut Cpu,
|
||||||
) -> Result<AddressingModeValue> {
|
) -> Result<AddressingModeValue> {
|
||||||
let byte: Byte = cpu.read(cpu.pc)?;
|
let byte: Byte = cpu.read(cpu.pc);
|
||||||
cpu.pc = cpu.pc.wrapping_add(1);
|
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);
|
cpu.pc = cpu.pc.wrapping_add(1);
|
||||||
Ok(AddressingModeValue::RelativeTest(byte, address))
|
Ok(AddressingModeValue::RelativeTest(byte, address))
|
||||||
}
|
}
|
||||||
|
@ -2055,7 +2056,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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 carry = cpu.get_flag(StatusFlag::Carry);
|
||||||
let result = cpu.a as Word + byte as Word + carry as Word;
|
let result = cpu.a as Word + byte as Word + carry as Word;
|
||||||
cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value()));
|
cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value()));
|
||||||
|
@ -2083,7 +2084,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Zero, cpu.a == 0);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2108,9 +2109,9 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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);
|
let result = asl(cpu, value);
|
||||||
cpu.write(address.try_into()?, result)?;
|
cpu.write(address.try_into()?, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2138,7 +2139,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2153,7 +2154,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2168,7 +2169,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2183,7 +2184,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2198,7 +2199,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2213,7 +2214,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2228,7 +2229,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2243,7 +2244,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2258,7 +2259,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2273,7 +2274,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2288,7 +2289,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2303,7 +2304,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2318,7 +2319,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2333,7 +2334,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2348,7 +2349,7 @@ impl Opcode {
|
||||||
Ok(value) => match value {
|
Ok(value) => match value {
|
||||||
AddressingModeValue::RelativeTest(byte, _address) => {
|
AddressingModeValue::RelativeTest(byte, _address) => {
|
||||||
let zero_page_address = byte as Word;
|
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)?;
|
branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2389,7 +2390,7 @@ impl Opcode {
|
||||||
| AddressingMode::AbsoluteA
|
| AddressingMode::AbsoluteA
|
||||||
| AddressingMode::AbsoluteIndexedWithX => {
|
| AddressingMode::AbsoluteIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Zero, result == 0);
|
||||||
cpu.set_flag(
|
cpu.set_flag(
|
||||||
StatusFlag::Overflow,
|
StatusFlag::Overflow,
|
||||||
|
@ -2498,7 +2499,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Carry, cpu.a >= byte);
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.a == byte);
|
cpu.set_flag(StatusFlag::Zero, cpu.a == byte);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.a <= byte);
|
cpu.set_flag(StatusFlag::Negative, cpu.a <= byte);
|
||||||
|
@ -2511,7 +2512,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::AbsoluteA => {
|
| AddressingMode::AbsoluteA => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Carry, cpu.x >= byte);
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.x == byte);
|
cpu.set_flag(StatusFlag::Zero, cpu.x == byte);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte));
|
||||||
|
@ -2524,7 +2525,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::AbsoluteA => {
|
| AddressingMode::AbsoluteA => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Carry, cpu.y >= byte);
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.y == byte);
|
cpu.set_flag(StatusFlag::Zero, cpu.y == byte);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.y <= byte);
|
cpu.set_flag(StatusFlag::Negative, cpu.y <= byte);
|
||||||
|
@ -2538,9 +2539,9 @@ impl Opcode {
|
||||||
| AddressingMode::AbsoluteA
|
| AddressingMode::AbsoluteA
|
||||||
| AddressingMode::AbsoluteIndexedWithX => {
|
| AddressingMode::AbsoluteIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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);
|
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::Zero, dec_byte == 0);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2576,7 +2577,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Zero, cpu.a == 0);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2595,10 +2596,10 @@ impl Opcode {
|
||||||
| AddressingMode::AbsoluteA
|
| AddressingMode::AbsoluteA
|
||||||
| AddressingMode::AbsoluteIndexedWithX => {
|
| AddressingMode::AbsoluteIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Zero, byte.wrapping_add(1) == 0);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(byte.wrapping_add(1)));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -2652,7 +2653,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedWithX
|
| AddressingMode::ZeroPageIndexedWithX
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
let byte = cpu.read(address.try_into()?)?;
|
let byte = cpu.read(address.try_into()?);
|
||||||
cpu.a = byte;
|
cpu.a = byte;
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.a == 0);
|
cpu.set_flag(StatusFlag::Zero, cpu.a == 0);
|
||||||
|
@ -2667,7 +2668,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithY => {
|
| AddressingMode::ZeroPageIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
let byte = cpu.read(address.try_into()?)?;
|
let byte = cpu.read(address.try_into()?);
|
||||||
cpu.x = byte;
|
cpu.x = byte;
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x));
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.x == 0);
|
cpu.set_flag(StatusFlag::Zero, cpu.x == 0);
|
||||||
|
@ -2682,7 +2683,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
let byte = cpu.read(address.try_into()?)?;
|
let byte = cpu.read(address.try_into()?);
|
||||||
cpu.y = byte;
|
cpu.y = byte;
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y));
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.y == 0);
|
cpu.set_flag(StatusFlag::Zero, cpu.y == 0);
|
||||||
|
@ -2708,9 +2709,9 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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);
|
let result = lsr(cpu, value);
|
||||||
cpu.write(address.try_into()?, result)?;
|
cpu.write(address.try_into()?, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -2731,7 +2732,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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::Zero, cpu.a == 0);
|
||||||
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2797,9 +2798,9 @@ impl Opcode {
|
||||||
Opcode::RMB0(mode) => match mode {
|
Opcode::RMB0(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1111_1110;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB1(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1111_1101;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB2(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1111_1011;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB3(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1111_0111;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB4(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1110_1111;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB5(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1101_1111;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB6(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b1011_1111;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::RMB7(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b0111_1111;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -2893,9 +2894,9 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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);
|
let result = rol(cpu, value);
|
||||||
cpu.write(address.try_into()?, result)?;
|
cpu.write(address.try_into()?, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -2920,9 +2921,9 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
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);
|
let result = ror(cpu, value);
|
||||||
cpu.write(address.try_into()?, result)?;
|
cpu.write(address.try_into()?, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -2955,7 +2956,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedIndirect
|
| AddressingMode::ZeroPageIndexedIndirect
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
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 carry = cpu.get_flag(StatusFlag::Carry);
|
||||||
let result = cpu.a as Word + byte as Word + !carry as Word;
|
let result = cpu.a as Word + byte as Word + !carry as Word;
|
||||||
cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value()));
|
cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value()));
|
||||||
|
@ -2995,9 +2996,9 @@ impl Opcode {
|
||||||
Opcode::SMB0(mode) => match mode {
|
Opcode::SMB0(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0000_0001;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB1(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0000_0010;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB2(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0000_0100;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB3(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0000_1000;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB4(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0001_0000;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB5(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b0010_0000;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB6(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte & 0b0100_0000;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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 {
|
Opcode::SMB7(mode) => match mode {
|
||||||
AddressingMode::ZeroPage => {
|
AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
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;
|
let reset_byte = byte | 0b1000_0000;
|
||||||
cpu.write(address.try_into()?, reset_byte)?;
|
cpu.write(address.try_into()?, reset_byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -3082,7 +3083,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPageIndexedWithX
|
| AddressingMode::ZeroPageIndexedWithX
|
||||||
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
| AddressingMode::ZeroPageIndirectIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
cpu.write(address.try_into()?, cpu.a)?;
|
cpu.write(address.try_into()?, cpu.a);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -3099,7 +3100,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithY => {
|
| AddressingMode::ZeroPageIndexedWithY => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
cpu.write(address.try_into()?, cpu.x)?;
|
cpu.write(address.try_into()?, cpu.x);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -3109,7 +3110,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
cpu.write(address.try_into()?, cpu.y)?;
|
cpu.write(address.try_into()?, cpu.y);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
@ -3120,7 +3121,7 @@ impl Opcode {
|
||||||
| AddressingMode::ZeroPage
|
| AddressingMode::ZeroPage
|
||||||
| AddressingMode::ZeroPageIndexedWithX => {
|
| AddressingMode::ZeroPageIndexedWithX => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
cpu.write(address.try_into()?, 0)?;
|
cpu.write(address.try_into()?, 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => 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
|
// (Garth rocks) http://forum.6502.org/viewtopic.php?t=48
|
||||||
AddressingMode::AbsoluteA | AddressingMode::ZeroPage => {
|
AddressingMode::AbsoluteA | AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
let byte = cpu.read(address.try_into()?)?;
|
let byte = cpu.read(address.try_into()?);
|
||||||
cpu.write(address.try_into()?, !cpu.a & byte)?;
|
cpu.write(address.try_into()?, !cpu.a & byte);
|
||||||
cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0);
|
cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3159,8 +3160,8 @@ impl Opcode {
|
||||||
Opcode::TSB(mode) => match mode {
|
Opcode::TSB(mode) => match mode {
|
||||||
AddressingMode::AbsoluteA | AddressingMode::ZeroPage => {
|
AddressingMode::AbsoluteA | AddressingMode::ZeroPage => {
|
||||||
let address = get_address(mode, cpu)?;
|
let address = get_address(mode, cpu)?;
|
||||||
let byte = cpu.read(address.try_into()?)?;
|
let byte = cpu.read(address.try_into()?);
|
||||||
cpu.write(address.try_into()?, cpu.a | byte)?;
|
cpu.write(address.try_into()?, cpu.a | byte);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
_ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc)
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
use minifb::{InputCallback, Key};
|
use minifb::{InputCallback, Key};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::memory::Mem;
|
use crate::memory::{MemHandle, MemoryWriter};
|
||||||
|
|
||||||
pub struct Keyboard {
|
pub struct Keyboard {
|
||||||
memory: Arc<Mutex<Mem>>,
|
memory: MemHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keyboard {
|
impl Keyboard {
|
||||||
pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
|
pub fn new(memory: MemHandle) -> Self {
|
||||||
Self { memory }
|
Self { memory }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MemoryWriter for Keyboard {
|
||||||
|
fn write(&self, address: u16, data: u8) {
|
||||||
|
self.memory.write(address, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl InputCallback for Keyboard {
|
impl InputCallback for Keyboard {
|
||||||
fn add_char(&mut self, _uni_char: u32) {}
|
fn add_char(&mut self, _uni_char: u32) {}
|
||||||
fn set_key_state(&mut self, key: Key, _state: bool) {
|
fn set_key_state(&mut self, key: Key, _state: bool) {
|
||||||
|
@ -70,18 +75,11 @@ impl InputCallback for Keyboard {
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
match &mut self.memory.lock() {
|
self.memory.write(0x4400, row0);
|
||||||
Ok(memory) => {
|
self.memory.write(0x4401, row1);
|
||||||
memory.write(0x4400, row0);
|
self.memory.write(0x4402, row2);
|
||||||
memory.write(0x4401, row1);
|
self.memory.write(0x4403, row3);
|
||||||
memory.write(0x4402, row2);
|
self.memory.write(0x4404, row4);
|
||||||
memory.write(0x4403, row3);
|
self.memory.write(0x4405, row5);
|
||||||
memory.write(0x4404, row4);
|
|
||||||
memory.write(0x4405, row5);
|
|
||||||
}
|
|
||||||
Err(_error) => {
|
|
||||||
println!("couldnt get a lock on memory while saving keyboard registers");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -14,6 +14,8 @@ use crate::keyboard::Keyboard;
|
||||||
use crate::memory::Mem;
|
use crate::memory::Mem;
|
||||||
use crate::video::Crtc;
|
use crate::video::Crtc;
|
||||||
|
|
||||||
|
use cpu::{CpuController, CpuReceiver};
|
||||||
|
use memory::MemHandle;
|
||||||
// use clap::Parser;
|
// use clap::Parser;
|
||||||
use minifb::{Scale, ScaleMode, Window, WindowOptions};
|
use minifb::{Scale, ScaleMode, Window, WindowOptions};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -23,7 +25,7 @@ use std::{
|
||||||
io::{stdout, Read, Result},
|
io::{stdout, Read, Result},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{mpsc, Arc, Mutex},
|
sync::mpsc,
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,20 +80,21 @@ fn main() -> Result<()> {
|
||||||
.dump(PathBuf::from_str("./coredump.bin").unwrap())
|
.dump(PathBuf::from_str("./coredump.bin").unwrap())
|
||||||
.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 cpu_memory = shared_memory.clone();
|
||||||
let display_memory = shared_memory.clone();
|
|
||||||
let keyboard_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();
|
let (window_tx, window_rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut screen = Crtc::new(
|
let mut screen = Crtc::new(
|
||||||
display_memory,
|
screen_memory,
|
||||||
config.char_rom.as_ref(),
|
config.char_rom.as_ref(),
|
||||||
interrupt_tx,
|
CpuController::new(screen_cpu_tx),
|
||||||
window_tx,
|
window_tx,
|
||||||
);
|
);
|
||||||
screen.run();
|
screen.run();
|
||||||
|
@ -116,9 +119,16 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
window.set_input_callback(Box::new(Keyboard::new(keyboard_memory)));
|
window.set_input_callback(Box::new(Keyboard::new(keyboard_memory)));
|
||||||
|
|
||||||
let cpu = Cpu::new(cpu_memory, interrupt_rx);
|
let mut cpu = Cpu::new(cpu_memory, CpuReceiver::new(cpu_rx), state_tx);
|
||||||
let mut tui = tui::App::new(cpu);
|
let mut tui = tui::App::new(
|
||||||
tui.init();
|
CpuController::new(cpu_tx.clone()),
|
||||||
|
shared_memory.clone(),
|
||||||
|
state_rx,
|
||||||
|
);
|
||||||
|
thread::spawn(move || {
|
||||||
|
cpu.reset().unwrap();
|
||||||
|
cpu.execute()
|
||||||
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if event::poll(std::time::Duration::from_millis(16))? {
|
if event::poll(std::time::Duration::from_millis(16))? {
|
||||||
|
|
|
@ -1,13 +1,56 @@
|
||||||
use crate::error::MemoryError;
|
|
||||||
use crate::types::{Byte, Word};
|
use crate::types::{Byte, Word};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use std::u16;
|
use std::u16;
|
||||||
use std::{fs::File, io::Read};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Mem {
|
pub struct Mem {
|
||||||
pub data: Vec<Byte>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mem {
|
impl Mem {
|
||||||
|
@ -30,27 +73,25 @@ impl Mem {
|
||||||
self.data[address as usize] = data;
|
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();
|
let bytes = rom.bytes();
|
||||||
for (address, byte) in bytes.enumerate() {
|
for (address, byte) in bytes.enumerate() {
|
||||||
match byte {
|
match byte {
|
||||||
Ok(value) => self.write(address as Word + 0x8000, value),
|
Ok(value) => self.write(address as Word + 0x8000, value),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Loading rom: couldn't write byte {:#04x}", address);
|
bail!("Loading rom: couldn't write byte {:#04x}", address);
|
||||||
return Err(MemoryError::Unwritable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
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();
|
// let bytes = f.bytes();
|
||||||
// for (address, byte) in bytes.enumerate() {
|
// for (address, byte) in bytes.enumerate() {
|
||||||
// match byte {
|
// match byte {
|
||||||
// Ok(value) => self.write(address as Word, value),
|
// Ok(value) => self.write(address as Word, value),
|
||||||
// Err(_) => {
|
// Err(_) => {
|
||||||
// println!("couldn't write byte {:#04x}", address);
|
// bail!("couldn't write byte {:#04x}", address);
|
||||||
// return Err(MemoryError::Unwritable);
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -2,44 +2,56 @@
|
||||||
|
|
||||||
mod tabs;
|
mod tabs;
|
||||||
mod term;
|
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 crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
layout::Layout,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Cell, Paragraph, Row, Table, TableState},
|
widgets::{Block, Cell, Paragraph, Row, Table, TableState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::cpu::Cpu;
|
use crate::{
|
||||||
|
cpu::{CpuController, CpuState},
|
||||||
|
memory::{MemHandle, MemoryReader, MemoryWriter},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
cpu: Cpu,
|
cpu: CpuController,
|
||||||
|
state: Receiver<CpuState>,
|
||||||
|
memory: MemHandle,
|
||||||
running: bool,
|
running: bool,
|
||||||
table_state: TableState,
|
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 {
|
impl App {
|
||||||
pub fn new(cpu: Cpu) -> Self {
|
pub fn new(cpu: CpuController, memory: MemHandle, state: Receiver<CpuState>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cpu,
|
cpu,
|
||||||
|
memory,
|
||||||
running: true,
|
running: true,
|
||||||
table_state: TableState::default(),
|
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<()> {
|
pub fn update(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
|
||||||
self.draw(terminal)?;
|
self.draw(terminal)?;
|
||||||
self.handle_events()?;
|
self.handle_events()?;
|
||||||
self.handle_cpu()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_cpu(&mut self) -> Result<()> {
|
|
||||||
if self.running {
|
|
||||||
self.cpu.cycle();
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,14 +76,19 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_press(&mut self, key: KeyEvent) {
|
fn handle_key_press(&mut self, key: KeyEvent) {
|
||||||
|
// let mut cpu = self.cpu.lock().unwrap();
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
if !self.running {
|
if !self.running {
|
||||||
self.cpu.cycle()
|
// cpu.cycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char(' ') => {
|
KeyCode::Char(' ') => {
|
||||||
self.running = !self.running;
|
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.
|
/// matter, but for larger apps this can be a significant performance improvement.
|
||||||
impl Widget for &App {
|
impl Widget for &App {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
self.cpu.data();
|
||||||
let [cpu_area, memory_area] =
|
let [cpu_area, memory_area] =
|
||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(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)
|
Paragraph::new(cpu_info)
|
||||||
.block(Block::bordered().title("cpu info!"))
|
.block(Block::bordered().title("cpu info!"))
|
||||||
.render(cpu_area, buf);
|
.render(cpu_area, buf);
|
||||||
|
|
||||||
let memory = self.cpu.memory.lock().unwrap();
|
// let table_height = memory_table.rows().count() - 2;
|
||||||
let table_height = memory_area.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> = memory.data[0..table_height * 16]
|
// let rows: Vec<Row> = self.memory.data()[0..table_height * 16]
|
||||||
.chunks(16)
|
// .chunks(16)
|
||||||
.map(|chunk| {
|
// .map(|chunk| {
|
||||||
chunk
|
// chunk
|
||||||
.iter()
|
// .iter()
|
||||||
.map(|content| Cell::from(Text::from(format!("{content:#02}"))))
|
// .map(|content| Cell::from(Text::from(format!("{content:x}"))))
|
||||||
.collect::<Row>()
|
// .collect::<Row>()
|
||||||
})
|
// })
|
||||||
.collect();
|
// .collect();
|
||||||
|
let zero = self.memory.read(0);
|
||||||
|
let rows: Vec<Row> = vec![Row::new([format!("{zero}")])];
|
||||||
let widths = vec![Constraint::Length(2); 16];
|
let widths = vec![Constraint::Length(2); 16];
|
||||||
Widget::render(
|
Widget::render(
|
||||||
Table::new(rows, widths)
|
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",
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e",
|
||||||
"f",
|
"f",
|
||||||
])
|
])
|
||||||
.style(Style::new().bold())
|
.style(Style::new().bold()), // To add space between the header and the rest of the rows, specify the margin
|
||||||
// To add space between the header and the rest of the rows, specify the margin
|
// .bottom_margin(1),
|
||||||
.bottom_margin(1),
|
|
||||||
)
|
)
|
||||||
.block(Block::bordered().title("memory!")),
|
.block(Block::bordered().title_top("memory")),
|
||||||
memory_area,
|
memory_table,
|
||||||
buf,
|
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 crate::{
|
||||||
use std::{
|
cpu::CpuController,
|
||||||
fs::File,
|
memory::{MemHandle, MemoryReader},
|
||||||
io::Read,
|
types::{Byte, Word},
|
||||||
path::Path,
|
|
||||||
sync::{mpsc::Sender, Arc, Mutex},
|
|
||||||
thread::sleep,
|
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
use std::{fs::File, io::Read, path::Path, sync::mpsc::Sender, thread::sleep, time::Duration};
|
||||||
|
|
||||||
const FG_COLOR: u32 = 0xFFCC00;
|
const FG_COLOR: u32 = 0xFFCC00;
|
||||||
const BG_COLOR: u32 = 0x110500;
|
const BG_COLOR: u32 = 0x110500;
|
||||||
|
// // BGR for softbuffer
|
||||||
pub struct Crtc {
|
// const FG_COLOR: u32 = 0x00CCFF;
|
||||||
memory: Arc<Mutex<Mem>>,
|
// const BG_COLOR: u32 = 0x000511;
|
||||||
buffer: Vec<u32>,
|
//
|
||||||
char_rom: Vec<u8>,
|
|
||||||
interrupt: Sender<bool>,
|
|
||||||
window: Sender<Vec<u32>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_char_bin<P>(char_rom: Option<P>) -> Vec<u8>
|
pub fn get_char_bin<P>(char_rom: Option<P>) -> Vec<u8>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
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 {
|
impl Crtc {
|
||||||
pub fn new<P>(
|
pub fn new<P>(
|
||||||
memory: Arc<Mutex<Mem>>,
|
memory: MemHandle,
|
||||||
char_rom: Option<P>,
|
char_rom: Option<P>,
|
||||||
interrupt: Sender<bool>,
|
cpu_controller: CpuController,
|
||||||
window: Sender<Vec<u32>>,
|
window: Sender<Vec<u32>>,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
|
@ -51,31 +57,24 @@ impl Crtc {
|
||||||
buffer: vec![0; 512 * 380],
|
buffer: vec![0; 512 * 380],
|
||||||
window,
|
window,
|
||||||
char_rom,
|
char_rom,
|
||||||
interrupt,
|
cpu_controller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn draw(&mut self) {
|
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
|
// the rest of this function is arcane wizardry
|
||||||
// based on the specifics of george's weird
|
// based on the specifics of george's weird
|
||||||
// display and characters... don't fuck around w it
|
// display and characters... don't fuck around w it
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for char_row in 0..29 {
|
for char_row in 0..29 {
|
||||||
for char_col in 0..64 {
|
for char_col in 0..64 {
|
||||||
let ascii = memory.read(0x6000 + i);
|
let ascii = self.memory.read(0x6000 + i);
|
||||||
i += 1;
|
i += 1;
|
||||||
for row in 0..13 {
|
for row in 0..13 {
|
||||||
let byte = self.char_rom[ascii as usize + (row * 0x100)];
|
let byte = self.char_rom[ascii as usize + (row * 0x100)];
|
||||||
for i in (0..8).rev() {
|
for bit_index in (0..8).rev() {
|
||||||
let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i);
|
let buffer_index =
|
||||||
if (byte << i) & 0x80 == 0x80 {
|
((char_row) * 13 + (row)) * 512 + (char_col * 8 + bit_index);
|
||||||
|
if (byte << bit_index) & 0x80 == 0x80 {
|
||||||
self.buffer[buffer_index] = FG_COLOR;
|
self.buffer[buffer_index] = FG_COLOR;
|
||||||
} else {
|
} else {
|
||||||
self.buffer[buffer_index] = BG_COLOR;
|
self.buffer[buffer_index] = BG_COLOR;
|
||||||
|
@ -92,10 +91,9 @@ impl Crtc {
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let _ = self.interrupt.send(false);
|
let _ = self.cpu_controller.irq();
|
||||||
sleep(Duration::from_millis(16));
|
sleep(Duration::from_millis(16));
|
||||||
self.draw();
|
self.draw();
|
||||||
let _ = self.interrupt.send(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue