From fb4fae430b49cfab3151f80b72796245aa679e69 Mon Sep 17 00:00:00 2001 From: august kline Date: Thu, 4 Jul 2024 13:04:13 -0400 Subject: [PATCH] cleaned up instructions, using termion now but is kinda broken, might switch back to crossterm --- Cargo.lock | 233 +-- Cargo.toml | 2 +- run.sh | 2 +- src/cpu.rs | 215 ++- src/instructions.rs | 3679 ++++++++++++++++++++++++----------------- src/keyboard.rs | 581 +++++-- src/main.rs | 83 +- src/roms/keyboard.asm | 2 +- src/video.rs | 37 +- 9 files changed, 2789 insertions(+), 2045 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cd09db..264c171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,31 +69,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.5.0", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "dlib" version = "0.5.2" @@ -122,7 +97,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -226,9 +201,9 @@ version = "0.1.0" dependencies = [ "anyhow", "bdf", - "crossterm", "minifb", "serde", + "termion", "toml", ] @@ -288,7 +263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -299,7 +274,7 @@ checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", + "redox_syscall", ] [[package]] @@ -308,16 +283,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -365,18 +330,6 @@ dependencies = [ "x11-dl", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "nix" version = "0.24.3" @@ -389,6 +342,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + [[package]] name = "once_cell" version = "1.19.0" @@ -407,29 +366,6 @@ dependencies = [ "sdl2-sys", ] -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.2", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -482,13 +418,10 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.2" +name = "redox_termios" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" -dependencies = [ - "bitflags 2.5.0", -] +checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" [[package]] name = "rustix" @@ -500,7 +433,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -509,12 +442,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sdl2" version = "0.35.2" @@ -567,36 +494,6 @@ dependencies = [ "serde", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.9" @@ -632,7 +529,19 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", +] + +[[package]] +name = "termion" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ccce68e518d1173e80876edd54760b60b792750d0cab6444a79101c6ea03848" +dependencies = [ + "libc", + "libredox", + "numtoa", + "redox_termios", ] [[package]] @@ -701,12 +610,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -878,37 +781,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -917,46 +796,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -969,48 +830,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 44993c5..ece112d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" [dependencies] anyhow = "1.0.81" bdf = "0.6.0" -crossterm = "0.27.0" minifb = "0.27.0" # minifb = "0.25.0" # ratatui = "0.26.3" serde = { version = "1.0.197", features = ["serde_derive", "derive"] } +termion = "4.0.2" toml = "0.8.12" diff --git a/run.sh b/run.sh index 89193f6..56d858c 100755 --- a/run.sh +++ b/run.sh @@ -8,5 +8,5 @@ fi set -e vasm6502_oldstyle ./src/roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/$1.rom; -cargo run rom "./src/roms/$1.rom"; +cargo run -- rom "./src/roms/$1.rom"; # hexdump -C ./cpu_dump.bin; diff --git a/src/cpu.rs b/src/cpu.rs index e5cf6f9..26f2b6a 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,9 +1,8 @@ use crate::instructions::{get_instruction, Instruction}; use crate::memory::{MemHandle, MemoryReader, MemoryWriter}; -use crate::types::{Byte, Word}; use std::fmt::Display; use std::sync::mpsc::{channel, Receiver, Sender}; -use std::thread::{sleep, LocalKey}; +use std::thread::sleep; use std::time::Duration; use anyhow::Result; @@ -58,30 +57,18 @@ impl CpuReceiver { } } -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, -} - -// #[derive(Clone)] pub struct Cpu { - 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 a: u8, // Accumulator Register + pub x: u8, // X Register + pub y: u8, // Y Register + pub pc: u16, // Program Counter + pub s: u8, // Stack Pointer + pub p: u8, // Status Register pub irq: bool, pub nmi: bool, pub memory: MemHandle, pub pending_cycles: usize, - receiver: CpuReceiver, + receiver: Option, stopped: bool, // cycle_count: usize, // state_tx: Sender, @@ -94,18 +81,18 @@ impl Display for Cpu { } impl MemoryReader for Cpu { - fn read(&self, address: Word) -> Byte { + fn read(&self, address: u16) -> u8 { self.memory.read(address) } } impl MemoryWriter for Cpu { - fn write(&self, address: Word, data: Byte) { + fn write(&self, address: u16, data: u8) { self.memory.write(address, data); } } impl Cpu { - pub fn new(memory: MemHandle, receiver: CpuReceiver) -> Self { + pub fn new(memory: MemHandle) -> Self { // pub fn new(memory: MemHandle) -> Self { Cpu { a: 0x00, @@ -116,7 +103,7 @@ impl Cpu { p: 0b0010_0100, irq: false, nmi: false, - receiver, + receiver: None, memory, stopped: false, pending_cycles: 0, @@ -124,78 +111,78 @@ impl Cpu { // state_tx, } } + pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self { + self.receiver = Some(receiver); + self + } pub fn new_with_control(memory: MemHandle) -> (Self, CpuController) { let (tx, rx) = channel::(); let controller = CpuController(tx); let receiver = CpuReceiver(rx); - let cpu = Cpu::new(memory, receiver); + let cpu = Cpu::new(memory).with_receiver(receiver); (cpu, controller) } - pub fn reset(&mut self) -> Result<()> { - let reset_vector_pointer = self.read_word(0xFFFC)?; + pub fn reset(&mut self) { + let reset_vector_pointer = self.read_word(0xFFFC); self.pc = reset_vector_pointer; self.pending_cycles = 0; - Ok(()) } - pub fn read_word(&self, address: Word) -> Result { + pub fn read_word(&self, address: u16) -> u16 { let low_byte = self.read(address); - let high_byte = self.read(address + 0x1); - Ok((high_byte as u16) << 8 | (low_byte as u16)) + let high_byte = self.read(address.wrapping_add(0x1)); + (high_byte as u16) << 8 | (low_byte as u16) } - fn stack_addr(&self) -> Word { + fn stack_addr(&self) -> u16 { // Dunno if this is necessary, i just don't like adding the 0x0100 every time 0x0100 + self.s as u16 } - pub fn push_stack(&mut self, data: Byte) -> Result<()> { + pub fn push_stack(&mut self, data: u8) { self.s = self.s.wrapping_sub(0x1); self.write(self.stack_addr(), data); - Ok(()) } - pub fn push_stack_word(&mut self, address: Word) -> Result<()> { + pub fn push_stack_word(&mut self, address: u16) { self.s = self.s.wrapping_sub(0x1); 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 - Ok(()) } - pub fn pop_stack(&mut self) -> Result { + pub fn pop_stack(&mut self) -> u8 { let byte = self.read(self.stack_addr()); self.s = self.s.wrapping_add(0x1); - Ok(byte) + byte } - pub fn pop_stack_word(&mut self) -> Result { - let low_byte = self.pop_stack()?; - let high_byte = self.pop_stack()?; - let word = ((high_byte as Word) << 8) + low_byte as u16; - Ok(word) + pub fn pop_stack_word(&mut self) -> u16 { + let low_byte = self.pop_stack(); + let high_byte = self.pop_stack(); + ((high_byte as u16) << 8) + low_byte as u16 } pub fn set_flag(&mut self, flag: StatusFlag, value: bool) { - self.p &= !(flag as Byte); + self.p &= !(flag as u8); if value { - self.p |= flag as Byte + self.p |= flag as u8 } } pub fn get_flag(&self, flag: StatusFlag) -> bool { - (self.p & flag as Byte) > 0 + (self.p & flag as u8) > 0 } - pub fn is_negative(&self, value: Byte) -> bool { + pub fn is_negative(&self, value: u8) -> bool { value & 0b1000_0000 == 0b1000_0000 } - pub fn execute(&mut self) { - self.cycle(); - while self.pending_cycles != 0 { - self.cycle(); - } - } + // pub fn execute(&mut self) { + // self.cycle(); + // while self.pending_cycles != 0 { + // self.cycle(); + // } + // } pub fn wait_for_interrupt(&self) { unimplemented!() @@ -203,36 +190,41 @@ impl Cpu { pub fn interrupt(&mut self) { self.irq = false; - self.push_stack_word(self.pc).unwrap(); - self.push_stack(self.p).unwrap(); + self.push_stack_word(self.pc); + self.push_stack(self.p); self.set_flag(StatusFlag::IrqDisable, true); - self.pc = self.read_word(0xFFFE).unwrap(); + self.pc = self.read_word(0xFFFE); } fn receive_control(&mut self) { - let control = match self.receiver.0.try_recv() { - Ok(control) => control, - Err(_error) => return, // most of the time we won't have any impending control - // messages, just return if that's the case - }; - 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(), + match &self.receiver { + Some(receiver) => { + let control = match receiver.0.try_recv() { + Ok(control) => control, + Err(_error) => return, // most of the time we won't have any impending control + // messages, just return if that's the case + }; + 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(), + } + } + None => {} } } @@ -251,37 +243,38 @@ impl Cpu { } let opcode = self.read(self.pc); let instruction = get_instruction(opcode); - match instruction { - Instruction::Valid(valid_instruction) => { - // 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, nmi = self.nmi); - // println!( - // "Instruction: {:?}, {:#04x}", - // valid_instruction.opcode, opcode - // ); - // println!(""); - self.pc += 1; - match valid_instruction.opcode.call(self) { - Ok(_) => { - self.pending_cycles += valid_instruction.cycles as usize; - } - Err(_) => { - println!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", self.pc, valid_instruction); - } - }; - } - Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode { - 0x02 => { - // 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); - &self.memory.dump(); - } - _ => { - println!( - "An invalid instruction with opcode {:#04x} was called at address {:#06x}", - invalid_instruction.opcode, self.pc - ); - } - }, - } + instruction.call(self); + // match instruction { + // Instruction::Valid(valid_instruction) => { + // // 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, nmi = self.nmi); + // // println!( + // // "Instruction: {:?}, {:#04x}", + // // valid_instruction.opcode, opcode + // // ); + // // println!(""); + // self.pc += 1; + // match valid_instruction.opcode.call(self) { + // Ok(_) => { + // self.pending_cycles += valid_instruction.cycles as usize; + // } + // Err(_) => { + // println!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", self.pc, valid_instruction); + // } + // }; + // } + // Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode { + // 0x02 => { + // // 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); + // let _ = &self.memory.dump(); + // } + // _ => { + // println!( + // "An invalid instruction with opcode {:#04x} was called at address {:#06x}", + // invalid_instruction.opcode, self.pc + // ); + // } + // }, + // } // self.cycle_count += 1; } pub fn stop(&mut self) { diff --git a/src/instructions.rs b/src/instructions.rs index a51fe42..30b7f6c 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -1,1548 +1,2169 @@ #![allow(clippy::upper_case_acronyms)] +use core::panic; + use crate::cpu::{Cpu, StatusFlag}; use crate::memory::{MemoryReader, MemoryWriter}; -use anyhow::{bail, Result}; +type InstrFn = fn(&mut Cpu, Option); +type AddressFn = fn(&mut Cpu) -> u16; #[derive(Clone, Copy, Debug)] -pub enum Instruction { - Valid(ValidInstruction), - Invalid(InvalidInstruction), +pub struct Instruction { + instr_fn: Option, + address_fn: Option, + cycles: u8, } -#[derive(Clone, Copy, Debug)] -pub struct ValidInstruction { - pub opcode: Opcode, - pub cycles: u8, +impl Instruction { + pub fn call(&self, cpu: &mut Cpu) { + cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte + match self.instr_fn { + // existence of instr_fn means this is a valid instruction + Some(instr_fn) => { + cpu.pending_cycles += self.cycles as usize; + match self.address_fn { + // if we have address_fn, that means + // addressing mode isn't implied, + // so we need to get an address + Some(address_fn) => { + let address = address_fn(cpu); + instr_fn(cpu, Some(address)); + } + None => instr_fn(cpu, None), // None for address_fn implies it's implied (lol) + } + } + None => { + panic!( + "An invalid instruction was called at {:04x}, with opcode {:02x}", + cpu.pc, + cpu.read(cpu.pc) + ) + } // idk if we should panic here, if we don't it's undefined behavior, which + // might actually be what we want in the cases where it's an undocumented + // 6502 instruction, but otherwise we should panic prolly, for now i'm just + // gonna panic + } + } } -#[derive(Clone, Copy, Debug)] -pub struct InvalidInstruction { - pub opcode: u8, +fn accumulator(cpu: &mut Cpu) -> u16 { + let byte = cpu.a; + byte as u16 } -#[derive(Clone, Copy, Debug)] -pub enum AddressingMode { - AbsoluteA, // a - AbsoluteIndexedWithX, // a, x - AbsoluteIndexedWithY, // a, y - AbsoluteIndirect, // (a) - AbsoluteIndexedIndirect, // (a,x) (4), only used with the JMP instruction - Accumulator, // A - Immediate, // # - Implied, // i - ProgramCounterRelative, // r - ProgramCounterRelativeTest, // r, but for instructions like BBR0-7, where the first operand - // is a zero-page address, and the second is the relative branch - // address - Stack, // s - ZeroPage, // zp - ZeroPageIndexedWithX, // zp, x - ZeroPageIndexedWithY, // zp, y - ZeroPageIndirect, // (zp) (4) - ZeroPageIndexedIndirect, // (zp, x) - ZeroPageIndirectIndexedWithY, // (zp), y +fn immediate(cpu: &mut Cpu) -> u16 { + // # + let address: u16 = cpu.pc; + cpu.pc = cpu.pc.wrapping_add(1); + address +} + +fn absolute_a(cpu: &mut Cpu) -> u16 { + // a + let address = cpu.read_word(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(2); + address +} + +fn zero_page(cpu: &mut Cpu) -> u16 { + // zp + let address = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + address as u16 +} + +fn absolute_indexed_with_x( + // a, y + cpu: &mut Cpu, +) -> u16 { + let word = cpu.read_word(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(2); + let address: u16 = word + cpu.x as u16; + address +} + +fn absolute_indexed_with_y( + // a, y + cpu: &mut Cpu, +) -> u16 { + let word = cpu.read_word(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(2); + let address: u16 = word + cpu.y as u16; + address +} + +fn zero_page_indexed_with_x( + // zp, x + cpu: &mut Cpu, +) -> u16 { + let byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + let address: u16 = (byte.wrapping_add(cpu.x)) as u16; + address +} + +fn zero_page_indexed_with_y( + // zp, y + cpu: &mut Cpu, +) -> u16 { + let byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + let address: u16 = (byte + cpu.y) as u16; + address +} + +fn absolute_indirect( + // (a) + cpu: &mut Cpu, +) -> u16 { + let word = cpu.read_word(cpu.pc); + let address = cpu.read_word(word); + cpu.pc = cpu.pc.wrapping_add(2); + address +} + +fn absolute_indexed_indirect( + // (a, x), only used with the JMP instruction + cpu: &mut Cpu, +) -> u16 { + let word = cpu.read_word(cpu.pc); + let address = cpu.read_word(word + cpu.x as u16); + cpu.pc = cpu.pc.wrapping_add(2); + address +} + +fn zero_page_indirect( + // (zp) + cpu: &mut Cpu, +) -> u16 { + let byte: u8 = cpu.read(cpu.pc); + let address: u16 = cpu.read_word(byte as u16); + cpu.pc = cpu.pc.wrapping_add(1); + address +} + +fn zero_page_indexed_indirect( + // (zp, x) + cpu: &mut Cpu, +) -> u16 { + let byte = cpu.read(cpu.pc); + let address = cpu.read_word((byte.wrapping_add(cpu.x)) as u16); // Anytime you see something like `byte as u16`, it's using the byte as a zero-page address + cpu.pc = cpu.pc.wrapping_add(1); + address +} + +fn zero_page_indirect_indexed_with_y( + // (zp), y + cpu: &mut Cpu, +) -> u16 { + let byte: u8 = cpu.read(cpu.pc); + let address: u16 = cpu.read_word(byte.wrapping_add(cpu.y) as u16); + cpu.pc = cpu.pc.wrapping_add(1); + address +} + +fn signed_byte_to_word(value: u8) -> u16 { + let mut value = u16::from(value); + if value & 0x80 > 0 { + value |= 0xff00; + } + value +} + +fn relative( + // return the address to jump to if branch condition met, in instr_fn + // we can set the program counter based on the condition + // r + cpu: &mut Cpu, +) -> u16 { + let byte = cpu.read(cpu.pc); + signed_byte_to_word(byte).wrapping_add(cpu.pc) +} + +fn relative_test( + // used for bbs/bbr instructions, where the dest + // address is read one byte after the value to test + // r + cpu: &mut Cpu, +) -> u16 { + let byte = cpu.read(cpu.pc + 1); + signed_byte_to_word(byte).wrapping_add(cpu.pc) +} + +fn branch(cpu: &mut Cpu, condition: bool, address: u16) { + if condition { + // coundition met? + if address & 0xff00 != cpu.pc & 0xff00 { + // page boundary? + cpu.pending_cycles += 2; + } else { + cpu.pending_cycles += 1; + } + cpu.pc = address; + } + cpu.pc = cpu.pc.wrapping_add(1); +} + +fn adc(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let carry = cpu.get_flag(StatusFlag::Carry); + let result = cpu.a as u16 + byte as u16 + carry as u16; + cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); + cpu.set_flag(StatusFlag::Zero, result as u8 == 0); + cpu.set_flag( + StatusFlag::Overflow, + (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, + // Truly have no idea what's happening here, + // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html + ); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); + cpu.a = result as u8; +} + +fn and(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.a &= cpu.read(address); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); +} + +fn as_left(cpu: &mut Cpu, value: u8) -> u8 { + cpu.set_flag(StatusFlag::Carry, value & 0b1000_0000 == 0b1000_0000); + let shifted_value = value << 1; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value +} + +fn asl(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let value = cpu.read(address); + let result = as_left(cpu, value); + cpu.write(address, result); +} + +fn asl_a(cpu: &mut Cpu, _address: Option) { + cpu.a = as_left(cpu, cpu.a); +} + +fn bbr0(cpu: &mut Cpu, address: Option) { + // address is address to branch to if condition met + // program counter is on value to test + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, address); +} + +fn bbr1(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, address); +} + +fn bbr2(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, address); +} +fn bbr3(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, address); +} + +fn bbr4(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, address); +} + +fn bbr5(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, address); +} + +fn bbr6(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, address); +} + +fn bbr7(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, address); +} + +fn bbs0(cpu: &mut Cpu, address: Option) { + // address is address to branch to if condition met + // program counter is on value to test + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, address); +} + +fn bbs1(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, address); +} + +fn bbs2(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, address); +} +fn bbs3(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, address); +} + +fn bbs4(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, address); +} + +fn bbs5(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, address); +} + +fn bbs6(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, address); +} + +fn bbs7(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let test_byte = cpu.read(cpu.pc); + cpu.pc = cpu.pc.wrapping_add(1); + branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, address); +} + +fn bcc(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, !cpu.get_flag(StatusFlag::Carry), address); +} + +fn bcs(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, cpu.get_flag(StatusFlag::Carry), address); +} + +fn beq(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, cpu.get_flag(StatusFlag::Zero), address); +} +fn bit(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let result = cpu.a & cpu.read(address); + cpu.set_flag(StatusFlag::Zero, result == 0); + cpu.set_flag( + StatusFlag::Overflow, + result & StatusFlag::Overflow as u8 == StatusFlag::Overflow as u8, + ); + cpu.set_flag( + StatusFlag::Negative, + result & StatusFlag::Negative as u8 == StatusFlag::Negative as u8, + ); +} + +fn bmi(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, cpu.get_flag(StatusFlag::Negative), address); +} + +fn bne(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, !cpu.get_flag(StatusFlag::Zero), address); +} + +fn bpl(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, !cpu.get_flag(StatusFlag::Negative), address); +} + +fn bra(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, true, address); +} + +fn brk(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Brk, true); +} + +fn bvc(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, !cpu.get_flag(StatusFlag::Overflow), address); +} + +fn bvs(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + branch(cpu, cpu.get_flag(StatusFlag::Overflow), address); +} + +fn clc(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Carry, false); +} + +fn cld(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Decimal, false); +} +fn cli(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::IrqDisable, false); +} + +fn clv(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Overflow, false); +} + +fn cmp(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.set_flag(StatusFlag::Carry, cpu.a >= byte); + cpu.set_flag(StatusFlag::Zero, cpu.a == byte); + cpu.set_flag(StatusFlag::Negative, cpu.a <= byte); +} + +fn cpx(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + 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)); +} + +fn cpy(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); + cpu.set_flag(StatusFlag::Zero, cpu.y == byte); + cpu.set_flag(StatusFlag::Negative, cpu.y <= byte); +} + +fn dec(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let dec_byte = byte.wrapping_sub(1); + cpu.write(address, dec_byte); + cpu.set_flag(StatusFlag::Zero, dec_byte == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte)); +} + +fn dec_a(cpu: &mut Cpu, _address: Option) { + cpu.a = cpu.a.wrapping_sub(1); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); +} + +fn dex(cpu: &mut Cpu, _address: Option) { + cpu.x = cpu.x.wrapping_sub(1); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); +} + +fn dey(cpu: &mut Cpu, _address: Option) { + cpu.y = cpu.y.wrapping_sub(1); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); +} + +fn eor(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.a ^= cpu.read(address); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); +} + +fn inc(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let inc_byte = byte.wrapping_add(1); + cpu.set_flag(StatusFlag::Zero, inc_byte == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(inc_byte)); + cpu.write(address, inc_byte); +} + +fn inc_a(cpu: &mut Cpu, _address: Option) { + cpu.a = cpu.a.wrapping_add(1); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); +} + +fn inx(cpu: &mut Cpu, _address: Option) { + cpu.x = cpu.x.wrapping_add(1); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); +} +fn iny(cpu: &mut Cpu, _address: Option) { + cpu.y = cpu.y.wrapping_add(1); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); +} + +fn jmp(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.pc = address; +} + +fn jsr(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let return_address = cpu.pc - 1; + cpu.push_stack_word(return_address); + cpu.pc = address; +} + +fn lda(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.a = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); +} + +fn ldx(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.x = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); +} + +fn ldy(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.y = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); +} + +fn ls_right(cpu: &mut Cpu, value: u8) -> u8 { + cpu.set_flag(StatusFlag::Carry, value & 1 == 1); + let shifted_value = value >> 1; + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(value)); + shifted_value +} + +fn lsr(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let value = cpu.read(address); + let result = ls_right(cpu, value); + cpu.write(address, result); +} + +fn lsr_a(cpu: &mut Cpu, _address: Option) { + cpu.a = ls_right(cpu, cpu.a); +} + +fn nop(_cpu: &mut Cpu, _address: Option) { + // ʕ·ᴥ·ʔ- ♥ george loves u ♥ +} + +fn ora(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.a |= cpu.read(address); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); +} +fn pha(cpu: &mut Cpu, _address: Option) { + cpu.push_stack(cpu.a); +} +fn php(cpu: &mut Cpu, _address: Option) { + cpu.push_stack(cpu.p); +} +fn phx(cpu: &mut Cpu, _address: Option) { + cpu.push_stack(cpu.x); +} +fn phy(cpu: &mut Cpu, _address: Option) { + cpu.push_stack(cpu.y); +} +fn pla(cpu: &mut Cpu, _address: Option) { + cpu.a = cpu.pop_stack(); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)) +} +fn plp(cpu: &mut Cpu, _address: Option) { + cpu.p = cpu.pop_stack(); +} +fn plx(cpu: &mut Cpu, _address: Option) { + cpu.x = cpu.pop_stack(); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)) +} +fn ply(cpu: &mut Cpu, _address: Option) { + cpu.y = cpu.pop_stack(); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)) +} +fn rmb0(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1111_1110; + cpu.write(address, reset_byte); +} + +fn rmb1(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1111_1101; + cpu.write(address, reset_byte); +} + +fn rmb2(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1111_1011; + cpu.write(address, reset_byte); +} + +fn rmb3(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1111_0111; + cpu.write(address, reset_byte); +} + +fn rmb4(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1110_1111; + cpu.write(address, reset_byte); +} + +fn rmb5(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1101_1111; + cpu.write(address, reset_byte); +} + +fn rmb6(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b1011_1111; + cpu.write(address, reset_byte); +} + +fn rmb7(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b0111_1111; + cpu.write(address, reset_byte); +} + +fn rot_left(cpu: &mut Cpu, value: u8) -> u8 { + let carry = cpu.get_flag(StatusFlag::Carry) as u8; + cpu.set_flag(StatusFlag::Carry, value >> 7 == 1); + let shifted_value = (value << 1) + carry; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value +} + +fn rol(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let value = cpu.read(address); + let result = rot_left(cpu, value); + cpu.write(address, result); +} + +fn rol_a(cpu: &mut Cpu, _address: Option) { + cpu.a = rot_left(cpu, cpu.a); +} + +fn rot_right(cpu: &mut Cpu, value: u8) -> u8 { + let carry = cpu.get_flag(StatusFlag::Carry) as u8; + cpu.set_flag(StatusFlag::Carry, value & 1 == 1); + let shifted_value = (value >> 1) + (carry << 7); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value +} + +fn ror(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let value = cpu.read(address); + let result = rot_right(cpu, value); + cpu.write(address, result); +} + +fn ror_a(cpu: &mut Cpu, _address: Option) { + cpu.a = rot_right(cpu, cpu.a); +} + +fn rti(cpu: &mut Cpu, _address: Option) { + cpu.p = cpu.pop_stack(); + cpu.pc = cpu.pop_stack_word(); +} + +fn rts(cpu: &mut Cpu, _address: Option) { + let return_address = cpu.pop_stack_word(); + cpu.pc = return_address + 3; // Go back to where we jsr'ed, skipping the operand +} + +fn sbc(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let carry = cpu.get_flag(StatusFlag::Carry); + let result = cpu.a as u16 + byte as u16 + !carry as u16; + cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); + cpu.set_flag(StatusFlag::Zero, result as u8 == 0); + cpu.set_flag( + StatusFlag::Overflow, + (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, + // Truly have no idea what's happening here, + // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html + ); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); +} + +fn sec(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Carry, true); +} + +fn sed(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Decimal, true); +} + +fn sei(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::IrqDisable, true); +} + +fn smb0(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0000_0001; + cpu.write(address, reset_byte); +} + +fn smb1(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0000_0010; + cpu.write(address, reset_byte); +} +fn smb2(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0000_0100; + cpu.write(address, reset_byte); +} +fn smb3(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0000_1000; + cpu.write(address, reset_byte); +} +fn smb4(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0001_0000; + cpu.write(address, reset_byte); +} +fn smb5(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b0010_0000; + cpu.write(address, reset_byte); +} +fn smb6(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte & 0b0100_0000; + cpu.write(address, reset_byte); +} +fn smb7(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + let reset_byte = byte | 0b1000_0000; + cpu.write(address, reset_byte); +} +fn sta(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.write(address, cpu.a); +} +fn stp(cpu: &mut Cpu, _address: Option) { + cpu.stop(); +} + +fn stx(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.write(address, cpu.x); +} + +fn sty(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.write(address, cpu.y); +} + +fn stz(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + cpu.write(address, 0); +} + +fn tax(cpu: &mut Cpu, _address: Option) { + cpu.x = cpu.a; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); +} + +fn tay(cpu: &mut Cpu, _address: Option) { + cpu.y = cpu.a; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); +} + +fn trb(cpu: &mut Cpu, address: Option) { + // Still not really sure when you would + // use TRB or TSB, but this was helpful + // (Garth rocks) http://forum.6502.org/viewtopic.php?t=48 + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.write(address, !cpu.a & byte); + cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); +} + +fn tsb(cpu: &mut Cpu, address: Option) { + let address = address.unwrap(); + let byte = cpu.read(address); + cpu.write(address, cpu.a | byte); + cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); +} + +fn tsx(cpu: &mut Cpu, _address: Option) { + cpu.x = cpu.s; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); +} + +fn txa(cpu: &mut Cpu, _address: Option) { + cpu.a = cpu.x; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); +} + +fn txs(cpu: &mut Cpu, _address: Option) { + cpu.s = cpu.x; +} + +fn tya(cpu: &mut Cpu, _address: Option) { + cpu.a = cpu.y; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); +} + +fn wai(cpu: &mut Cpu, _address: Option) { + cpu.set_flag(StatusFlag::Brk, true); + cpu.wait_for_interrupt(); } // if you want to optimize this later https://llx.com/Neil/a2/opcodes.html pub fn get_instruction(opcode: u8) -> Instruction { - let opcodes = include!("./opcodes.in"); - opcodes[opcode as usize] + OPCODES[opcode as usize] } -#[derive(Clone, Copy, Debug)] -pub enum Opcode { - ADC(AddressingMode), - AND(AddressingMode), - ASL(AddressingMode), - BBR0(AddressingMode), - BBR1(AddressingMode), - BBR2(AddressingMode), - BBR3(AddressingMode), - BBR4(AddressingMode), - BBR5(AddressingMode), - BBR6(AddressingMode), - BBR7(AddressingMode), - BBS0(AddressingMode), - BBS1(AddressingMode), - BBS2(AddressingMode), - BBS3(AddressingMode), - BBS4(AddressingMode), - BBS5(AddressingMode), - BBS6(AddressingMode), - BBS7(AddressingMode), - BCC(AddressingMode), - BCS(AddressingMode), - BEQ(AddressingMode), - BIT(AddressingMode), - BMI(AddressingMode), - BNE(AddressingMode), - BPL(AddressingMode), - BRA(AddressingMode), - BRK(AddressingMode), - BVC(AddressingMode), - BVS(AddressingMode), - CLC(AddressingMode), - CLD(AddressingMode), - CLI(AddressingMode), - CLV(AddressingMode), - CMP(AddressingMode), - CPX(AddressingMode), - CPY(AddressingMode), - DEC(AddressingMode), - DEX(AddressingMode), - DEY(AddressingMode), - EOR(AddressingMode), - INC(AddressingMode), - INX(AddressingMode), - INY(AddressingMode), - JMP(AddressingMode), - JSR(AddressingMode), - LDA(AddressingMode), - LDX(AddressingMode), - LDY(AddressingMode), - LSR(AddressingMode), - NOP(AddressingMode), - ORA(AddressingMode), - PHA(AddressingMode), - PHP(AddressingMode), - PHX(AddressingMode), - PHY(AddressingMode), - PLA(AddressingMode), - PLP(AddressingMode), - PLX(AddressingMode), - PLY(AddressingMode), - RMB0(AddressingMode), - RMB1(AddressingMode), - RMB2(AddressingMode), - RMB3(AddressingMode), - RMB4(AddressingMode), - RMB5(AddressingMode), - RMB6(AddressingMode), - RMB7(AddressingMode), - ROL(AddressingMode), - ROR(AddressingMode), - RTI(AddressingMode), - RTS(AddressingMode), - SBC(AddressingMode), - SEC(AddressingMode), - SED(AddressingMode), - SEI(AddressingMode), - SMB0(AddressingMode), - SMB1(AddressingMode), - SMB2(AddressingMode), - SMB3(AddressingMode), - SMB4(AddressingMode), - SMB5(AddressingMode), - SMB6(AddressingMode), - SMB7(AddressingMode), - STA(AddressingMode), - STP(AddressingMode), - STX(AddressingMode), - STY(AddressingMode), - STZ(AddressingMode), - TAX(AddressingMode), - TAY(AddressingMode), - TRB(AddressingMode), - TSB(AddressingMode), - TSX(AddressingMode), - TXA(AddressingMode), - TXS(AddressingMode), - TYA(AddressingMode), - WAI(AddressingMode), -} - -#[derive(Clone, Copy)] -enum AddressingModeValue { - Implied, // Used when an instruction doesn't need an operation, e.g. RTS - Relative(u8), // Used by branch instructions to offset the program counter - RelativeTest(u8, u8), // Used by branch instructions to offset the program counter - Absolute(u16), // Just a plain ol' address - Accumulator(u8), // This isn't technically accurate, since the datasheet says this would be - // Implied, but we do use the value of the accumulator as the operand so this - // is how i'm gonna do it -} - -impl TryFrom for u16 { - type Error = anyhow::Error; - fn try_from(value: AddressingModeValue) -> Result { - match value { - AddressingModeValue::Implied => bail!( - "An addressing mode value without an address was attempted to turn into a u16" - ), - AddressingModeValue::RelativeTest(_zero_page, offset) => Ok(offset as u16), - AddressingModeValue::Relative(inner_value) => Ok(inner_value as u16), - AddressingModeValue::Absolute(inner_value) => Ok(inner_value), - AddressingModeValue::Accumulator(inner_value) => Ok(inner_value as u16), - } - } -} - -fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result { - fn accumulator(cpu: &mut Cpu) -> AddressingModeValue { - let byte = cpu.a; - AddressingModeValue::Accumulator(byte) - } - - fn implied(_cpu: &mut Cpu) -> AddressingModeValue { - // i - AddressingModeValue::Implied - } - - fn immediate(cpu: &mut Cpu) -> AddressingModeValue { - // # - let address: u16 = cpu.pc; - cpu.pc = cpu.pc.wrapping_add(1); - AddressingModeValue::Absolute(address) - } - - fn absolute_a(cpu: &mut Cpu) -> Result { - // a - let address = cpu.read_word(cpu.pc)?; - cpu.pc = cpu.pc.wrapping_add(2); - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page(cpu: &mut Cpu) -> Result { - // zp - let address = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::Absolute(address as u16)) - } - - fn absolute_indexed_with_x( - // a, y - cpu: &mut Cpu, - ) -> Result { - let word = cpu.read_word(cpu.pc)?; - cpu.pc = cpu.pc.wrapping_add(2); - let address: u16 = word + cpu.x as u16; - Ok(AddressingModeValue::Absolute(address)) - } - - fn absolute_indexed_with_y( - // a, y - cpu: &mut Cpu, - ) -> Result { - let word = cpu.read_word(cpu.pc)?; - cpu.pc = cpu.pc.wrapping_add(2); - let address: u16 = word + cpu.y as u16; - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page_indexed_with_x( - // zp, x - cpu: &mut Cpu, - ) -> Result { - let byte = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - let address: u16 = (byte.wrapping_add(cpu.x)) as u16; - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page_indexed_with_y( - // zp, y - cpu: &mut Cpu, - ) -> Result { - let byte = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - let address: u16 = (byte + cpu.y) as u16; - Ok(AddressingModeValue::Absolute(address)) - } - - fn absolute_indirect( - // (a) - cpu: &mut Cpu, - ) -> Result { - let word = cpu.read_word(cpu.pc)?; - let address = cpu.read_word(word)?; - cpu.pc = cpu.pc.wrapping_add(2); - Ok(AddressingModeValue::Absolute(address)) - } - - fn absolute_indexed_indirect( - // (a, x), only used with the JMP instruction - cpu: &mut Cpu, - ) -> Result { - let word = cpu.read_word(cpu.pc)?; - let address = cpu.read_word(word + cpu.x as u16)?; - cpu.pc = cpu.pc.wrapping_add(2); - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page_indirect( - // (zp) - cpu: &mut Cpu, - ) -> Result { - let byte: u8 = cpu.read(cpu.pc); - let address: u16 = cpu.read_word(byte as u16)?; - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page_indexed_indirect( - // (zp, x) - cpu: &mut Cpu, - ) -> Result { - let byte = cpu.read(cpu.pc); - let address = cpu.read_word((byte.wrapping_add(cpu.x)) as u16)?; // Anytime you see something like `byte as u16`, it's using the byte as a zero-page address - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::Absolute(address)) - } - - fn zero_page_indirect_indexed_with_y( - // (zp), y - cpu: &mut Cpu, - ) -> Result { - let byte: u8 = cpu.read(cpu.pc); - let address: u16 = cpu.read_word(byte.wrapping_add(cpu.y) as u16)?; - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::Absolute(address)) - } - - fn relative( - // r - cpu: &mut Cpu, - ) -> Result { - let byte: u8 = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::Relative(byte)) - } - - fn relative_test( - // r - cpu: &mut Cpu, - ) -> Result { - let byte: u8 = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - let address = cpu.read(cpu.pc); - cpu.pc = cpu.pc.wrapping_add(1); - Ok(AddressingModeValue::RelativeTest(byte, address)) - } - - match mode { - AddressingMode::AbsoluteA => absolute_a(cpu), - AddressingMode::AbsoluteIndexedWithX => absolute_indexed_with_x(cpu), - AddressingMode::AbsoluteIndexedWithY => absolute_indexed_with_y(cpu), - AddressingMode::AbsoluteIndirect => absolute_indirect(cpu), - AddressingMode::AbsoluteIndexedIndirect => absolute_indexed_indirect(cpu), - AddressingMode::Immediate => Ok(immediate(cpu)), - AddressingMode::ProgramCounterRelative => relative(cpu), - AddressingMode::ZeroPage => zero_page(cpu), - AddressingMode::ZeroPageIndexedWithX => zero_page_indexed_with_x(cpu), - AddressingMode::ZeroPageIndexedWithY => zero_page_indexed_with_y(cpu), - AddressingMode::ZeroPageIndirect => zero_page_indirect(cpu), - AddressingMode::ZeroPageIndexedIndirect => zero_page_indexed_indirect(cpu), - AddressingMode::ZeroPageIndirectIndexedWithY => zero_page_indirect_indexed_with_y(cpu), - AddressingMode::Accumulator => Ok(accumulator(cpu)), - AddressingMode::ProgramCounterRelativeTest => relative_test(cpu), - _ => Ok(implied(cpu)), - } -} - -fn branch(cpu: &mut Cpu, condition: bool, value: AddressingModeValue) -> Result<()> { - fn signed_byte_to_word(value: u8) -> u16 { - let mut value = u16::from(value); - if value & 0x80 > 0 { - value |= 0xff00; - } - value - } - match value { - AddressingModeValue::Relative(address) => { - let address = signed_byte_to_word(address).wrapping_add(cpu.pc); - if condition { - if address & 0xff00 != cpu.pc & 0xff00 { - cpu.pending_cycles += 2; - } else { - cpu.pending_cycles += 1; - } - cpu.pc = address; - } - Ok(()) - } - AddressingModeValue::RelativeTest(_zero_page, address) => { - let address = signed_byte_to_word(address).wrapping_add(cpu.pc); - if condition { - if address & 0xff00 != cpu.pc & 0xff00 { - cpu.pending_cycles += 2; - } else { - cpu.pending_cycles += 1; - } - cpu.pc = address; - } - Ok(()) - } - _ => bail!("Tried to branch but received an invalid addressing mode value"), - } -} - -impl Opcode { - pub fn call(&self, cpu: &mut Cpu) -> Result<()> { - match self { - Opcode::ADC(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let carry = cpu.get_flag(StatusFlag::Carry); - let result = cpu.a as u16 + byte as u16 + carry as u16; - cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); - cpu.set_flag(StatusFlag::Zero, result as u8 == 0); - cpu.set_flag( - StatusFlag::Overflow, - (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, - // Truly have no idea what's happening here, - // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html - ); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); - cpu.a = result as u8; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::AND(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - 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(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::ASL(mode) => { - fn asl(cpu: &mut Cpu, value: u8) -> u8 { - cpu.set_flag(StatusFlag::Carry, value & 0b1000_0000 == 0b1000_0000); - let shifted_value = value << 1; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); - cpu.set_flag(StatusFlag::Zero, shifted_value == 0); - shifted_value - } - match mode { - AddressingMode::Accumulator => { - cpu.a = asl(cpu, cpu.a); - Ok(()) - } - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?); - let result = asl(cpu, value); - cpu.write(address.try_into()?, result); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - } - } - Opcode::BBR0(mode) => match mode { - // These instructions are weird, cause they're relative, except the byte being tested is immediate, - // so i'm not sure if what i've already written is going to model this accurately - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR1(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR2(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR3(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR4(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR5(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR6(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBR7(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS0(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS1(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS2(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS3(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS4(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS5(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS6(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BBS7(mode) => match mode { - AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { - Ok(value) => match value { - AddressingModeValue::RelativeTest(byte, _address) => { - let zero_page_address = byte as u16; - let test_byte = cpu.read(zero_page_address); - branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, value)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Err(error) => bail!("Tried to get address, but {error:#?}"), - }, - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BCC(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, !cpu.get_flag(StatusFlag::Carry), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BCS(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, cpu.get_flag(StatusFlag::Carry), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BEQ(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, cpu.get_flag(StatusFlag::Zero), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BIT(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX => { - let address = get_address(mode, cpu)?; - let result = cpu.a & cpu.read(address.try_into()?); - cpu.set_flag(StatusFlag::Zero, result == 0); - cpu.set_flag( - StatusFlag::Overflow, - result & StatusFlag::Overflow as u8 == StatusFlag::Overflow as u8, - ); - cpu.set_flag( - StatusFlag::Negative, - result & StatusFlag::Negative as u8 == StatusFlag::Negative as u8, - ); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BMI(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, cpu.get_flag(StatusFlag::Negative), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BNE(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, !cpu.get_flag(StatusFlag::Zero), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BPL(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, !cpu.get_flag(StatusFlag::Negative), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BRA(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, true, address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BRK(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Brk, true); - Ok(()) - //panic!("Interrupts unimplemented!"); - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BVC(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, !cpu.get_flag(StatusFlag::Overflow), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::BVS(mode) => match mode { - AddressingMode::ProgramCounterRelative => { - let address = get_address(mode, cpu)?; - branch(cpu, cpu.get_flag(StatusFlag::Overflow), address)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CLC(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Carry, false); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CLD(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Decimal, false); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CLI(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::IrqDisable, false); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CLV(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Overflow, false); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CMP(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CPX(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::AbsoluteA => { - let address = get_address(mode, cpu)?; - 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)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::CPY(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::AbsoluteA => { - let address = get_address(mode, cpu)?; - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::DEC(mode) => match mode { - AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let dec_byte = byte.wrapping_sub(1); - 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(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::DEX(mode) => match mode { - AddressingMode::Implied => { - cpu.x = cpu.x.wrapping_sub(1); - cpu.set_flag(StatusFlag::Zero, cpu.x == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::DEY(mode) => match mode { - AddressingMode::Implied => { - cpu.y = cpu.y.wrapping_sub(1); - cpu.set_flag(StatusFlag::Zero, cpu.y == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::EOR(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - 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(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::INC(mode) => match mode { - AddressingMode::Accumulator => { - cpu.a = cpu.a.wrapping_add(1); - cpu.set_flag(StatusFlag::Zero, cpu.a == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); - Ok(()) - } - AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX => { - let address = get_address(mode, cpu)?; - 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)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::INX(mode) => match mode { - AddressingMode::Implied => { - cpu.x = cpu.x.wrapping_add(1); - cpu.set_flag(StatusFlag::Zero, cpu.x == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::INY(mode) => match mode { - AddressingMode::Implied => { - cpu.y = cpu.y.wrapping_add(1); - cpu.set_flag(StatusFlag::Zero, cpu.y == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::JMP(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndirect - | AddressingMode::AbsoluteIndexedIndirect => { - let address = get_address(mode, cpu)?; - cpu.pc = address.try_into()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::JSR(mode) => match mode { - AddressingMode::AbsoluteA => { - let return_address = cpu.pc - 1; - cpu.push_stack_word(return_address)?; - let address = get_address(mode, cpu)?; - cpu.pc = address.try_into()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::LDA(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::LDX(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithY => { - let address = get_address(mode, cpu)?; - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::LDY(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::LSR(mode) => { - fn lsr(cpu: &mut Cpu, value: u8) -> u8 { - cpu.set_flag(StatusFlag::Carry, value & 1 == 1); - let shifted_value = value >> 1; - cpu.set_flag(StatusFlag::Zero, shifted_value == 0); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(value)); - shifted_value - } - match mode { - AddressingMode::Accumulator => { - cpu.a = lsr(cpu, cpu.a); - Ok(()) - } - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?); - let result = lsr(cpu, value); - cpu.write(address.try_into()?, result); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - } - } - Opcode::NOP(_mode) => { - cpu.pc = cpu.pc.wrapping_add(1); - Ok(()) - } - Opcode::ORA(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - 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(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PHA(mode) => match mode { - AddressingMode::Stack => { - cpu.push_stack(cpu.a)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PHP(mode) => match mode { - AddressingMode::Stack => { - cpu.push_stack(cpu.p)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PHX(mode) => match mode { - AddressingMode::Stack => { - cpu.push_stack(cpu.x)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PHY(mode) => match mode { - AddressingMode::Stack => { - cpu.push_stack(cpu.y)?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PLA(mode) => match mode { - AddressingMode::Stack => { - cpu.a = cpu.pop_stack()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PLP(mode) => match mode { - AddressingMode::Stack => { - cpu.p = cpu.pop_stack()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PLX(mode) => match mode { - AddressingMode::Stack => { - cpu.x = cpu.pop_stack()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::PLY(mode) => match mode { - AddressingMode::Stack => { - cpu.y = cpu.pop_stack()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB0(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1111_1110; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB1(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1111_1101; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB2(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1111_1011; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB3(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1111_0111; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB4(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1110_1111; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB5(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1101_1111; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB6(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b1011_1111; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RMB7(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b0111_1111; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::ROL(mode) => { - fn rol(cpu: &mut Cpu, value: u8) -> u8 { - let carry = cpu.get_flag(StatusFlag::Carry) as u8; - cpu.set_flag(StatusFlag::Carry, value >> 7 == 1); - let shifted_value = (value << 1) + carry; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); - cpu.set_flag(StatusFlag::Zero, shifted_value == 0); - shifted_value - } - match mode { - AddressingMode::Accumulator => { - cpu.a = rol(cpu, cpu.a); - Ok(()) - } - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?); - let result = rol(cpu, value); - cpu.write(address.try_into()?, result); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - } - } - Opcode::ROR(mode) => { - fn ror(cpu: &mut Cpu, value: u8) -> u8 { - let carry = cpu.get_flag(StatusFlag::Carry) as u8; - cpu.set_flag(StatusFlag::Carry, value & 1 == 1); - let shifted_value = (value >> 1) + (carry << 7); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); - cpu.set_flag(StatusFlag::Zero, shifted_value == 0); - shifted_value - } - match mode { - AddressingMode::Accumulator => { - cpu.a = ror(cpu, cpu.a); - Ok(()) - } - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - let value = cpu.read(address.try_into()?); - let result = ror(cpu, value); - cpu.write(address.try_into()?, result); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - } - } - Opcode::RTI(mode) => match mode { - AddressingMode::Implied => { - cpu.p = cpu.pop_stack()?; - cpu.pc = cpu.pop_stack_word()?; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::RTS(mode) => match mode { - AddressingMode::Stack => { - let return_address = cpu.pop_stack_word()?; - cpu.pc = return_address + 3; // Go back to where we jsr'ed, skipping the operand - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SBC(mode) => match mode { - AddressingMode::Immediate - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let carry = cpu.get_flag(StatusFlag::Carry); - let result = cpu.a as u16 + byte as u16 + !carry as u16; - cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); - cpu.set_flag(StatusFlag::Zero, result as u8 == 0); - cpu.set_flag( - StatusFlag::Overflow, - (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, - // Truly have no idea what's happening here, - // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html - ); - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SEC(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Carry, true); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SED(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::Decimal, true); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SEI(mode) => match mode { - AddressingMode::Implied => { - cpu.set_flag(StatusFlag::IrqDisable, true); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB0(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0000_0001; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB1(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0000_0010; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB2(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0000_0100; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB3(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0000_1000; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB4(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0001_0000; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB5(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b0010_0000; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB6(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte & 0b0100_0000; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::SMB7(mode) => match mode { - AddressingMode::ZeroPage => { - let address = get_address(mode, cpu)?; - let byte = cpu.read(address.try_into()?); - let reset_byte = byte | 0b1000_0000; - cpu.write(address.try_into()?, reset_byte); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::STA(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::AbsoluteIndexedWithY - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndirect - | AddressingMode::ZeroPageIndexedIndirect - | AddressingMode::ZeroPageIndexedWithX - | AddressingMode::ZeroPageIndirectIndexedWithY => { - let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.a); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::STP(mode) => match mode { - AddressingMode::Implied => { - cpu.stop(); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::STX(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithY => { - let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.x); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::STY(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.y); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::STZ(mode) => match mode { - AddressingMode::AbsoluteA - | AddressingMode::AbsoluteIndexedWithX - | AddressingMode::ZeroPage - | AddressingMode::ZeroPageIndexedWithX => { - let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TAX(mode) => match mode { - AddressingMode::Implied => { - cpu.x = cpu.a; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); - cpu.set_flag(StatusFlag::Zero, cpu.x == 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TAY(mode) => match mode { - AddressingMode::Implied => { - cpu.y = cpu.a; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); - cpu.set_flag(StatusFlag::Zero, cpu.y == 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TRB(mode) => match mode { - // Still not really sure when you would - // use TRB or TSB, but this was helpful - // (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); - cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - 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); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TSX(mode) => match mode { - AddressingMode::Implied => { - cpu.x = cpu.s; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); - cpu.set_flag(StatusFlag::Zero, cpu.x == 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TXA(mode) => match mode { - AddressingMode::Implied => { - cpu.a = cpu.x; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); - cpu.set_flag(StatusFlag::Zero, cpu.a == 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TXS(mode) => match mode { - AddressingMode::Implied => { - cpu.s = cpu.x; - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::TYA(mode) => match mode { - AddressingMode::Implied => { - cpu.a = cpu.y; - cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); - cpu.set_flag(StatusFlag::Zero, cpu.a == 0); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - Opcode::WAI(mode) => match mode { - AddressingMode::Implied => { - cpu.wait_for_interrupt(); - Ok(()) - } - _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) - }, - } - } -} +static OPCODES: [Instruction; 256] = [ + Instruction { + instr_fn: Some(brk), + address_fn: None, + cycles: 7, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(tsb), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(asl), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(rmb0), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(php), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(asl_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(tsb), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(asl), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbr0), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bpl), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(absolute_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(trb), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(asl), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(rmb1), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(clc), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(inc_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(trb), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(ora), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(asl), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbr1), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(jsr), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(bit), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(rol), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(rmb2), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(plp), + address_fn: None, + cycles: 4, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(rol_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(bit), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(rol), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbr2), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bmi), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(bit), + address_fn: Some(zero_page_indexed_with_x), + cycles: 3, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(rol), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(rmb3), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(sec), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(dec_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(bit), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(and), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(rol), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbr3), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(rti), + address_fn: None, + cycles: 6, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(lsr), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(rmb4), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(pha), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(lsr_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(jmp), + address_fn: Some(absolute_a), + cycles: 3, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(lsr), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbr4), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bvc), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(lsr), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(rmb5), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(cli), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(phy), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(eor), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(lsr), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbr5), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(rts), + address_fn: None, + cycles: 6, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(stz), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(ror), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(rmb6), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(pla), + address_fn: None, + cycles: 4, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(ror_a), + address_fn: Some(accumulator), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(jmp), + address_fn: Some(absolute_indirect), + cycles: 5, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(ror), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbr6), + address_fn: Some(relative), + cycles: 4, + }, + Instruction { + instr_fn: Some(bvs), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(stz), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(ror), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(rmb7), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(sei), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(ply), + address_fn: None, + cycles: 4, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(jmp), + address_fn: Some(absolute_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: Some(adc), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(ror), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbr7), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bra), + address_fn: Some(relative), + cycles: 3, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(sty), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(stx), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(smb0), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(dey), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(bit), + address_fn: Some(immediate), + cycles: 3, + }, + Instruction { + instr_fn: Some(txa), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(sty), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(stx), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(bbs0), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bcc), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 6, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(sty), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(stx), + address_fn: Some(zero_page_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(smb1), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(tya), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(absolute_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(txs), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(stz), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(sta), + address_fn: Some(absolute_indexed_with_x), + cycles: 5, + }, + Instruction { + instr_fn: Some(stz), + address_fn: Some(absolute_indexed_with_x), + cycles: 5, + }, + Instruction { + instr_fn: Some(bbs1), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(ldy), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: Some(ldx), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(ldy), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(ldx), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(smb2), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(tay), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(tax), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(ldy), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(ldx), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(bbs2), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bcs), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(zero_page_indirect), + cycles: 5, // Unsure, see https://cx16.dk/65c02/reference.html#LDA + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(ldy), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(ldx), + address_fn: Some(zero_page_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(smb3), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(clv), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(tsx), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(ldy), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(lda), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(ldx), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(bbs3), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(cpy), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(cpy), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(dec), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(smb4), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(iny), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(dex), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(wai), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: Some(cpy), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(dec), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbs4), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(bne), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(zero_page_indirect), + cycles: 5, // Unsure, look here: https://cx16.dk/65c02/reference.html#CMP + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(dec), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(smb5), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(cld), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(phx), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: Some(stp), + address_fn: None, + cycles: 3, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(cmp), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(dec), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbs5), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(cpx), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(zero_page_indexed_indirect), + cycles: 6, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(cpx), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(zero_page), + cycles: 3, + }, + Instruction { + instr_fn: Some(inc), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(smb6), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(inx), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(immediate), + cycles: 2, + }, + Instruction { + instr_fn: Some(nop), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(cpx), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(absolute_a), + cycles: 4, + }, + Instruction { + instr_fn: Some(inc), + address_fn: Some(absolute_a), + cycles: 6, + }, + Instruction { + instr_fn: Some(bbs6), + address_fn: Some(relative_test), + cycles: 4, + }, + Instruction { + instr_fn: Some(beq), + address_fn: Some(relative), + cycles: 2, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(zero_page_indirect_indexed_with_y), + cycles: 5, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(zero_page_indirect), + cycles: 5, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(zero_page_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(inc), + address_fn: Some(zero_page_indexed_with_x), + cycles: 6, + }, + Instruction { + instr_fn: Some(smb7), + address_fn: Some(zero_page), + cycles: 5, + }, + Instruction { + instr_fn: Some(sed), + address_fn: None, + cycles: 2, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(absolute_indexed_with_y), + cycles: 4, + }, + Instruction { + instr_fn: Some(plx), + address_fn: None, + cycles: 4, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: None, + address_fn: None, + cycles: 0, + }, + Instruction { + instr_fn: Some(sbc), + address_fn: Some(absolute_indexed_with_x), + cycles: 4, + }, + Instruction { + instr_fn: Some(inc), + address_fn: Some(absolute_indexed_with_x), + cycles: 7, + }, + Instruction { + instr_fn: Some(bbs7), + address_fn: Some(relative_test), + cycles: 4, + }, +]; diff --git a/src/keyboard.rs b/src/keyboard.rs index 0998463..a165be6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,5 +1,10 @@ -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use minifb::{InputCallback, Key}; +use std::io::{self, stdin, Read}; + +// use minifb::{InputCallback, Key}; +use termion::{ + event::Key, + input::{Keys, TermRead}, +}; use crate::memory::{MemHandle, MemoryWriter}; @@ -7,74 +12,405 @@ pub struct Keyboard { memory: MemHandle, } +struct PlainKeys { + iter: Keys, +} + +impl Iterator for PlainKeys { + type Item = Result; + fn next(&mut self) -> Option { + loop { + match self.iter.next() { + Some(Ok(key)) => match key { + Key::Ctrl(char) => return Some(Ok(char)), + Key::Alt(char) => return Some(Ok(char)), + Key::Char('A') => return Some(Ok('a')), + Key::Char('B') => return Some(Ok('b')), + Key::Char('C') => return Some(Ok('c')), + Key::Char('D') => return Some(Ok('d')), + Key::Char('E') => return Some(Ok('e')), + Key::Char('F') => return Some(Ok('f')), + Key::Char('G') => return Some(Ok('g')), + Key::Char('H') => return Some(Ok('h')), + Key::Char('I') => return Some(Ok('i')), + Key::Char('J') => return Some(Ok('j')), + Key::Char('K') => return Some(Ok('k')), + Key::Char('L') => return Some(Ok('l')), + Key::Char('M') => return Some(Ok('m')), + Key::Char('N') => return Some(Ok('n')), + Key::Char('O') => return Some(Ok('o')), + Key::Char('P') => return Some(Ok('p')), + Key::Char('Q') => return Some(Ok('q')), + Key::Char('R') => return Some(Ok('r')), + Key::Char('S') => return Some(Ok('s')), + Key::Char('T') => return Some(Ok('t')), + Key::Char('U') => return Some(Ok('u')), + Key::Char('V') => return Some(Ok('v')), + Key::Char('W') => return Some(Ok('w')), + Key::Char('X') => return Some(Ok('x')), + Key::Char('Y') => return Some(Ok('y')), + Key::Char('Z') => return Some(Ok('z')), + Key::Char('!') => return Some(Ok('1')), + Key::Char('@') => return Some(Ok('2')), + Key::Char('#') => return Some(Ok('3')), + Key::Char('$') => return Some(Ok('4')), + Key::Char('%') => return Some(Ok('5')), + Key::Char('^') => return Some(Ok('6')), + Key::Char('&') => return Some(Ok('7')), + Key::Char('*') => return Some(Ok('8')), + Key::Char('(') => return Some(Ok('9')), + Key::Char(')') => return Some(Ok('0')), + Key::Char('~') => return Some(Ok('`')), + Key::Char('_') => return Some(Ok('-')), + Key::Char('+') => return Some(Ok('=')), + Key::Char('|') => return Some(Ok('\\')), + Key::Char('}') => return Some(Ok(']')), + Key::Char('{') => return Some(Ok('[')), + Key::Char('"') => return Some(Ok('\'')), + Key::Char(':') => return Some(Ok(';')), + Key::Char('?') => return Some(Ok('/')), + Key::Char('>') => return Some(Ok('.')), + Key::Char('<') => return Some(Ok(',')), + Key::Char(char) => return Some(Ok(char)), + _ => continue, + }, + Some(Err(e)) => return Some(Err(e)), + None => return None, + } + } + } +} + +#[derive(Clone, Copy, Debug, Ord, Eq, PartialEq, PartialOrd)] +enum ControlKey { + Alt, + Shift, + Ctrl, + Left, + Right, + Up, + Down, + Esc, + Enter, + Home, + End, + Tab, + Backspace, + Delete, + Insert, + PageDown, + PageUp, +} + +struct ControlKeys { + iter: Keys, + extra: Option, +} + +impl Iterator for ControlKeys { + type Item = Result; + fn next(&mut self) -> Option { + loop { + if let Some(key) = self.extra { + return Some(Ok(key)); + } + match self.iter.next() { + Some(Ok(key)) => match key { + Key::Ctrl(char) => match char { + 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' + | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' + | 'Y' | 'Z' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')' + | '~' | '_' | '+' | '|' | '}' | '{' | '"' | ':' | '?' | '>' | '<' => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Ctrl)); + } + _ => return Some(Ok(ControlKey::Ctrl)), + }, + Key::CtrlUp => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::Up)); + } + Key::CtrlDown => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::Down)); + } + Key::CtrlRight => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::Right)); + } + Key::CtrlLeft => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::Left)); + } + Key::CtrlEnd => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::End)); + } + Key::CtrlHome => { + self.extra = Some(ControlKey::Ctrl); + return Some(Ok(ControlKey::Home)); + } + Key::Alt(char) => match char { + 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' + | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' + | 'Y' | 'Z' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')' + | '~' | '_' | '+' | '|' | '}' | '{' | '"' | ':' | '?' | '>' | '<' => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Alt)); + } + _ => return Some(Ok(ControlKey::Alt)), + }, + Key::AltUp => { + self.extra = Some(ControlKey::Alt); + return Some(Ok(ControlKey::Up)); + } + Key::AltDown => { + self.extra = Some(ControlKey::Alt); + return Some(Ok(ControlKey::Down)); + } + Key::AltRight => { + self.extra = Some(ControlKey::Alt); + return Some(Ok(ControlKey::Right)); + } + Key::AltLeft => { + self.extra = Some(ControlKey::Alt); + return Some(Ok(ControlKey::Left)); + } + Key::Char('A') + | Key::Char('B') + | Key::Char('C') + | Key::Char('D') + | Key::Char('E') + | Key::Char('F') + | Key::Char('G') + | Key::Char('H') + | Key::Char('I') + | Key::Char('J') + | Key::Char('K') + | Key::Char('L') + | Key::Char('M') + | Key::Char('N') + | Key::Char('O') + | Key::Char('P') + | Key::Char('Q') + | Key::Char('R') + | Key::Char('S') + | Key::Char('T') + | Key::Char('U') + | Key::Char('V') + | Key::Char('W') + | Key::Char('X') + | Key::Char('Y') + | Key::Char('Z') + | Key::Char('!') + | Key::Char('@') + | Key::Char('#') + | Key::Char('$') + | Key::Char('%') + | Key::Char('^') + | Key::Char('&') + | Key::Char('*') + | Key::Char('(') + | Key::Char(')') + | Key::Char('~') + | Key::Char('_') + | Key::Char('+') + | Key::Char('|') + | Key::Char('}') + | Key::Char('{') + | Key::Char('"') + | Key::Char(':') + | Key::Char('?') + | Key::Char('>') + | Key::Char('<') => return Some(Ok(ControlKey::Shift)), + Key::ShiftUp => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Up)); + } + Key::ShiftDown => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Down)); + } + Key::ShiftRight => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Right)); + } + Key::ShiftLeft => { + self.extra = Some(ControlKey::Shift); + return Some(Ok(ControlKey::Left)); + } + Key::Backspace => return Some(Ok(ControlKey::Backspace)), + Key::Up => return Some(Ok(ControlKey::Up)), + Key::Down => return Some(Ok(ControlKey::Down)), + Key::Right => return Some(Ok(ControlKey::Right)), + Key::Left => return Some(Ok(ControlKey::Left)), + Key::Home => return Some(Ok(ControlKey::Home)), + Key::End => return Some(Ok(ControlKey::End)), + Key::PageUp => return Some(Ok(ControlKey::PageUp)), + Key::PageDown => return Some(Ok(ControlKey::PageDown)), + Key::Insert => return Some(Ok(ControlKey::Insert)), + Key::Delete => return Some(Ok(ControlKey::Delete)), + _ => continue, + }, + Some(Err(e)) => return Some(Err(e)), + None => return None, + } + } + } +} + +trait GetKeys { + fn get_plain(self) -> PlainKeys + where + Self: Sized; + fn get_modifiers(self) -> ControlKeys + where + Self: Sized; +} + +impl GetKeys for R { + fn get_plain(self) -> PlainKeys + where + Self: Sized, + { + PlainKeys { iter: self.keys() } + } + fn get_modifiers(self) -> ControlKeys + where + Self: Sized, + { + ControlKeys { + iter: self.keys(), + extra: None, + } + } +} + impl Keyboard { pub fn new(memory: MemHandle) -> Self { Self { memory } } - pub fn read_keys(&self, key: KeyEvent) { + + pub fn clear_keys(&self) { + self.memory.write(0x4400, 0x00); + self.memory.write(0x4401, 0x00); + self.memory.write(0x4402, 0x00); + self.memory.write(0x4403, 0x00); + self.memory.write(0x4404, 0x00); + self.memory.write(0x4405, 0x00); + } + + // pub fn read_keys(&self, key: KeyEvent) { + pub fn read_keys(&self) { let mut row0 = 0; let mut row1 = 0; let mut row2 = 0; let mut row3 = 0; let mut row4 = 0; let mut row5 = 0; - match key.modifiers { - KeyModifiers::SHIFT => row2 ^= 0b1000_0000, - KeyModifiers::CONTROL => row3 ^= 0b1000_0000, - KeyModifiers::ALT => row4 ^= 0b1000_0000, - KeyModifiers::META => row5 ^= 0b1000_0000, - KeyModifiers::SUPER => row5 ^= 0b0010_0000, - _ => {} - }; - match key.code { - KeyCode::Esc => row0 ^= 0b1000_0000, - KeyCode::Char('w') => row0 ^= 0b0100_0000, - KeyCode::Char('e') => row0 ^= 0b0010_0000, - KeyCode::Char('r') => row0 ^= 0b0001_0000, - KeyCode::Char('t') => row0 ^= 0b0000_1000, - KeyCode::Char('u') => row0 ^= 0b0000_0100, - KeyCode::Char('o') => row0 ^= 0b0000_0010, - KeyCode::Backspace => row0 ^= 0b0000_0001, - KeyCode::Tab => row1 ^= 0b1000_0000, - KeyCode::Char('q') => row1 ^= 0b0100_0000, - KeyCode::Char('s') => row1 ^= 0b0010_0000, - KeyCode::Char('g') => row1 ^= 0b0001_0000, - KeyCode::Char('y') => row1 ^= 0b0000_1000, - KeyCode::Char('i') => row1 ^= 0b0000_0100, - KeyCode::Char('p') => row1 ^= 0b0000_0010, - KeyCode::Enter => row1 ^= 0b0000_0001, - KeyCode::Char('d') => row2 ^= 0b0100_0000, - KeyCode::Char('v') => row2 ^= 0b0010_0000, - KeyCode::Char('h') => row2 ^= 0b0001_0000, - KeyCode::Char('k') => row2 ^= 0b0000_1000, - KeyCode::Char('\'') => row2 ^= 0b0000_0100, - KeyCode::Char('/') => row2 ^= 0b0000_0010, - KeyCode::Char('a') => row2 ^= 0b0000_0001, - KeyCode::Char('z') => row3 ^= 0b0100_0000, - KeyCode::Char('f') => row3 ^= 0b0010_0000, - KeyCode::Char('b') => row3 ^= 0b0001_0000, - KeyCode::Char('j') => row3 ^= 0b0000_1000, - KeyCode::Char('l') => row3 ^= 0b0000_0100, - KeyCode::Char('2') => row3 ^= 0b0000_0010, - KeyCode::Char('4') => row3 ^= 0b0000_0001, - KeyCode::Char('x') => row4 ^= 0b0100_0000, - KeyCode::Char('c') => row4 ^= 0b0010_0000, - KeyCode::Char('n') => row4 ^= 0b0001_0000, - KeyCode::Char('m') => row4 ^= 0b0000_1000, - KeyCode::Char(',') => row4 ^= 0b0000_0100, - KeyCode::Char('1') => row4 ^= 0b0000_0010, - KeyCode::Char('3') => row4 ^= 0b0000_0001, - KeyCode::Char(' ') => row5 ^= 0b0100_0000, - _ => { - row0 = 0; - row1 = 0; - row2 = 0; - row3 = 0; - row4 = 0; - row5 = 0; + let modifiers = termion::async_stdin().get_modifiers(); + for m in modifiers { + match m.unwrap() { + ControlKey::Shift => row2 ^= 0b1000_0000, + ControlKey::Ctrl => row3 ^= 0b1000_0000, + ControlKey::Alt => row4 ^= 0b1000_0000, + ControlKey::Esc => row0 ^= 0b1000_0000, + ControlKey::Backspace => row0 ^= 0b0000_0001, + ControlKey::Tab => row1 ^= 0b1000_0000, + ControlKey::Enter => row1 ^= 0b0000_0001, + _ => {} } - }; + } + + let keys = termion::async_stdin().get_plain(); + for k in keys { + match k.unwrap() { + 'w' => row0 ^= 0b0100_0000, + 'e' => row0 ^= 0b0010_0000, + 'r' => row0 ^= 0b0001_0000, + 't' => row0 ^= 0b0000_1000, + 'u' => row0 ^= 0b0000_0100, + 'o' => row0 ^= 0b0000_0010, + 'q' => row1 ^= 0b0100_0000, + 's' => row1 ^= 0b0010_0000, + 'g' => row1 ^= 0b0001_0000, + 'y' => row1 ^= 0b0000_1000, + 'i' => row1 ^= 0b0000_0100, + 'p' => row1 ^= 0b0000_0010, + 'd' => row2 ^= 0b0100_0000, + 'v' => row2 ^= 0b0010_0000, + 'h' => row2 ^= 0b0001_0000, + 'k' => row2 ^= 0b0000_1000, + '\\' => row2 ^= 0b0000_0100, + '/' => row2 ^= 0b0000_0010, + 'a' => row2 ^= 0b0000_0001, + 'z' => row3 ^= 0b0100_0000, + 'f' => row3 ^= 0b0010_0000, + 'b' => row3 ^= 0b0001_0000, + 'j' => row3 ^= 0b0000_1000, + 'l' => row3 ^= 0b0000_0100, + '2' => row3 ^= 0b0000_0010, + '4' => row3 ^= 0b0000_0001, + 'x' => row4 ^= 0b0100_0000, + 'c' => row4 ^= 0b0010_0000, + 'n' => row4 ^= 0b0001_0000, + 'm' => row4 ^= 0b0000_1000, + ',' => row4 ^= 0b0000_0100, + '1' => row4 ^= 0b0000_0010, + '3' => row4 ^= 0b0000_0001, + ' ' => row5 ^= 0b0100_0000, + _ => {} + } + } + // match key.code { + // KeyCode::Esc => row0 ^= 0b1000_0000, + // KeyCode::Char('w') => row0 ^= 0b0100_0000, + // KeyCode::Char('e') => row0 ^= 0b0010_0000, + // KeyCode::Char('r') => row0 ^= 0b0001_0000, + // KeyCode::Char('t') => row0 ^= 0b0000_1000, + // KeyCode::Char('u') => row0 ^= 0b0000_0100, + // KeyCode::Char('o') => row0 ^= 0b0000_0010, + // KeyCode::Backspace => row0 ^= 0b0000_0001, + // KeyCode::Tab => row1 ^= 0b1000_0000, + // KeyCode::Char('q') => row1 ^= 0b0100_0000, + // KeyCode::Char('s') => row1 ^= 0b0010_0000, + // KeyCode::Char('g') => row1 ^= 0b0001_0000, + // KeyCode::Char('y') => row1 ^= 0b0000_1000, + // KeyCode::Char('i') => row1 ^= 0b0000_0100, + // KeyCode::Char('p') => row1 ^= 0b0000_0010, + // KeyCode::Enter => row1 ^= 0b0000_0001, + // KeyCode::Char('d') => row2 ^= 0b0100_0000, + // KeyCode::Char('v') => row2 ^= 0b0010_0000, + // KeyCode::Char('h') => row2 ^= 0b0001_0000, + // KeyCode::Char('k') => row2 ^= 0b0000_1000, + // KeyCode::Char('\'') => row2 ^= 0b0000_0100, + // KeyCode::Char('/') => row2 ^= 0b0000_0010, + // KeyCode::Char('a') => row2 ^= 0b0000_0001, + // KeyCode::Char('z') => row3 ^= 0b0100_0000, + // KeyCode::Char('f') => row3 ^= 0b0010_0000, + // KeyCode::Char('b') => row3 ^= 0b0001_0000, + // KeyCode::Char('j') => row3 ^= 0b0000_1000, + // KeyCode::Char('l') => row3 ^= 0b0000_0100, + // KeyCode::Char('2') => row3 ^= 0b0000_0010, + // KeyCode::Char('4') => row3 ^= 0b0000_0001, + // KeyCode::Char('x') => row4 ^= 0b0100_0000, + // KeyCode::Char('c') => row4 ^= 0b0010_0000, + // KeyCode::Char('n') => row4 ^= 0b0001_0000, + // KeyCode::Char('m') => row4 ^= 0b0000_1000, + // KeyCode::Char(',') => row4 ^= 0b0000_0100, + // KeyCode::Char('1') => row4 ^= 0b0000_0010, + // KeyCode::Char('3') => row4 ^= 0b0000_0001, + // KeyCode::Char(' ') => row5 ^= 0b0100_0000, + // _ => { + // row0 = 0; + // row1 = 0; + // row2 = 0; + // row3 = 0; + // row4 = 0; + // row5 = 0; + // } + // }; self.memory.write(0x4400, row0); self.memory.write(0x4401, row1); @@ -87,72 +423,75 @@ impl Keyboard { impl MemoryWriter for Keyboard { fn write(&self, address: u16, data: u8) { + if data != 0x00 { + println!("wrote {:02x} to address {:04x}", data, address); + } 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) { - let mut row0 = 0; - let mut row1 = 0; - let mut row2 = 0; - let mut row3 = 0; - let mut row4 = 0; - let mut row5 = 0; +// impl InputCallback for Keyboard { +// fn add_char(&mut self, _uni_char: u32) {} +// fn set_key_state(&mut self, key: Key, _state: bool) { +// let mut row0 = 0; +// let mut row1 = 0; +// let mut row2 = 0; +// let mut row3 = 0; +// let mut row4 = 0; +// let mut row5 = 0; - match key { - Key::Escape => row0 ^= 0b1000_0000, - Key::W => row0 ^= 0b0100_0000, - Key::E => row0 ^= 0b0010_0000, - Key::R => row0 ^= 0b0001_0000, - Key::T => row0 ^= 0b0000_1000, - Key::U => row0 ^= 0b0000_0100, - Key::O => row0 ^= 0b0000_0010, - Key::Backspace => row0 ^= 0b0000_0001, - Key::Tab => row1 ^= 0b1000_0000, - Key::Q => row1 ^= 0b0100_0000, - Key::S => row1 ^= 0b0010_0000, - Key::G => row1 ^= 0b0001_0000, - Key::Y => row1 ^= 0b0000_1000, - Key::I => row1 ^= 0b0000_0100, - Key::P => row1 ^= 0b0000_0010, - Key::Enter => row1 ^= 0b0000_0001, - Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, - Key::D => row2 ^= 0b0100_0000, - Key::V => row2 ^= 0b0010_0000, - Key::H => row2 ^= 0b0001_0000, - Key::K => row2 ^= 0b0000_1000, - Key::Apostrophe => row2 ^= 0b0000_0100, - Key::Slash => row2 ^= 0b0000_0010, - Key::A => row2 ^= 0b0000_0001, - Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, - Key::Z => row3 ^= 0b0100_0000, - Key::F => row3 ^= 0b0010_0000, - Key::B => row3 ^= 0b0001_0000, - Key::J => row3 ^= 0b0000_1000, - Key::L => row3 ^= 0b0000_0100, - Key::Key2 => row3 ^= 0b0000_0010, - Key::Key4 => row3 ^= 0b0000_0001, - Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, - Key::X => row4 ^= 0b0100_0000, - Key::C => row4 ^= 0b0010_0000, - Key::N => row4 ^= 0b0001_0000, - Key::M => row4 ^= 0b0000_1000, - Key::Comma => row4 ^= 0b0000_0100, - Key::Key1 => row4 ^= 0b0000_0010, - Key::Key3 => row4 ^= 0b0000_0001, - Key::LeftSuper => row5 ^= 0b1000_0000, - Key::Space => row5 ^= 0b0100_0000, - Key::RightSuper => row5 ^= 0b0010_0000, - _ => {} - }; +// match key { +// Key::Escape => row0 ^= 0b1000_0000, +// Key::W => row0 ^= 0b0100_0000, +// Key::E => row0 ^= 0b0010_0000, +// Key::R => row0 ^= 0b0001_0000, +// Key::T => row0 ^= 0b0000_1000, +// Key::U => row0 ^= 0b0000_0100, +// Key::O => row0 ^= 0b0000_0010, +// Key::Backspace => row0 ^= 0b0000_0001, +// Key::Tab => row1 ^= 0b1000_0000, +// Key::Q => row1 ^= 0b0100_0000, +// Key::S => row1 ^= 0b0010_0000, +// Key::G => row1 ^= 0b0001_0000, +// Key::Y => row1 ^= 0b0000_1000, +// Key::I => row1 ^= 0b0000_0100, +// Key::P => row1 ^= 0b0000_0010, +// Key::Enter => row1 ^= 0b0000_0001, +// Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, +// Key::D => row2 ^= 0b0100_0000, +// Key::V => row2 ^= 0b0010_0000, +// Key::H => row2 ^= 0b0001_0000, +// Key::K => row2 ^= 0b0000_1000, +// Key::Apostrophe => row2 ^= 0b0000_0100, +// Key::Slash => row2 ^= 0b0000_0010, +// Key::A => row2 ^= 0b0000_0001, +// Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, +// Key::Z => row3 ^= 0b0100_0000, +// Key::F => row3 ^= 0b0010_0000, +// Key::B => row3 ^= 0b0001_0000, +// Key::J => row3 ^= 0b0000_1000, +// Key::L => row3 ^= 0b0000_0100, +// Key::Key2 => row3 ^= 0b0000_0010, +// Key::Key4 => row3 ^= 0b0000_0001, +// Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, +// Key::X => row4 ^= 0b0100_0000, +// Key::C => row4 ^= 0b0010_0000, +// Key::N => row4 ^= 0b0001_0000, +// Key::M => row4 ^= 0b0000_1000, +// Key::Comma => row4 ^= 0b0000_0100, +// Key::Key1 => row4 ^= 0b0000_0010, +// Key::Key3 => row4 ^= 0b0000_0001, +// Key::LeftSuper => row5 ^= 0b1000_0000, +// Key::Space => row5 ^= 0b0100_0000, +// Key::RightSuper => row5 ^= 0b0010_0000, +// _ => {} +// }; - 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); - } -} +// self.memory.write(0x4400, row0); +// self.memory.write(0x4401, row1); +// self.memory.write(0x4402, row2); +// self.memory.write(0x4403, row3); +// self.memory.write(0x4404, row4); +// self.memory.write(0x4405, row5); +// } +// } diff --git a/src/main.rs b/src/main.rs index fd128b3..fa678db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,64 +10,36 @@ mod video; use crate::cpu::Cpu; use crate::keyboard::Keyboard; use crate::memory::Mem; -use crate::video::{Screen, TerminalRenderer}; +use crate::video::Screen; use cli::get_input; -use crossterm::execute; -use crossterm::terminal::{size, Clear, ClearType, SetSize}; // use cpu::CpuController; use memory::MemHandle; -use minifb::{Scale, ScaleMode, Window, WindowOptions}; -use std::io::{stdout, Result}; +use std::io::{stdin, stdout, Result}; use std::thread::{self, sleep}; use std::time::Duration; +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::IntoRawMode; +// use termion::raw::IntoRawMode; -use crossterm::{ - cursor, - event::{self, KeyCode, KeyEventKind}, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; +use std::io::Write; -// For when we want to leave the terminal again :sigh: -// -// thread::spawn(move || { -// let mut screen = Crtc::new( -// screen_memory, -// config.char_rom.as_ref(), -// CpuController::new(screen_cpu_tx), -// window_tx, -// ); -// screen.run(); -// }); - -// let mut window = Window::new( -// "ʕ·ᴥ·ʔ-☆", -// 512, -// 380, -// WindowOptions { -// resize: true, -// borderless: true, -// title: true, -// transparency: false, -// scale: Scale::X2, -// scale_mode: ScaleMode::AspectRatioStretch, -// topmost: false, -// none: true, -// }, -// ) -// .unwrap(); +use termion::{self, clear, cursor, screen}; fn main() -> Result<()> { - let mut stdout = stdout(); - let (cols, rows) = size()?; + let mut stdout = stdout().into_raw_mode().unwrap(); + // let (cols, rows) = size()?; let config = get_input(); let mut memory = Mem::new(); let _ = memory.load_rom(&config.rom); - execute!(stdout, SetSize(64, 29), cursor::Hide, EnterAlternateScreen)?; - enable_raw_mode()?; + write!(stdout, "{}{}", cursor::Hide, screen::ToAlternateScreen)?; + + // execute!(stdout, SetSize(64, 29), cursor::Hide, EnterAlternateScreen)?; + // enable_raw_mode()?; let shared_memory = MemHandle::new(memory); let screen_memory = shared_memory.clone(); @@ -86,24 +58,21 @@ fn main() -> Result<()> { let mut screen = Screen::new(&config, cpu_controller, screen_memory); loop { - sleep(Duration::from_millis(16)); + keyboard.clear_keys(); // nasty hack until i can figure out a good way of clearing keyboard + // memory when no keys are pressed screen.draw(); // if event::poll(std::time::Duration::from_millis(16))? { - if let event::Event::Key(key) = event::read()? { - keyboard.read_keys(key); - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - break; + keyboard.read_keys(); + for key in stdin().keys() { + match key.unwrap() { + Key::Char('q') => { + write!(stdout, "{}", clear::All)?; + return Ok(()); + } + _ => {} } - // } } + // } + sleep(Duration::from_millis(16)); } - - execute!( - stdout, - LeaveAlternateScreen, - SetSize(cols, rows), - Clear(ClearType::Purge) - )?; - disable_raw_mode()?; - Ok(()) } diff --git a/src/roms/keyboard.asm b/src/roms/keyboard.asm index 29c8d84..fac5f17 100644 --- a/src/roms/keyboard.asm +++ b/src/roms/keyboard.asm @@ -30,7 +30,7 @@ cleardisplay: main: ; jsr read_keys ; lda key_buffer - lda $4402 + lda $4400 sta $6000 jmp main diff --git a/src/video.rs b/src/video.rs index b0e08c9..7846061 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,10 +1,9 @@ -use crossterm::{ - cursor::{MoveTo, RestorePosition, SavePosition}, - execute, queue, - style::{Color, PrintStyledContent, Stylize}, -}; use minifb::{Scale, ScaleMode, Window, WindowOptions}; use serde::{Deserialize, Serialize}; +use termion::{ + color::{self, Bg, Color, Fg}, + cursor::Goto, +}; use crate::{ cli::Config, @@ -16,6 +15,8 @@ use std::{ fs::File, io::{self, Read, Write}, path::Path, + process::exit, + time::Instant, }; const FG_COLOR: u32 = 0xFFCC00; @@ -150,40 +151,24 @@ const ASCII_LOOPUP: [&str; 256] = [ // impl Renderer for TerminalRenderer<'_> { impl Renderer for TerminalRenderer { fn render(&mut self) { - // let now = Instant::now(); let mut stdout = io::stdout(); - let _ = execute!(stdout, SavePosition); let mut i = 0; for char_row in 0..29 { for char_col in 0..64 { let ascii = self.read(0x6000 + i); i += 1; let char = ASCII_LOOPUP[ascii as usize]; - let _ = queue!( + let _ = write!( // FG_COLOR = 0xFFCC00 // BG_COLOR = 0x110500 stdout, - MoveTo(char_col, char_row), - PrintStyledContent( - char.with(Color::Rgb { - r: 0xFF, - g: 0xCC, - b: 0x00 - }) - .on(Color::Rgb { - r: 0x11, - g: 0x05, - b: 0x00 - }) - ) + "{}{}{}{char}", + Goto(char_col + 1, char_row + 1), + Fg(color::Rgb(0xFF, 0xCC, 0x00)), + Bg(color::Rgb(0x11, 0x05, 0x00)) ); } } - let _ = stdout.flush(); - let _ = execute!(stdout, RestorePosition); - // let elapsed = now.elapsed(); - // println!("{elapsed:?}"); - // exit(0); } }